| Crates.io | openapi-gen |
| lib.rs | openapi-gen |
| version | 0.3.1 |
| created_at | 2025-05-27 15:56:51.15187+00 |
| updated_at | 2025-07-27 16:16:45.166222+00 |
| description | Rust macro to generate client from OpenAPI spec. |
| homepage | |
| repository | https://github.com/paulvandermeijs/openapi-gen |
| max_upload_size | |
| id | 1691284 |
| size | 111,270 |
A Rust procedural macro crate that generates type-safe, async HTTP clients from OpenAPI 3.0 specifications.
reqwest with full async supportAdd this to your Cargo.toml:
[dependencies]
openapi-gen = "0.3"
reqwest = { version = "0.11", features = ["json"] }
tokio = { version = "1.0", features = ["full"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
thiserror = "1.0"
use openapi_gen::openapi_client;
// Generate client from OpenAPI spec with auto-generated name
openapi_client!("path/to/your/openapi.json");
// Or specify a custom client name
openapi_client!("path/to/your/openapi.json", "MyApiClient");
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create client instance
let client = MyApiClient::new("https://api.example.com");
// Use the generated methods
let users = client.list_users(Some(10), None, None).await?;
let user = client.get_user_by_id(123).await?;
Ok(())
}
The macro generates:
/// Represents a user in the system with comprehensive profile information.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct User {
/// Unique identifier for the user
pub id: i64,
/// User's chosen username
pub username: String,
/// User's email address
pub email: String,
/// User type (tests Rust keyword as field name)
pub r#type: String,
// ... more fields
}
impl MyApiClient {
/// List all users
///
/// Retrieve a paginated list of all users in the system. Supports filtering and sorting.
///
/// **HTTP Method:** `GET`
/// **Path:** `/users`
/// **Operation ID:** `listUsers`
pub async fn list_users(
&self,
limit: Option<i32>,
offset: Option<i64>,
r#type: Option<String>
) -> ApiResult<UserList> {
// Generated implementation
}
}
The generated client includes:
info section| Feature | Support | Notes |
|---|---|---|
| HTTP Methods | ✅ | GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS, TRACE |
| Path Parameters | ✅ | Automatic URL template substitution |
| Query Parameters | ✅ | Optional and required parameters |
| Request Bodies | ✅ | JSON request bodies |
| Response Types | ✅ | Typed response parsing |
| Schema References | ✅ | $ref resolution for reusable components |
| Enums | ✅ | String enumerations with proper Rust enum generation |
| Arrays | ✅ | Vec<T> generation for array types |
| Objects | ✅ | Struct generation with proper field types |
| Optional Fields | ✅ | Option<T> for non-required fields |
| Nested Objects | ✅ | Complex object hierarchies |
| Type Aliases | ✅ | Simple type aliases |
| Rust Keywords | ✅ | Automatic escaping with r# or _ suffix |
The generator automatically handles Rust keywords in:
type → r#type, self → self_const → r#constSpecial handling for self and Self (cannot be raw identifiers):
self → self_Self → Self_The generated client includes a comprehensive error type:
#[derive(Debug, thiserror::Error)]
pub enum ApiError {
#[error("HTTP error: {0}")]
Http(#[from] reqwest::Error),
#[error("Serialization error: {0}")]
Serialization(#[from] serde_json::Error),
#[error("API error {status}: {message}")]
Api { status: u16, message: String },
}
pub type ApiResult<T> = Result<T, ApiError>;
// Basic client
let client = MyApiClient::new("https://api.example.com");
// Client with custom HTTP client
let http_client = reqwest::Client::builder()
.timeout(std::time::Duration::from_secs(30))
.build()?;
let client = MyApiClient::with_client("https://api.example.com", http_client);
use openapi_gen::openapi_client;
use serde_json::json;
// Generate the client
openapi_client!("openapi.json", "TestApi");
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = TestApi::new("https://api.test.com/v2");
// List users with pagination
let users = client.list_users(Some(20), Some(0), None).await?;
println!("Found {} users", users.total);
// Create a new user
let new_user = json!({
"username": "john_doe",
"email": "john@example.com",
"firstName": "John",
"lastName": "Doe"
});
let created_user = client.create_user(new_user).await?;
// Get user details
let user = client.get_user_by_id(created_user.id).await?;
println!("User: {} <{}>", user.username, user.email);
// Update user
let update_data = json!({
"firstName": "Jonathan"
});
let updated_user = client.update_user(user.id, update_data).await?;
Ok(())
}
Runtime dependencies (required in your project):
reqwest - HTTP client with JSON supportserde - Serialization framework (with derive feature)serde_json - JSON serializationthiserror - Error handlingtokio - Async runtimeBuild-time dependencies (used by the macro):
proc-macro2, quote, syn - Procedural macro infrastructureopenapiv3 - OpenAPI 3.0 specification parsingserde_yaml - YAML parsing for specsheck - Case conversionstokio - For compile-time URL fetchingcargo build
cargo test
cargo doc --no-deps --open
The project includes a comprehensive test OpenAPI specification that validates all crate features.
Contributions are welcome! Please feel free to submit a Pull Request.
This project is licensed under the MIT License - see the LICENSE file for details.