Client Generation
Client generation creates a reqwest client and the request and response types for an
API. We’ll generate the petstore client, configure its credentials, and call an
operation. Later sections cover request bodies, response decoding, and event streams.
The commands run from a checkout of this repository and use the petstore fixture,
fixtures/petstore.json . If you installed the tool, replace cargo run -- with
oas3-gen and supply the path to your document. Rust excerpts assume you’ve imported
the generated types. Put snippets that use ? in a function returning
anyhow::Result , and use an async function for calls that
contain .await .
- Choosing the Output Layout
- Creating a Client
- Sending Credentials
- Making a Request
- Sending Request Bodies
- Reading Responses
- Reading Server-Sent Events
- Deprecated Operations
- Choosing Operations and Names
Choosing the Output Layout
The client mode puts the client and its types in one file:
cargo run -- generate client \
-i fixtures/petstore.json -o client.rs \
--enable-builders
Here, --enable-builders adds the request builders we’ll use below. The client’s name
comes from the document’s title, Petstore , so the generated client is
PetstoreClient . To choose a different name, add
--api-name .
Use client-mod if you’d like to keep the client and types in separate files:
cargo run -- generate client-mod \
-i fixtures/petstore.json -o src/api/ \
--enable-builders
The output directory contains three files:
src/api/
├── mod.rs
├── types.rs
└── client.rs
The mod.rs file declares both modules and re-exports their items:
mod types;
mod client;
pub use types::*;
pub use client::*;
To include this directory in your application, declare mod api; in your crate root.
You’ll also need the dependencies referenced by the generated code. If you’d prefer a
separate crate with its own manifest, add --workspace and use a crate directory as the
output path. See
Workspace Crate Output for the layout
and dependency rules.
Creating a Client
The client stores a reqwest::Client and a
reqwest::Url for the base URL. You can call new() to use the
first URL in the document’s servers list, https://api.petstore.example/v3 . To
choose a different endpoint, such as one of the petstore’s regional deployments, call
with_base_url() :
let client = PetstoreClient::with_base_url("https://eu-central.api.petstore.example/v3")?;
This constructor returns a Result because parsing the URL or
building the HTTP client can fail. Replace the example URL with the API endpoint you
want to call.
If your application already has a configured reqwest::Client , pass it to
PetstoreClient::with_client(base_url, http_client) . For clients that use cookie API
keys, you’ll also need to connect the generated cookie store, as shown in
Using a Custom HTTP Client .
Sending Credentials
For operations with apiKey or bearer security schemes, the client provides methods to
set credentials. See Security Schemes for how schemes and
requirements are declared.
The petstore’s top-level security requires a header key named ApiKeyAuth , so most
operations accept it. Individual operations also accept a query key named QueryKey , a
cookie key named SessionCookie , a bearer token named BearerAuth , and an apiKey
scheme named TokenAuth that travels in the Authorization header. To configure the
header key, read it from your application’s environment and pass it to the generated
setter:
let api_key = std::env::var("API_KEY")?;
let client = PetstoreClient::new().with_api_key_auth(api_key);
The client stores header keys as http::HeaderValue values
marked sensitive, and query keys and bearer tokens as
secrecy::SecretString values, each wrapped in
Option . A header key setter leaves the key unset when it’s empty or
isn’t a valid header value. The client gets one field and setter for each supported
scheme used by the selected operations. Names come from the scheme: ApiKeyAuth becomes
api_key_auth and with_api_key_auth() . If a name collides with another field,
including client or base_url , the generator adds a numeric suffix. Schemes the
generator doesn’t support, such as the petstore’s BasicAuth and PetstoreOAuth ,
don’t get a setter.
Each method attaches the configured header keys, query keys, and bearer tokens that its operation accepts. It doesn’t check whether you’ve supplied all the required credentials. An unset key is omitted from the request, and a generated server rejects it as described in Rejections from a Generated Server .
Bearer Tokens
A bearer scheme gets a field and setter, like a header key. The petstore’s BearerAuth
scheme produces with_bearer_auth() :
let token = std::env::var("STAFF_TOKEN")?;
let client = PetstoreClient::new().with_bearer_auth(token);
Pass the token by itself, without the Bearer prefix. For an operation that accepts the
scheme, such as revokeApiKey , the client method calls
reqwest::RequestBuilder::bearer_auth() . That method
sends the token in an Authorization: Bearer <token> header and marks the header value
as sensitive.
A request carries one Authorization header. When an operation accepts more than one
credential that travels in it, such as a bearer token and an apiKey scheme named
Authorization , the client sends only one of them: of the credentials you’ve
configured, the scheme declared last wins. The petstore’s listAuditEvents accepts
BearerAuth or TokenAuth , and TokenAuth is declared later, so a client with both
set sends the TokenAuth key.
Cookie Keys
Cookie keys use a shared
reqwest_cookie_store::CookieStoreMutex in the client’s
cookies field. The new() and with_base_url() constructors attach that store to the
underlying reqwest::Client . To add the petstore’s session
cookie, call its setter:
let session_key = std::env::var("SESSION_KEY")?;
let client = PetstoreClient::new().with_session_cookie(session_key);
The setter inserts a cookie named session for the base URL and sends your key as it
is, without percent-encoding, so a key with / , = , : , or @ reaches the server
unchanged. A generated server percent-decodes cookie values, so a key containing an
escape such as %41 arrives there as A . A key with a space or a semicolon can’t be
sent in a Cookie header. Reqwest then sends the cookie on requests that match its
scope, including requests to operations that don’t declare that scheme. The store also
retains cookies received from the server. Clones of the generated client share the
store, so setting a cookie key on a clone changes the store used by the original client
too.
Using a Custom HTTP Client
with_client() accepts an HTTP client that’s already built, so it can’t attach the
generated cookie store to it. To configure your own client with cookie support, build it
with the generated store and assign it to client :
use std::sync::Arc;
let mut api = PetstoreClient::with_base_url("https://api.petstore.example/v3")?;
api.client = reqwest::Client::builder()
.cookie_provider(Arc::clone(&api.cookies))
.build()?;
let api = api.with_session_cookie(std::env::var("SESSION_KEY")?);
The Arc lets api and its HTTP client share the same store. Add any
other configuration to the reqwest::ClientBuilder
before calling build() .
Keeping Keys Out of Logs
Formatting a generated client with {:?} hides its stored credentials. Sensitive
HeaderValue s print as Sensitive , and
SecretString redacts query keys and bearer tokens. The cookie store is omitted. See Keeping Keys Out of Logs
for the secret type’s behavior when you explicitly read a key.
Making a Request
Each operation has a request type. Path, query, header, and cookie parameters are grouped into nested structs. With builders enabled, setters assemble those structs for you. Let’s request a pet by its ID:
let pet_id = uuid::Uuid::parse_str("0192f0c1-7a6e-7c4e-9d2a-5b1e8f3c2a10")?;
let request = ShowPetByIdRequest::builder()
.pet_id(pet_id)
.x_api_version("2026-10".to_string())
.build()?;
let response = client.show_pet_by_id(request).await?;
The petId path parameter has format: uuid , so its setter takes a
uuid::Uuid . We haven’t called the setter for the optional
If-None-Match header, so the request leaves it out.
The builder checks the request’s generated validation constraints. The client method
also validates the request before constructing and sending the HTTP request. If
validation, transport, or response decoding fails, the method returns an error through
its outer anyhow::Result .
You can also construct requests with struct literals. The Builder Pattern chapter compares both forms and explains which checks happen during compilation and which happen at runtime.
Every HTTP method that OpenAPI allows gets a client method, including HEAD , OPTIONS
, and TRACE . For example, the petstore’s describePets operation is OPTIONS /pets
, and its 204 response lists the allowed methods in an Allow header:
if let ApiResponse::NoContent(WithHeaders { headers, .. }) =
client.describe_pets(DescribePetsRequest::default()).await?
{
println!("Allowed: {}", headers.allow);
}
Query Arrays
When a query parameter is an array, the client sends it according to the parameter’s
style and explode fields. The petstore’s searchPets operation has both kinds. This
excerpt shows two of its parameters:
{
"name": "colors",
"in": "query",
"style": "pipeDelimited",
"explode": false,
"schema": { "type": "array", "items": { "type": "string" } }
},
{
"name": "tags",
"in": "query",
"schema": { "type": "array", "items": { "type": "string" } }
}
Let’s search for pets with two tags and two coat colors:
let request = SearchPetsRequest::builder()
.q("golden retriever".to_string())
.tags(vec!["calm".to_string(), "shy".to_string()])
.colors(vec!["gold".to_string(), "cream".to_string()])
.build()?;
let response = client.search_pets(request).await?;
The client sends this query string:
q=golden+retriever&colors=gold%7Ccream&tags=calm&tags=shy
The tags parameter doesn’t set style or explode , so it uses OpenAPI’s default for
query parameters, form with explode: true . The client sends one tags= pair per
item. The colors parameter sets explode: false , so its items are joined with the
pipeDelimited separator into one value, gold|cream , which is percent-encoded as
gold%7Ccream .
The client serializes the query struct with serde_html_form
. It omits parameters set to None and accepts scalars or arrays of scalars. If the
untyped filter parameter contains a JSON object or nested array, the method returns an
error before sending the request.
The client percent-encodes reserved characters such as / , including when a parameter
sets allowReserved . The petstore’s q parameter sets that flag, but it doesn’t
change the generated request.
Cookie Parameters
Cookie parameters have setters like any other parameter. The petstore’s getPetCard
operation accepts a cardTheme cookie and a recentlyViewed list of pet IDs. Here,
first_viewed and second_viewed are two more uuid::Uuid values:
let request = GetPetCardRequest::builder()
.pet_id(pet_id)
.card_theme(GetPetCardRequestCookieCardTheme::Dark)
.recently_viewed(vec![first_viewed, second_viewed])
.build()?;
let response = client.get_pet_card(request).await?;
The client sends both cookies in one Cookie header:
cardTheme=dark; recentlyViewed=0192f0c1-7a6e-7c4e-9d2a-5b1e8f3c2a11%2C0192f0c1-7a6e-7c4e-9d2a-5b1e8f3c2a12
The client joins array items with commas, then uses the cookie crate
to percent-encode each value, including the commas. It omits optional cookie parameters
that you haven’t set.
Cookies from the client’s store, such as a cookie key , follow the
operation’s parameters in the same header: cardTheme=dark; session=... . If both
supply the same name, the parameter takes precedence. A stored cookie is still sent when
you leave the corresponding parameter unset.
Sending Request Bodies
When an operation declares a request body, its request type has a body field, and the
builder has a body() setter. The client sends the body in the media type the operation
declares and sets Content-Type to match:
| Declared media type | How the client sends the body |
|---|---|
application/json | As JSON, with Content-Type: application/json |
A +json type, such as application/merge-patch+json | As JSON, with the declared Content-Type |
application/x-www-form-urlencoded | As form fields |
text/*, such as text/plain | As text, with the declared Content-Type |
A binary type, such as image/png | As raw bytes, with the declared Content-Type |
A wildcard, such as */* or image/* | In the body’s own kind: JSON as application/json, text as text/plain, bytes as application/octet-stream |
multipart/form-data | As parts; see Sending Multipart Bodies |
A charset parameter other than UTF-8 on a text or JSON body is replaced with
charset=utf-8 , because the client sends that text as UTF-8. A binary body carries no
charset, so its charset parameter is dropped.
For example, the petstore’s updatePet operation takes a JSON merge patch. Its
update_pet() method sets the declared media type and then serializes the body:
let mut req_builder = self
.client
.patch(url)
.header(reqwest::header::CONTENT_TYPE, "application/merge-patch+json")
.json(&request.body);
In this excerpt from client.rs , optional patch fields use Option
. Serialization omits fields set to None , so this client can’t send null to clear a
nullable field such as nickname .
A body whose schema is a primitive, such as a string, keeps that schema’s constraints.
The petstore’s setPetNotes operation declares a text/plain body with
maxLength: 2000 , so the generated request validates the body like any other field:
let request = SetPetNotesRequest::builder()
.pet_id(pet_id)
.body("Prefers the quiet kennel by the window.".to_string())
.build()?;
let response = client.set_pet_notes(request).await?;
The client sends the notes as text/plain . Notes longer than 2,000 characters make
build() return a validation error, and the client method checks the same constraint
before sending.
Sending Multipart Bodies
When an operation’s request body uses multipart/form-data , the client method sends
the body struct as a form with one or more parts per property.
Multipart Form Bodies describes which part each
property becomes. The example below shows how the client builds the form.
The petstore’s submitIntakeForm operation takes an IntakeForm body with name ,
file , attachments , and quality fields, among others. Its submit_intake_form()
method moves each field into a reqwest::multipart::Form :
let body = request.body;
let mut form = reqwest::multipart::Form::new().percent_encode_noop();
form = form.text("name", body.name);
if let Some(value) = body.file {
form = form
.part(
"file",
reqwest::multipart::Part::bytes(value)
.file_name("file")
.mime_str("application/octet-stream")?,
);
}
for value in body.attachments {
form = form
.part(
"attachments",
reqwest::multipart::Part::bytes(value)
.file_name("attachments")
.mime_str("application/octet-stream")?,
);
}
if let Some(value) = body.quality {
form = form.text("quality", value.to_string());
}
req_builder = req_builder.multipart(form);
This is an excerpt from the generated client.rs , trimmed to those four fields. A
binary field becomes a file part with a filename, which is how servers such as FastAPI
tell an uploaded file from a text field. The filename is the property name because the
specification doesn’t name the file. An array sends one part per item under the same
name, and a None field sends no part at all. The bytes move into the part rather than
being copied. Part names are sent as raw UTF-8, as browsers send them, so the form’s
résumé property keeps its name.
When the multipart body itself is optional, as in the petstore’s uploadMedia
operation, the request’s body field is an Option . A None body
sends a request with no body and no Content-Type . A body whose fields are all unset
still sends an empty multipart/form-data body.
Reading Responses
ApiResponse represents a decoded HTTP response. Its variants identify statuses, and
its type parameters hold the operation’s success and failure payloads. For example, a
declared 404 response can decode successfully into an ApiResponse variant. A
transport or decoding failure returns an error through the method’s outer Result .
For show_pet_by_id , the success payload contains both a pet and response headers. Its
failures use two schemas: the 404 response returns a Problem , while the 4XX range
and the default response return an Error . The generator combines them in the
payload union ErrorOrProblem . We can match the response returned by
show_pet_by_id() in Making a Request :
match response {
ApiResponse::Ok(WithHeaders { headers, body: pet }) => {
println!("Pet: {}", pet.name);
println!("Cache status: {:?}", headers.x_cache);
}
ApiResponse::NotModified => println!("Pet unchanged"),
ApiResponse::NotFound(ErrorOrProblem::Problem(problem)) => {
eprintln!("Not found: {:?}", problem.detail);
}
ApiResponse::ClientError(status, ErrorOrProblem::Error(error))
| ApiResponse::Unknown(status, ErrorOrProblem::Error(error)) => {
eprintln!("{status}: {}", error.message);
}
other => anyhow::bail!("unexpected response: {other:?}"),
}
The Ok arm gives us the body and headers separately. NotModified is the 304
response, which has no body. ClientError represents the 4XX range and Unknown the
default response; both carry the status that arrived. The wildcard arm covers the
remaining variants because operations share one response enum, including variants that
this operation doesn’t return.
The client decodes each body in the media type its status declares. For example,
get_pet_card() returns its text/plain kennel card as ApiResponse<String, Problem>
, and get_pet_photo() returns its image/png photo as ApiResponse<Vec<u8>, Problem>
. See Response Media Types .
A response that the document doesn’t describe comes back as ApiResponse::Other ,
holding the unread reqwest::Response . That includes a
status the operation doesn’t declare, such as 405 Method Not Allowed . It also
includes a declared status under a media type the status doesn’t declare, such as an
empty text/html 404 page from a proxy where showPetById promises a JSON Problem
. The method returns these responses as values rather than errors, so you can still read
the status, headers, and body:
if let ApiResponse::Other(response) = response {
let status = response.status();
let body = response.text().await?;
eprintln!("{status}: {body}");
}
The method returns an error when the request fails, or when a response the document describes can’t be decoded as described.
An optional response header becomes None when it’s absent or can’t be parsed. For a
list header, one invalid element makes the whole value None . A missing or invalid
required header causes decoding to return an error. See Responses
for how the generator chooses payload types, combines headers, and represents undeclared
statuses.
Missing and Null Fields
A struct whose schema gives any property a default or a const has
#[serde(default)] , so a missing field takes its value from the struct’s Default
implementation, as Field Defaults describes. The
client also reads null in such a struct’s required fields. Each required field without
a default of its own gets a serde_with::DefaultOnNull adapter, which reads null as
the field type’s default:
#[serde_with::serde_as]
#[serde(default)]
pub struct Order {
/* ... */
#[serde_as(as = "serde_with::DefaultOnNull")]
#[serde(rename = "petId")]
pub pet_id: uuid::Uuid,
/* ... */
}
Both "petId": null and an omitted petId give pet_id the nil UUID. When a field
already has an adapter, DefaultOnNull wraps it; for example,
serde_with::DefaultOnNull<serde_with::base64::Base64> . A field with its own default
or const still rejects null .
Constant Properties
A property with a const , or with an enum that lists a single value, has that one
value as its default. The client reads the property from the response like any other
field, so it keeps the value the server sends. The optional object property of
ChatPreference becomes:
pub struct ChatPreference {
/* ... */
#[default(Some("chat.preference".to_string()))]
pub object: Option<String>,
/* ... */
}
If the response omits object , deserialization uses the constant from
#[default(...)] . If the response supplies a different value, deserialization keeps
it. The constant sets a default; it doesn’t validate the received value.
Rejections from a Generated Server
A generated server answers some requests before its service runs. Missing credentials
receive 401 , and malformed headers or bodies receive 400 . Each of these responses
carries a text/plain reason rather than a body from the document’s error schemas; see
Returning Responses . The client decodes a
body only under a media type that its status declares, so unless the status declares a
plain-text body or no body at all, the method returns ApiResponse::Other with the
unread response.
For example, calling list_api_keys() without setting the ApiKeyAuth key sends a
request that a generated server rejects with 401 . Although listApiKeys declares a
JSON Error body for 401 , the method returns ApiResponse::Other rather than
ApiResponse::Unauthorized , and the response’s text names the missing credential. Set
the required credentials before calling an operation. The service’s own 401 responses,
such as one for a key it doesn’t recognize, use the declared body and decode normally.
Decoding a Response from Your Own Transport
Client request types also provide a parse_response() associated function. If you send
the HTTP request yourself, you can pass the resulting
reqwest::Response to the same decoder the generated client
uses:
let response = ShowPetByIdRequest::parse_response(http_response).await?;
Here, http_response is the response you’ve already received. The decoder reads
declared headers before decoding the body and returns the operation’s ApiResponse
type.
Reading Server-Sent Events
Some operations send a stream of events rather than one body. OpenAPI describes this
with a text/event-stream response, the media type of Server-Sent Events
(SSE), and the response’s schema describes one event. The petstore’s streamPetVitals
operation streams VitalsReading events, so its client method returns
ApiResponse<oas3_gen_support::EventStream<oas3_gen_support::SseFrame<VitalsReading>>, ()>
.
EventStream is a Stream whose items are
Result<SseFrame<VitalsReading>, DiagnosticsError> . Each SseFrame contains one
received event. Its accessors return:
data(): the event data decoded from JSON.event(): the event name.id(): the last event ID set by the stream.retry(): the most recent reconnection delay requested by the stream.
The stream creates these read-only frames. To print readings as they arrive, iterate over them:
use futures::StreamExt;
let request = StreamPetVitalsRequest::builder().pet_id(pet_id).build()?;
let mut readings = match client.stream_pet_vitals(request).await? {
ApiResponse::Ok(readings) => readings,
other => anyhow::bail!("unexpected response: {other:?}"),
};
while let Some(reading) = readings.next().await {
match reading {
Ok(frame) => println!("{} bpm", frame.data().heart_rate_bpm),
Err(e) => eprintln!("skipped an event: {e}"),
}
}
The stream_pet_vitals() method returns once the response headers arrive. The readings
are decoded as the server sends them, and the loop ends when the server closes the
stream. StreamExt::next() comes from the futures
crate, which the generated client manifest doesn’t declare, so add futures to your own
dependencies. When you only need the data, SseFrame::into_data() gives it to you
without the rest of the frame.
An event whose data doesn’t match VitalsReading yields a
DiagnosticsError::DeserializationError whose path names the field under data that
failed, such as data.heartRateBpm , and the stream continues with the next event. An
event without data is skipped. If reading the response body fails, the stream yields
DiagnosticsError::BodyReadError and then ends.
The parser follows the HTML standard’s event-stream rules . Lines can end with LF, CR, or CRLF, comment lines and unknown fields are ignored, a leading byte order mark is dropped, and invalid UTF-8 becomes U+FFFD. An event that the server cuts off before its closing blank line is never yielded.
Reading Whole Frames
Some response schemas describe an event’s data and metadata together. When a
text/event-stream schema is an object whose properties all come from data , event
, id , and retry , with data among them, the generator treats it as a frame
type. Speakeasy uses this convention, and OpenAPI 3.2 adopts it for event streams. The
petstore’s streamAssistantMessage streams a frame type:
"AssistantEventFrame": {
"type": "object",
"required": ["data", "event"],
"properties": {
"data": { "$ref": "#/components/schemas/AssistantEvent" },
"event": { "type": "string", "enum": ["message_start", "message_stop", "ping"] },
"id": { "type": "string" },
"retry": { "type": "integer", "minimum": 0 }
}
}
The excerpt omits some event names. For this schema, the client generates an alias of
SseFrame using the declared data and event types:
pub type AssistantEventFrame = oas3_gen_support::SseFrame<Box<AssistantEvent>, AssistantEventFrameEvent>;
Each part of the frame decodes like this:
datadecodes as its own schema. A plainstringkeeps the frame’s text as it is. A string withcontentMediaType: application/jsonand acontentSchemadecodes as that schema. Anything else, such as theAssistantEventreference here, decodes from JSON.eventdecodes into its declared type, such asAssistantEventFrameEvent. An unrecognized name produces aDeserializationErrorwithpath: event. If the frame omitsevent:, its name ismessage. That name becomesNonewhen the property is optional. An event declared as a string orconstusesString, the default event type.id()andretry()come from the stream, whatever types theidandretryproperties declare. A frame without anid:line keeps the last ID the stream set, including one sent in a frame without data, as browsers report it.retry()works the same way for the reconnection time.
A text/event-stream schema can also be a oneOf of frame types that each fix event
to a constant. The petstore’s streamAssistantTokens streams AssistantTokenFrame , a
oneOf of TokenFrame and UsageFrame whose events are token and usage . The
client generates an enum of the frames the members alias, SseFrame<String> and
SseFrame<Usage> , and picks the member from the frame’s event name. Let’s print the
tokens as they arrive and then the usage:
use futures::StreamExt;
let mut frames = match client.stream_assistant_tokens(StreamAssistantTokensRequest {}).await? {
ApiResponse::Ok(frames) => frames,
other => anyhow::bail!("unexpected response: {other:?}"),
};
while let Some(frame) = frames.next().await {
match frame? {
AssistantTokenFrame::Token(token) => print!("{}", token.data()),
AssistantTokenFrame::Usage(usage) => println!("\n{} tokens", usage.data().output_tokens),
}
}
A frame whose event name no member declares yields a DeserializationError whose path
is event , and the stream continues with the next frame. A frame without an event:
line goes to the member whose event is message , if there is one.
A frame schema may also appear in a JSON body or as another schema’s property. In that
case, the client preserves the generated struct for those uses. The stream still reads
SseFrame<..> values with the struct’s data and event types. If another schema
refers to a union of frame types, that union uses ordinary JSON event data instead of
whole-frame decoding.
streamAssistantMessage answers 200 with either a ContentResponse document or the
stream, so its payload is the union ContentResponseOrAssistantEventFrameStream . The
stream member is named after its item type with a Stream suffix, and the response’s
Content-Type decides which member you receive.
Ending and Resuming a Stream
A text/event-stream media type can name the data that ends the stream with the
x-sse-sentinel or x-speakeasy-sse-sentinel extension. When a media type declares
both, x-sse-sentinel wins. streamAssistantMessage declares
x-speakeasy-sse-sentinel: "[DONE]" , so its stream returns None when an event’s data
is [DONE] , and streamAssistantTokens declares x-sse-sentinel: "[END]" . The
sentinel itself is never decoded, so it doesn’t produce an error item. Without a
sentinel, the stream ends when the server closes it.
To resume an interrupted stream, wait for the delay from EventStream::retry() and send
EventStream::last_event_id() to the server, for example in a Last-Event-ID header.
These methods report the stream’s current state, including updates from frames without
data. A previously received frame’s id() and retry() retain the state from when that
frame arrived.
Deprecated Operations
When an operation sets deprecated: true , its client method carries Rust’s
#[deprecated] attribute, so code that calls it gets a compiler
warning. The petstore’s listCats operation has been superseded by listPets . This
excerpt shows the start of its generated method:
/// List cats
///
/// Superseded by `listPets` with `x-only: cat`.
///
/// * Path: `GET /cats`
#[deprecated]
pub async fn list_cats(
&self,
request: ListCatsRequest,
) -> anyhow::Result<ApiResponse<WithHeaders<XNextHeaders, Cats>, Error>> {
The method still sends the request. The operation’s description, copied into the
method’s documentation, usually tells you what to use instead. To keep calling the
method while you migrate, add #[allow(deprecated)] to the calling function.
Choosing Operations and Names
Use --only or --exclude to select
operations. Use --fn-name to rename
methods and their request types, or
--api-name to name the client itself. The
shared reference also covers visibility, enums,
type adapters, and collection choices.