Attribute Macro utoipa::path

source ·
#[path]
Expand description

Path attribute macro implements OpenAPI path for the decorated function.

This is a #[derive] implementation for Path trait. Macro accepts set of attributes that can be used to configure and override default values what are resolved automatically.

You can use the Rust’s own #[deprecated] attribute on functions to mark it as deprecated and it will reflect to the generated OpenAPI spec. Only parameters has a special deprecated attribute to define them as deprecated.

#[deprecated] attribute supports adding additional details such as a reason and or since version but this is is not supported in OpenAPI. OpenAPI has only a boolean flag to determine deprecation. While it is totally okay to declare deprecated with reason #[deprecated = "There is better way to do this"] the reason would not render in OpenAPI spec.

Doc comment at decorated function will be used for description and summary of the path. First line of the doc comment will be used as the summary and the whole doc comment will be used as description.

/// This is a summary of the operation
///
/// All lines of the doc comment will be included to operation description.
#[utoipa::path(get, path = "/operation")]
fn operation() {}

§Path Attributes

  • operation Must be first parameter! Accepted values are known http operations such as get, post, put, delete, head, options, connect, patch, trace.

  • path = "..." Must be OpenAPI format compatible str with arguments within curly braces. E.g {id}

  • operation_id = ... Unique operation id for the endpoint. By default this is mapped to function name. The operation_id can be any valid expression (e.g. string literals, macro invocations, variables) so long as its result can be converted to a String using String::from.

  • context_path = "..." Can add optional scope for path. The context_path will be prepended to beginning of path. This is particularly useful when path does not contain the full path to the endpoint. For example if web framework allows operation to be defined under some context path or scope which does not reflect to the resolved path then this context_path can become handy to alter the path.

  • tag = "..." Can be used to group operations. Operations with same tag are grouped together. By default this is derived from the handler that is given to OpenApi. If derive results empty str then default value crate is used instead.

  • request_body = ... | request_body(...) Defining request body indicates that the request is expecting request body within the performed request.

  • responses(...) Slice of responses the endpoint is going to possibly return to the caller.

  • params(...) Slice of params that the endpoint accepts.

  • security(...) List of SecurityRequirements local to the path operation.

§Request Body Attributes

Simple format definition by request_body = ...

  • request_body = Type, request_body = inline(Type) or request_body = ref("..."). The given Type can be any Rust type that is JSON parseable. It can be Option, Vec or Map etc. With inline(...) the schema will be inlined instead of a referenced which is the default for ToSchema types. ref("./external.json") can be used to reference external json file for body schema. Note! Utoipa does not guarantee that free form ref is accessbile via OpenAPI doc or Swagger UI, users are eligible to make these guarantees.

Advanced format definition by request_body(...)

  • content = ... Can be content = Type, content = inline(Type) or content = ref("..."). The given Type can be any Rust type that is JSON parseable. It can be Option, Vec or Map etc. With inline(...) the schema will be inlined instead of a referenced which is the default for ToSchema types. ref("./external.json") can be used to reference external json file for body schema. Note! Utoipa does not guarantee that free form ref is accessible via OpenAPI doc or Swagger UI, users are eligible to make these guarantees.

  • description = "..." Define the description for the request body object as str.

  • content_type = "..." Can be used to override the default behavior of auto resolving the content type from the content attribute. If defined the value should be valid content type such as application/json. By default the content type is text/plain for primitive Rust types, application/octet-stream for [u8] and application/json for struct and complex enum types.

  • example = ... Can be json!(...). json!(...) should be something that serde_json::json! can parse as a serde_json::Value.

  • examples(...) Define multiple examples for single request body. This attribute is mutually exclusive to the example attribute and if both are defined this will override the example. This has same syntax as examples(...) in Response Attributes examples(…)

Example request body definitions.

 request_body(content = String, description = "Xml as string request", content_type = "text/xml"),
 request_body = Pet,
 request_body = Option<[Pet]>,

