Generation Options
Use generation options to choose the output layout, select operations, and control the generated API’s names and visibility. The Flag Summary lists the defaults and accepted values.
Commands run from a checkout of this repository. If you installed the tool, replace
cargo run -- with oas3-gen . Use the path to your own document in place of
spec.json or petstore.json . Unless stated otherwise, examples refer to
fixtures/petstore.json ; Rust excerpts may omit imports, attributes, and unrelated
definitions.
- Generation Modes
- Workspace Crate Output
- Operation Filtering
- Schema Filtering
- API Name Override
- Function Name Overrides
- Visibility
- Header Emission
- Builder Generation
- Documentation Formatting
- Known Limitations
- Flag Summary
Generation Modes
The positional mode argument selects the kind of output. Choose types when you need
data types, client or client-mod when you want to call an API, and server-mod when
you want to implement one:
cargo run -- generate <MODE> -i spec.json -o <OUTPUT>
types
The types mode writes type definitions to a single file. Let’s generate the petstore’s
types:
cargo run -- generate types -i petstore.json -o types.rs
Among others, types.rs now contains the PetStatus enum and the Pet struct:
/// Where the pet is in the adoption process
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, oas3_gen_support::Default)]
pub enum PetStatus {
#[serde(rename = "available")]
#[default]
Available,
#[serde(rename = "pending")]
Pending,
#[serde(rename = "adopted")]
Adopted,
}
/// A pet in the store's care.
#[derive(Debug, Clone, PartialEq, Deserialize, oas3_gen_support::Default)]
pub struct Pet {
/// Server-assigned identifier
pub id: uuid::Uuid,
pub name: String,
/// Kinds of pets the store cares for
pub species: Species,
/// Where the pet is in the adoption process
pub status: Option<PetStatus>,
/* ... */
}
The required properties id , name , and species become plain fields, and an
optional property such as status becomes an Option . The derives
follow how the API uses each type. Pet appears only in responses, so it derives
Deserialize but not
Serialize . NewPet , which the API only accepts, derives
Serialize and Validate instead. Server output reverses those
directions.
client
The client mode writes types and a reqwest client to one file. See
Choosing the Output Layout for an
example.
client-mod
The client-mod mode writes types.rs , client.rs , and mod.rs to a directory. See
Client Generation for how to generate and use it.
server-mod
The server-mod mode writes types.rs , server.rs , and mod.rs to a directory. See
Server Generation for how to implement the trait and use the
generated Axum router.
Workspace Crate Output
Use --workspace when you want the output to have its own Cargo.toml and src/
directory. You can build that output as a crate or include it in a Cargo workspace. The
flag is available with client-mod and server-mod ; using it with types or client
produces a generation error.
-w, --workspace
For example, let’s generate a client crate in petstore-api :
cargo run -- generate client-mod -i petstore.json -o petstore-api --workspace
The generated directory has this structure:
petstore-api/
├── Cargo.toml
└── src/
├── lib.rs
├── types.rs
└── client.rs
The src/lib.rs file is the library root, so you can build petstore-api as a crate.
The type and client or server files contain the same code as the corresponding module
output.
Crate Name
The package takes its name from the final component of the output directory, so
-o petstore-api produces name = "petstore-api" and a petstore_api library target.
Names are transliterated to ASCII and converted to kebab-case: -o "output/Pet Store"
yields pet-store , -o café-api yields cafe-api , and -o petstore_v2 yields
petstore-v2 . The library target of petstore-v2 is still petstore_v2 , because
Cargo maps dashes to underscores.
Generation fails when no valid name can be derived, for example when the directory name is empty after sanitization or starts with a digit.
Generated Manifest
The manifest declares the package and the dependencies its generated code uses. The command above writes this manifest for the petstore client, shown here without its banner; the dependency list for your API may differ:
[package]
name = "petstore-api"
version = "0.0.0"
edition = "2024"
rust-version = "1.89"
description = "Rust client generated from the Petstore OpenAPI document"
[dependencies]
anyhow = "1.0"
chrono = { version = "0.4.42", default-features = false, features = ["std", "clock", "serde"] }
http = "1.5"
indexmap = { version = "2.14", features = ["serde"] }
oas3-gen-support = "0.29.0"
regex = "1.13"
reqwest = { version = "0.13", default-features = false, features = ["form", "json", "multipart", "http2", "native-tls", "query", "stream"] }
reqwest_cookie_store = "0.10"
secrecy = "0.10"
serde = { version = "1.0", features = ["derive"] }
serde_html_form = { version = "0.4", default-features = false, features = ["ser", "std"] }
serde_json = { version = "1.0", features = ["preserve_order"] }
serde_with = { version = "3.24", features = ["base64", "chrono"] }
uuid = { version = "1.26", features = ["serde"] }
validator = { version = "0.21", features = ["derive", "indexmap"] }
The description comes from the document’s info.title , Petstore . Adding
--enable-builders adds a bon entry. Generating the petstore server with server-mod
instead leaves out reqwest , reqwest_cookie_store , and serde_html_form , and adds
the Axum crates:
axum = { version = "0.8.5", features = ["multipart"] }
axum-extra = { version = "0.12", features = ["cookie", "query"] }
The generator rewrites Cargo.toml on every run and adds a banner identifying it as
generated output. Put code that needs its own dependencies in a separate crate so that
regenerating the API doesn’t overwrite your changes.
The package version starts at 0.0.0 . Pass --module-version to set a version for the
generated crate:
cargo run -- generate client-mod -i petstore.json -o petstore-api --workspace --module-version 1.0.0
The --module-version flag requires --workspace , because that’s the mode that
creates the manifest.
Dependencies
Each generator release contains the dependency version requirements it writes to the
manifest. It doesn’t read them from your project’s environment. The requirement for
oas3-gen-support uses the generator’s version. Cargo resolves these requirements when
you build the generated crate.
The manifest declares crates referenced by the generated source. For example, a
specification without date or time types doesn’t need a chrono entry, and server
output doesn’t need the client’s reqwest dependency. The following table describes the
dependencies you may see:
| Crate | Declared when the generated code |
|---|---|
anyhow | returns anyhow::Result from client or server methods |
axum | defines a server-mod router and extractors, with the multipart feature |
axum-extra | reads query parameters or cookie API keys on the server, with the cookie and query features |
bon | derives builders (--enable-builders) |
chrono | maps date, date-time, or time formats |
futures | returns or sends a server’s text/event-stream as a BoxStream |
http | emits header constants or HeaderMap conversions |
indexmap | emits IndexMap/IndexSet collection types |
oas3-gen-support | uses runtime derives, diagnostics, multipleOf checks, or reads event streams on the client |
regex | emits pattern validation constants |
reqwest | performs client-mod HTTP calls; the form feature sends application/x-www-form-urlencoded bodies |
reqwest_cookie_store | sends cookie API keys or encodes cookie parameters from the client |
secrecy | stores API keys and bearer tokens |
serde | derives Serialize/Deserialize |
serde_html_form | serializes query parameters on the client, with only its ser and std features |
serde_json | handles freeform serde_json::Value payloads |
serde_with | applies serde_as conversions |
uuid | maps the uuid format |
validator | derives Validate |
serde_json gains the preserve_order feature under the default collection policy so
that freeform JSON objects keep their key order. With
--no-ordered-collections the feature is
omitted. Likewise, validator gains its indexmap feature when the generated code also
uses indexmap . The feature checks the length of an IndexSet or IndexMap field,
such as the maxItems limit on the tags of NewPet .
Using the Crate
The manifest declares a plain [package] with no [workspace] table, so the crate
builds standalone and can also be added to an existing workspace:
[workspace]
members = ["crates/*", "petstore-api"]
To use the generated crate from another crate, add a path dependency to that crate’s
Cargo.toml :
[dependencies]
petstore-api = { path = "../petstore-api" }
Keep the default --visibility public for a crate other code depends on.
crate and file visibility restrict the re-exports in lib.rs , so other crates
can’t access those re-exported items.
Operation Filtering
You may only need a few operations from a large API. Use --only to select those
operations, or --exclude to leave specific operations out. You can use one of these
flags at a time.
--only <id1,id2,...>
--exclude <id1,id2,...>
--only
Pass the normalized snake_case operation IDs, separated by commas. Filtering uses
these IDs before any --fn-name overrides. For example, the petstore’s listPets and
createPet operations use list_pets and create_pet in the filter:
cargo run -- generate client-mod -i petstore.json -o output/ \
--only list_pets,create_pet
The generated client keeps list_pets() and create_pet() and omits the other
operations. With server-mod , the same selection keeps those two trait methods and
their routes. Types needed by the selected methods are still included, as we’ll see in
Schema Dependency Resolution .
--exclude
Use --exclude when you want most of an API but don’t need a particular operation. For
example, to leave out the image upload operation, run:
cargo run -- generate client-mod -i petstore.json -o output/ \
--exclude upload_pet_image
The client keeps the petstore’s other operations, including list_pets() and
create_pet() , and omits upload_pet_image() . In server-mod mode, the flag omits
that operation’s trait method and route.
A callback follows the operation that declares it. The petstore’s adopt_pet operation
declares the statusChanged callback, which server-mod generates as
adopt_pet_status_changed . In server-mod , --only adopt_pet keeps that callback’s
route and trait method, and --exclude adopt_pet removes them.
You can also name a callback by its own normalized snake_case ID.
--only adopt_pet_status_changed keeps only the callback, and
--exclude adopt_pet_status_changed removes only the callback while adopt_pet stays.
Schema Dependency Resolution
Filtering an operation also filters the types needed to use it. The generator follows dependencies through the schemas so that a selected operation has the types it needs:
- It collects schemas used by the selected operations’ parameters, request bodies, and responses.
- It follows references from those schemas to other schemas.
- It generates the resulting set of types.
For example, the petstore’s listPets returns Pets , an array of Pet values, and an
Error for its default response. Selecting only list_pets also generates the
schemas Pet refers to: Species , PetStatus , the Health union with its
Allergies and Diet branches, PetVaccinations , and VaccineRecord . The operation
doesn’t refer to any of them directly.
Schema Filtering
By default, the generator includes schemas used by the selected operations and the
schemas they depend on. Pass --all-schemas when you also need types that no operation
references.
--all-schemas
Default (Reachability Filtering)
This example uses a different document. Suppose a specification defines Pet ,
Category , Store , and Inventory , but its operations only use Pet and
Category . Generate types with the default options:
cargo run -- generate types -i spec.json -o types.rs
The output contains Pet and Category . The generator skips Store and Inventory
because no selected operation needs them. With --verbose , it reports how many of
these orphaned schemas it skipped.
With --all-schemas
To include those unused schemas too, add the flag:
cargo run -- generate types -i spec.json -o types.rs --all-schemas
The output now contains all four types: Pet , Category , Store , and Inventory .
Combining with Operation Filtering
You can’t combine --all-schemas with --only or --exclude in one command. If you
need both a complete set of types and a filtered client, generate two outputs:
cargo run -- generate types -i spec.json -o types.rs --all-schemas
cargo run -- generate client -i spec.json -o client.rs --only list_pets
The first file contains all schemas. The second contains the filtered client and its own required types; these commands don’t make the client import types from the first file.
API Name Override
Use --api-name to name the client struct in client or client-mod mode, or the
server trait in server-mod mode. The generator converts the supplied name to
PascalCase .
--api-name <NAME>
By default, the client name comes from the specification’s info.title . For example,
the petstore’s title, "Petstore" , becomes PetstoreClient ; an empty title produces
ApiClient . The default server trait name is ApiServer . To choose PetStoreClient
for the client, run:
cargo run -- generate client-mod -i petstore.json -o output/ --api-name PetStoreClient
The client struct and its implementation now use PetStoreClient . In server mode,
choose a trait name with the same flag:
cargo run -- generate server-mod -i petstore.json -o output/ --api-name PetStoreApi
The server trait is named PetStoreApi , and the generated router uses that name in its
trait bound. Changing the API name doesn’t rename the operations; use
--fn-name for those.
Function Name Overrides
Use --fn-name to choose the generated client or server method name for an operation.
The key is its operationId from the specification; the generator also accepts the
normalized operation ID shown by oas3-gen list operations .
--fn-name <ID=NAME>
The generator converts your method name to snake_case and uses it to derive the
request type’s name. For example, renaming listPets to fetch_all_pets also produces
FetchAllPetsRequest . Repeat the flag to rename more than one operation:
cargo run -- generate client-mod -i petstore.json -o output/ \
--fn-name listPets=fetch_all_pets \
--fn-name showPetById=get_pet
The override changes both names together:
| Operation ID | Method Name | Request Type |
|---|---|---|
listPets | fetch_all_pets | FetchAllPetsRequest |
showPetById | get_pet | GetPetRequest |
Use the new names when calling the client or implementing the server trait.
Visibility
Use --visibility to choose where generated items can be accessed. The default,
public , makes them available to other crates. Choose crate when the generated API
is an internal part of your application.
-C, --visibility <LEVEL>
| Value | Modifier | Use Case |
|---|---|---|
public (default) | pub | Library distribution |
crate | pub(crate) | Internal crate types |
file | pub(super) | Items used only inside the generated module |
Let’s compare the three values on two petstore types: the Pet struct and the
ChatModelChoice union, which has helper methods .
Example: --visibility public
With public visibility, the types, fields, and methods use pub :
pub struct Pet {
pub id: uuid::Uuid,
pub name: String,
/* ... */
}
pub enum ChatModelChoice {
ChatModel(ChatModel),
String(String),
}
impl ChatModelChoice {
pub fn chat10() -> Self {
Self::ChatModel(ChatModel::Chat10)
}
/* ... */
}
Example: --visibility crate
With crate visibility, those items use pub(crate) :
pub(crate) struct Pet {
pub(crate) id: uuid::Uuid,
pub(crate) name: String,
/* ... */
}
pub(crate) enum ChatModelChoice {
ChatModel(ChatModel),
String(String),
}
impl ChatModelChoice {
pub(crate) fn chat10() -> Self {
Self::ChatModel(ChatModel::Chat10)
}
/* ... */
}
Example: --visibility file
With file visibility, those items use pub(super) :
pub(super) struct Pet {
pub(super) id: uuid::Uuid,
pub(super) name: String,
/* ... */
}
pub(super) enum ChatModelChoice {
ChatModel(ChatModel),
String(String),
}
impl ChatModelChoice {
pub(super) fn chat10() -> Self {
Self::ChatModel(ChatModel::Chat10)
}
/* ... */
}
In module output, pub(super) lets the generated files use each other’s items:
client.rs or server.rs can reach the types in types.rs . The module’s mod.rs or
lib.rs doesn’t re-export anything, so the items stay inside the generated module.
Header Emission
Header name constants use
http::HeaderName, so you can refer to headers without repeating their string names. By default, the generator creates constants for request parameters and response headers
used by the selected operations. Use --all-headers to include header parameters from
components/parameters even when no operation references them.
Headers that the http crate already names are the exception. The
http::header module declares constants for standard
headers such as Content-Type , Location , and Authorization . The generator
doesn’t declare its own constant for any of them; generated code refers to http ’s
constant, such as http::header::LOCATION , instead.
OpenAPI says to ignore header parameters named Accept , Content-Type , or
Authorization , so the generator leaves them out of request types. The specification
describes those headers elsewhere: media types cover Accept and Content-Type , and
security schemes cover Authorization . For example, the petstore’s listAuditEvents
declares a required Authorization header parameter, yet its request struct has no
header field. To send credentials in Authorization , declare a
security scheme , as that operation also does.
--all-headers
Default (Operation-Referenced Headers Only)
The petstore’s showPetById operation uses an x-api-version header. The document also
declares an x-debug-trace header parameter in components/parameters that no
operation uses. Start with the default options:
cargo run -- generate types -i petstore.json -o types.rs
Among the other header constants, the output contains one for the header the operation uses:
pub const X_API_VERSION: http::HeaderName = http::HeaderName::from_static(
"x-api-version",
);
x-debug-trace isn’t emitted because no operation uses it.
With --all-headers
Now include the unused component header:
cargo run -- generate types -i petstore.json -o types.rs --all-headers
The output gains a constant for it:
pub const X_DEBUG_TRACE: http::HeaderName = http::HeaderName::from_static(
"x-debug-trace",
);
Builder Generation
Use --enable-builders when you want to construct generated values through setter
methods. The generator adds bon::Builder derives to schema
structs and #[builder] constructors to request structs.
--enable-builders
Builders are disabled by default. The Builder Pattern chapter walks through using them and explains how request builders run validation.
Default (Builders Disabled)
Without the flag, generated structs have no bon attributes. This excerpt from the
petstore client shows a schema type and a request type:
/// A pet in the store's care.
#[derive(Debug, Clone, PartialEq, Deserialize, oas3_gen_support::Default)]
pub struct Pet {
/// Server-assigned identifier
pub id: uuid::Uuid,
pub name: String,
/* ... */
}
/// Get a pet
#[derive(Debug, Clone, validator::Validate, oas3_gen_support::Default)]
pub struct ShowPetByIdRequest {
pub path: ShowPetByIdRequestPath,
#[validate(nested)]
pub header: ShowPetByIdRequestHeader,
}
With --enable-builders
With the flag, the schema gets a derive and the request gets a constructor annotated for
bon :
/// A pet in the store's care.
#[derive(Debug, Clone, PartialEq, Deserialize, oas3_gen_support::Default, bon::Builder)]
pub struct Pet {
/// Server-assigned identifier
pub id: uuid::Uuid,
pub name: String,
/* ... */
}
/// Get a pet
#[derive(Debug, Clone, validator::Validate, oas3_gen_support::Default)]
pub struct ShowPetByIdRequest {
pub path: ShowPetByIdRequestPath,
#[validate(nested)]
pub header: ShowPetByIdRequestHeader,
}
#[bon::bon]
impl ShowPetByIdRequest {
/// Create a new request with the given parameters.
#[builder]
pub fn new(
pet_id: uuid::Uuid,
if_none_match: Option<String>,
x_api_version: String,
) -> anyhow::Result<Self> {
/* ... */
}
}
The constructor accepts each parameter separately, so the builder has a setter for
pet_id , if_none_match , and x_api_version . It assembles the nested path and
header structs and validates the request before returning it.
Documentation Formatting
Descriptions and summaries in the OpenAPI document become documentation comments in the
generated code. Use --doc-format to format longer comments with the external
mdformat tool. The generator sends text
longer than 100 bytes to mdformat with a line-wrap setting of 100 characters.
--doc-format
Install mdformat and make sure it’s available on your PATH before using the flag:
pip install mdformat
Default (Formatting Disabled)
Without the flag, the generator preserves documentation text while converting escaped
newlines to line breaks. A long description can therefore remain on one line, as the
petstore’s CollarTelemetry description does:
/// A batch of smart-collar readings, written with JSON Schema 2020-12 keywords. Keywords without a Rust equivalent are accepted and ignored.
pub struct CollarTelemetry {
/* ... */
}
With --doc-format
cargo run -- generate types -i petstore.json -o types.rs --doc-format
The same description is wrapped into multiple lines:
/// A batch of smart-collar readings, written with JSON Schema 2020-12 keywords. Keywords without a Rust
/// equivalent are accepted and ignored.
pub struct CollarTelemetry {
/* ... */
}
Known Limitations
The following constraints and serialization options aren’t fully represented in the generated code. The petstore examples show where each limitation applies:
- With
--no-ordered-collections, an array withuniqueItems: true, such as thetagsofPet, is aVecand accepts repeated items. See Ordering and Collections. - An optional field that also allows
nullis anOption<T>, so JSONnulland a missing property both decode asNone, andNoneis left out when serializing. A client therefore can’t sendnullto clear a field with a JSON merge patch, such as thenicknamein thePetPatchbody ofupdatePet. Likewise, a field without a type, such asanyValueinCollarTelemetry, decodesnullasNone. - The generator ignores
allowReservedon query parameters. The client percent-encodes reserved characters, such as the/in aqvalue forsearchPets. - A query parameter’s value must be a scalar or an array of scalars. A JSON
object or an array of arrays in an untyped parameter, such as
filteronsearchPets, makes the client method return an error before it sends the request. - An
anyOfwhose branches each require a different property isn’t enforced. ThePetCarrierschema requires eitherwheelsorstraps, but both become optional fields, so a carrier with neither decodes successfully. - A multipart part name can’t contain
",\, a carriage return, a line feed, or another control character. The generator warns about quotes and line breaks, as described in Generation Warnings. - A
multipleOfthat the generator can’t check exactly only appears in the field’s documentation. See Multiples.
Flag Summary
This table collects the options used across the generation chapters. Defaults apply when
you omit the corresponding option. Run oas3-gen generate --help to see the
command-line help, including input, output, and terminal options.
| Flag | Default | Description |
|---|---|---|
mode | types | Generation mode: types, client, client-mod, server-mod |
-w, --workspace | false | Emit a workspace-compatible Cargo.toml with sources in src/; requires client-mod or server-mod |
--module-version | 0.0.0 | Version written to the generated Cargo.toml [package] table; requires --workspace |
-C, --visibility | public | Item visibility: public, crate, file |
--enum-mode | merge | Enum duplicate handling: merge, preserve, relaxed |
--enum-layout | spec | Variant ordering: spec, sorted |
--no-helpers | false | Disable enum constructor helpers |
--odata-support | false | Make @odata.* fields optional |
-c, --customize | (none) | Custom serde_with adapter; repeatable |
--fn-name | (none) | Custom function name per operation (ID=NAME); repeatable |
--api-name | (none) | Name for the generated client struct or server trait |
--all-headers | false | Emit header constants for all component-level headers |
--enable-builders | false | Enable bon builder derives and methods |
--no-ordered-collections | false | Emit HashMap/Vec instead of indexmap collection types |
--doc-format | false | Format documentation comments with mdformat |
--only | (none) | Include only specified operations |
--exclude | (none) | Exclude specified operations |
--all-schemas | false | Generate all schemas regardless of usage |