§Response Attributes

  • status = ... Is either a valid http status code integer. E.g. 200 or a string value representing a range such as "4XX" or "default" or a valid http::status::StatusCode. StatusCode can either be use path to the status code or status code constant directly.

  • description = "..." Define description for the response as str.

  • body = ... Optional response body object type. When left empty response does not expect to send any response body. Can be body = Type, body = inline(Type), or body = ref("..."). The given Type can be any Rust type that is JSON parseable. It can be Option, Vec or Map etc. With inline(...) the schema will be inlined instead of a referenced which is the default for ToSchema types. ref("./external.json") can be used to reference external json file for body schema. Note! Utoipa does not guarantee that free form ref is accessible via OpenAPI doc or Swagger UI, users are eligible to make these guarantees.

  • content_type = "..." | content_type = [...] Can be used to override the default behavior of auto resolving the content type from the body attribute. If defined the value should be valid content type such as application/json. By default the content type is text/plain for primitive Rust types, application/octet-stream for [u8] and application/json for struct and complex enum types. Content type can also be slice of content_type values if the endpoint support returning multiple response content types. E.g ["application/json", "text/xml"] would indicate that endpoint can return both json and xml formats. The order of the content types define the default example show first in the Swagger UI. Swagger UI wil use the first content_type value as a default example.

  • headers(...) Slice of response headers that are returned back to a caller.

  • example = ... Can be json!(...). json!(...) should be something that serde_json::json! can parse as a serde_json::Value.

  • response = ... Type what implements ToResponse trait. This can alternatively be used to define response attributes. response attribute cannot co-exist with other than status attribute.

  • content((...), (...)) Can be used to define multiple return types for single response status. Supported format for single content is (content_type = response_body, example = "...", examples(...)). example and examples are optional arguments. Examples attribute behaves exactly same way as in the response and is mutually exclusive with the example attribute.

  • examples(...) Define multiple examples for single response. This attribute is mutually exclusive to the example attribute and if both are defined this will override the example.

    • name = ... This is first attribute and value must be literal string.
    • summary = ... Short description of example. Value must be literal string.
    • description = ... Long description of example. Attribute supports markdown for rich text representation. Value must be literal string.
    • value = ... Example value. It must be json!(...). json!(...) should be something that serde_json::json! can parse as a serde_json::Value.
    • external_value = ... Define URI to literal example value. This is mutually exclusive to the value attribute. Value must be literal string.

    Example of example definition.

     ("John" = (summary = "This is John", value = json!({"name": "John"})))
    

Minimal response format:

responses(
    (status = 200, description = "success response"),
    (status = 404, description = "resource missing"),
    (status = "5XX", description = "server error"),
    (status = StatusCode::INTERNAL_SERVER_ERROR, description = "internal server error"),
    (status = IM_A_TEAPOT, description = "happy easter")
)

More complete Response:

responses(
    (status = 200, description = "Success response", body = Pet, content_type = "application/json",
        headers(...),
        example = json!({"id": 1, "name": "bob the cat"})
    )
)

Response with multiple response content types:

responses(
    (status = 200, description = "Success response", body = Pet, content_type = ["application/json", "text/xml"])
)

Multiple response return types with content(...) attribute:

Define multiple response return types for single response status with their own example.

responses(
   (status = 200, content(
           ("application/vnd.user.v1+json" = User, example = json!(User {id: "id".to_string()})),
           ("application/vnd.user.v2+json" = User2, example = json!(User2 {id: 2}))
       )
   )
)

§Using ToResponse for reusable responses

ReusableResponse must be a type that implements ToResponse.

responses(
    (status = 200, response = ReusableResponse)
)

ToResponse can also be inlined to the responses map.

responses(
    (status = 200, response = inline(ReusableResponse))
)

§Responses from IntoResponses

Responses for a path can be specified with one or more types that implement IntoResponses.

responses(MyResponse)

§Response Header Attributes

  • name Name of the header. E.g. x-csrf-token

  • type Additional type of the header value. Can be Type or inline(Type). The given Type can be any Rust type that is JSON parseable. It can be Option, Vec or Map etc. With inline(...) the schema will be inlined instead of a referenced which is the default for ToSchema types. Reminder! It’s up to the user to use valid type for the response header.

  • description = "..." Can be used to define optional description for the response header as str.

Header supported formats:

("x-csrf-token"),
("x-csrf-token" = String, description = "New csrf token"),

§Params Attributes

The list of attributes inside the params(...) attribute can take two forms: Tuples or IntoParams Type.

§Tuples

In the tuples format, parameters are specified using the following attributes inside a list of tuples separated by commas:

  • name Must be the first argument. Define the name for parameter.

  • parameter_type Define possible type for the parameter. Can be Type or inline(Type). The given Type can be any Rust type that is JSON parseable. It can be Option, Vec or Map etc. With inline(...) the schema will be inlined instead of a referenced which is the default for ToSchema types. Parameter type is placed after name with equals sign E.g. "id" = String

  • in Must be placed after name or parameter_type. Define the place of the parameter. This must be one of the variants of openapi::path::ParameterIn. E.g. Path, Query, Header, Cookie

  • deprecated Define whether the parameter is deprecated or not. Can optionally be defined with explicit bool value as deprecated = bool.

  • description = "..." Define possible description for the parameter as str.

  • style = ... Defines how parameters are serialized by ParameterStyle. Default values are based on in attribute.

  • explode Defines whether new parameter=value is created for each parameter within object or array.

  • allow_reserved Defines whether reserved characters :/?#[]@!$&'()*+,;= is allowed within value.

  • example = ... Can method reference or json!(...). Given example will override any example in underlying parameter type.

§Parameter type attributes

These attributes supported when parameter_type is present. Either by manually providing one or otherwise resolved e.g from path macro argument when actix_extras crate feature is enabled.

  • format = ... May either be variant of the KnownFormat enum, or otherwise an open value as a string. By default the format is derived from the type of the property according OpenApi spec.

  • write_only Defines property is only used in write operations POST,PUT,PATCH but not in GET

  • read_only Defines property is only used in read operations GET but not in POST,PUT,PATCH

  • xml(...) Can be used to define Xml object properties for the parameter type. See configuration options at xml attributes of ToSchema

  • nullable Defines property is nullable (note this is different to non-required).

  • multiple_of = ... Can be used to define multiplier for a value. Value is considered valid division will result an integer. Value must be strictly above 0.

  • maximum = ... Can be used to define inclusive upper bound to a number value.

  • minimum = ... Can be used to define inclusive lower bound to a number value.

  • exclusive_maximum = ... Can be used to define exclusive upper bound to a number value.

  • exclusive_minimum = ... Can be used to define exclusive lower bound to a number value.

  • max_length = ... Can be used to define maximum length for string types.

  • min_length = ... Can be used to define minimum length for string types.

  • pattern = ... Can be used to define valid regular expression in ECMA-262 dialect the field value must match.

  • max_items = ... Can be used to define maximum items allowed for array fields. Value must be non-negative integer.

  • min_items = ... Can be used to define minimum items allowed for array fields. Value must be non-negative integer.

For example:

params(
    ("id" = String, Path, deprecated, description = "Pet database id"),
    ("name", Path, deprecated, description = "Pet name"),
    (
        "value" = inline(Option<[String]>),
        Query,
        description = "Value description",
        style = Form,
        allow_reserved,
        deprecated,
        explode,
        example = json!(["Value"])),
        max_length = 10,
        min_items = 1
    )
)

§IntoParams Type

In the IntoParams parameters format, the parameters are specified using an identifier for a type that implements IntoParams. See IntoParams for an example.

params(MyParameters)

Note! that MyParameters can also be used in combination with the tuples representation or other structs.

params(
    MyParameters1,
    MyParameters2,
    ("id" = String, Path, deprecated, description = "Pet database id"),
)

§Security Requirement Attributes

  • name Define the name for security requirement. This must match to name of existing SecuritySchema.
  • scopes = [...] Define the list of scopes needed. These must be scopes defined already in existing SecuritySchema.

Security Requirement supported formats:

(),
("name" = []),
("name" = ["scope1", "scope2"]),

Leaving empty () creates an empty SecurityRequirement this is useful when security requirement is optional for operation.

§actix_extras feature support for actix-web

actix_extras feature gives utoipa ability to parse path operation information from actix-web types and macros.

  1. Ability to parse path from actix-web path attribute macros e.g. #[get(...)].
  2. Ability to parse [std::primitive] or [String] or tuple typed path parameters from actix-web web::Path<...>.
  3. Ability to parse path and query parameters form actix-web web::Path<...>, web::Query<...> types with IntoParams trait.

See the actix_extras in action in examples todo-actix.

With actix_extras feature enabled the you can leave out definitions for path, operation and parameter types.

use actix_web::{get, web, HttpResponse, Responder};
use serde_json::json;

/// Get Pet by id
#[utoipa::path(
    responses(
        (status = 200, description = "Pet found from database")
    ),
    params(
        ("id", description = "Pet id"),
    )
)]
#[get("/pet/{id}")]
async fn get_pet_by_id(id: web::Path<i32>) -> impl Responder {
    HttpResponse::Ok().json(json!({ "pet": format!("{:?}", &id.into_inner()) }))
}

With actix_extras you may also not to list any params if you do not want to specify any description for them. Params are resolved from path and the argument types of handler

use actix_web::{get, web, HttpResponse, Responder};
use serde_json::json;

/// Get Pet by id
#[utoipa::path(
    responses(
        (status = 200, description = "Pet found from database")
    )
)]
#[get("/pet/{id}")]
async fn get_pet_by_id(id: web::Path<i32>) -> impl Responder {
    HttpResponse::Ok().json(json!({ "pet": format!("{:?}", &id.into_inner()) }))
}

§rocket_extras feature support for rocket

rocket_extras feature enhances path operation parameter support. It gives utoipa ability to parse path, path parameters and query parameters based on arguments given to rocket proc macros such as #[get(...)].

  1. It is able to parse parameter types for primitive types, [String], [Vec], [Option] or [std::path::PathBuf] type.
  2. It is able to determine parameter_in for IntoParams trait used for FromForm type of query parameters.

See the rocket_extras in action in examples rocket-todo.

§axum_extras feature support for axum

axum_extras feature enhances parameter support for path operation in following ways.

  1. It allows users to use tuple style path parameters e.g. Path((id, name)): Path<(i32, String)> and resolves parameter names and types from it.
  2. It enhances IntoParams derive functionality by automatically resolving parameter_in from Path<...> or Query<...> handler function arguments.

Resole path argument types from tuple style handler arguments.

/// Get todo by id and name.
#[utoipa::path(
    get,
    path = "/todo/{id}",
    params(
        ("id", description = "Todo id"),
        ("name", description = "Todo name")
    ),
    responses(
        (status = 200, description = "Get todo success", body = String)
    )
)]
async fn get_todo(
    Path((id, name)): Path<(i32, String)>
) -> String {
    String::new()
}

Use IntoParams to resolve query parameters.

#[derive(Deserialize, IntoParams)]
struct TodoSearchQuery {
    /// Search by value. Search is incase sensitive.
    value: String,
    /// Search by `done` status.
    done: bool,
}

/// Search Todos by query params.
#[utoipa::path(
    get,
    path = "/todo/search",
    params(
        TodoSearchQuery
    ),
    responses(
        (status = 200, description = "List matching todos by query", body = [String])
    )
)]
async fn search_todos(
    query: Query<TodoSearchQuery>,
) -> Json<Vec<String>> {
    Json(vec![])
}

§Examples

More complete example.

#[utoipa::path(
   post,
   operation_id = "custom_post_pet",
   path = "/pet",
   tag = "pet_handlers",
   request_body(content = Pet, description = "Pet to store the database", content_type = "application/json"),
   responses(
        (status = 200, description = "Pet stored successfully", body = Pet, content_type = "application/json",
            headers(
                ("x-cache-len" = String, description = "Cache length")
            ),
            example = json!({"id": 1, "name": "bob the cat"})
        ),
   ),
   params(
     ("x-csrf-token" = String, Header, deprecated, description = "Current csrf token of user"),
   ),
   security(
       (),
       ("my_auth" = ["read:items", "edit:items"]),
       ("token_jwt" = [])
   )
)]
fn post_pet(pet: Pet) -> Pet {
    Pet {
        id: 4,
        name: "bob the cat".to_string(),
    }
}

More minimal example with the defaults.

#[utoipa::path(
   post,
   path = "/pet",
   request_body = Pet,
   responses(
        (status = 200, description = "Pet stored successfully", body = Pet,
            headers(
                ("x-cache-len", description = "Cache length")
            )
        ),
   ),
   params(
     ("x-csrf-token", Header, description = "Current csrf token of user"),
   )
)]
fn post_pet(pet: Pet) -> Pet {
    Pet {
        id: 4,
        name: "bob the cat".to_string(),
    }
}

Use of Rust’s own #[deprecated] attribute will reflect to the generated OpenAPI spec and mark this operation as deprecated.

#[utoipa::path(
    responses(
        (status = 200, description = "Pet found from database")
    ),
    params(
        ("id", description = "Pet id"),
    )
)]
#[get("/pet/{id}")]
#[deprecated]
async fn get_pet_by_id(id: web::Path<i32>) -> impl Responder {
    HttpResponse::Ok().json(json!({ "pet": format!("{:?}", &id.into_inner()) }))
}

Define context path for endpoint. The resolved path shown in OpenAPI doc will be /api/pet/{id}.

#[utoipa::path(
    context_path = "/api",
    responses(
        (status = 200, description = "Pet found from database")
    )
)]
#[get("/pet/{id}")]
async fn get_pet_by_id(id: web::Path<i32>) -> impl Responder {
    HttpResponse::Ok().json(json!({ "pet": format!("{:?}", &id.into_inner()) }))
}

Example with multiple return types

#[utoipa::path(
    get,
    path = "/user",
    responses(
        (status = 200, content(
                ("application/vnd.user.v1+json" = User1, example = json!({"id": "id".to_string()})),
                ("application/vnd.user.v2+json" = User2, example = json!({"id": 2}))
            )
        )
    )
)]
fn get_user() -> Box<dyn User> {
  Box::new(User1 {id: "id".to_string()})
}

Example with multiple examples on single response.

 #[utoipa::path(
     get,
     path = "/user",
     responses(
         (status = 200, body = User,
             examples(
                 ("Demo" = (summary = "This is summary", description = "Long description",
                             value = json!(User{name: "Demo".to_string()}))),
                 ("John" = (summary = "Another user", value = json!({"name": "John"})))
              )
         )
     )
 )]
 fn get_user() -> User {
   User {name: "John".to_string()}
 }