Expand description
§okapi-operation
okapi-operation
Crate which allow to generate OpenAPI’s operation definitions (using types from okapi
crate) with procedural macro openapi
.
§Example (using axum, but without axum_integration feature)
use axum::{
extract::Query,
http::Method,
routing::{get, post},
Json, Router,
};
use okapi_operation::*;
use serde::Deserialize;
#[derive(Deserialize, JsonSchema)]
struct Request {
/// Echo data
data: String,
}
#[openapi(
summary = "Echo using GET request",
operation_id = "echo_get",
tags = "echo",
parameters(
query(name = "echo-data", required = true, schema = "std::string::String",),
header(name = "x-request-id", schema = "std::string::String",)
)
)]
async fn echo_get(query: Query<Request>) -> Json<String> {
Json(query.0.data)
}
#[openapi(
summary = "Echo using POST request",
operation_id = "echo_post",
tags = "echo"
)]
async fn echo_post(
#[request_body(description = "Echo data", required = true)] body: Json<Request>,
) -> Json<String> {
Json(body.0.data)
}
async fn openapi_spec() -> Json<OpenApi> {
let generate_spec = || {
OpenApiBuilder::new("Echo API", "1.0.0")
.add_operation("/echo/get", Method::GET, echo_get__openapi)?
.add_operation("/echo/post", Method::POST, echo_post__openapi)?
.generate_spec()
};
generate_spec().map(Json).expect("Should not fail")
}
#[tokio::main]
async fn main() {
let app = Router::new()
.route("/echo/get", get(echo_get))
.route("/echo/post", post(echo_post))
.route("/openapi", get(openapi_spec));
axum::Server::bind(&"0.0.0.0:3000".parse().unwrap())
.serve(app.into_make_service())
.await
.unwrap();
}
§openapi
macro
This macro generate function with name <original_name>__openapi
of type fn(&mut Components) -> Result<Operation, anyhow::Error>
(OperationGenerator
), which generate okapi::openapi3::Operation
, storing type definitions in provided Components
.
If any attribute is missing, it is set to None/false.
Since most attributes taken from OpenAPI specification directly, refer to OpenAPI website for additional information.
§Minimal example
Macro doesn’t have any mandatory attributes.
#[openapi]
async fn handler() {}
§Operation attributes
All attributes is translated into same fields of okapi::openapi3::Operation
.
Tags is provided as single string, which later is separated by comma.
#[openapi(
summary = "Simple handler",
description = "Simple handler, demonstrating how to use operation attributes",
operation_id = "simple",
tags = "examples,handlers",
deprecated = false
)]
async fn handler() {}
§External documentation
External documentation can be set for operation. It is translated to okapi::openapi3::ExternalDocs
.
#[openapi(
external_docs(
url = "https://example.com",
description = "Example Domain"
)
)]
async fn handler() {}
§Request parameters
Request parameters can be:
- HTTP header (
location: header
); - query parameter (
?param=value
) (location: query
); - part of the path (
/api/user/:id
, where:id
is parameter) (location: path
); - reference to one of the above.
Parameters is defined in [openapi]
macro. Inferring header from fucntion signature is not supported currently.
This definition translated to okapi::openapi3::Parameter
with okapi::openapi3::ParameterValue::Schema
.
§Header
header
have following attributes:
- name (string, mandatory);
- description (string, optional);
- required (bool, optional);
- deprecated (bool, optional);
- style (string, optional) - how parameter is serialized (see OpenAPI docs);
- schema (path, mandatory) - path to type of parameter.
#[openapi(
parameters(
header(
name = "x-custom-header",
description = "Custom header description",
required = true,
deprecated = false,
style = "simple",
schema = "std::string::String",
)
)
)]
async fn handler() {}
§Query
query
have following attributes:
- name (string, mandatory);
- description (string, optional);
- required (bool, optional);
- deprecated (bool, optional);
- style (string, optional) - how parameter is serialized (https://swagger.io/docs/specification/serialization/);
- explode (bool, optional) - specifies whether arrays and objects should generate separate parameters for each array item or object property;
- allow_empty_value (bool, optional) - allow empty value for this parameter;
- allow_reserved (bool, optional) - allow reserved characters
:/?#[]@!$&'()*+,;=
in parameter; - schema (path, mandatory) - path to type of parameter.
#[openapi(
parameters(
query(
name = "page",
description = "Which page to return",
required = true,
deprecated = false,
style = "simple",
explode = true,
allow_empty_value = false,
allow_reserved = false,
schema = "std::string::String",
)
)
)]
async fn handler() {}
§Path
path
have following attributes:
- name (string, mandatory);
- description (string, optional);
- deprecated (bool, optional);
- style (string, optional) - how parameter is serialized (https://swagger.io/docs/specification/serialization/);
- schema (path, mandatory) - path to type of parameter.
Unlike header and query parameters, all path parameters is mandatory.
#[openapi(
parameters(
path(
name = "user_id",
description = "ID of user",
deprecated = false,
style = "simple",
schema = "std::string::String",
)
)
)]
async fn handler() {}
§Cookie
cookie
have following attributes:
- name (string, mandatory);
- description (string, optional);
- required (bool, optional);
- deprecated (bool, optional);
- explode (bool, optional) - specifies whether arrays and objects should generate separate parameters for each array item or object property;
- allow_empty_value (bool, optional) - allow empty value for this parameter;
- schema (path, mandatory) - path to type of parameter.
#[openapi(
parameters(
cookie(
name = "session_id",
description = "Session ID",
required = false,
deprecated = false,
explode = true,
allow_empty_value = false,
schema = "std::string::String",
)
)
)]
async fn handler() {}
§Reference
#[openapi(
parameters(
reference = "#/components/parameters/ReusableHeader"
)
)]
async fn handler() {}
§Multiple parameters
Specifying multiple parameters is supported:
#[openapi(
parameters(
header(
name = "x-request-id",
description = "ID of request for logging",
required = true,
deprecated = false,
style = "simple",
schema = "std::string::String",
),
header(
name = "traceparent",
description = "ID of parent span",
required = true,
deprecated = false,
style = "simple",
schema = "std::string::String",
),
path(
name = "user_id",
description = "ID of user",
deprecated = false,
style = "simple",
schema = "std::string::String",
),
reference = "#/components/parameters/ReusableHeader"
),
)]
async fn handler() {}
§Request body
Request body is associated with one of function arguments and by default it’s schema is inferred from argument type.
Request body definition have following attributes:
- description (string, optional);
- required (bool, optional);
- content (path, optional) - path to type, which schema should be used. If not speified, argument’s type is used.
#[derive(JsonSchema)]
struct Request {
user_id: String
}
#[openapi]
async fn handler(
#[request_body(
description = "JSON with user ID",
required = true,
)] body: Json<Request>
) {}
#[openapi]
async fn handler_with_request_body_override(
#[request_body(
description = "JSON with user ID",
required = true,
content = "Json<std::string::String>",
)] body: Json<Request>
) {}
§Responses
Responses can be:
- inferred from return type;
- specified in
openapi
macro.
§From return type
Return type should implement ToResponses
trait.
#[derive(JsonSchema)]
struct Response {
data: String
}
#[openapi]
async fn handler() -> Json<Response> {
}
§Ignore return type
If return type doesn’t implement ToResponses
, it can be ignored with special attribute ignore_return_type
:
#[openapi(
responses(
ignore_return_type = true,
)
)]
async fn handler() -> String {
}
§Manual definition
Manual definition is helpful when you type for some reason doesn’t implement ToResponses
or
if you need to specify some responses, which can occur outside handler (in middleware, for example).
§Single response
Single response define response for a single HTTP status (or pattern). Schema of this response should implement ToMediaTypes
.
Single response have following attributes:
- status (string, mandatory) - HTTP status (or pattern like 2XX, 3XX). To define defautl fallback type, use special
default
value; - description (string, optional);
- content (path, mandatory) - path to type, which provide schemas for this response;
- headers (list, optional) - list of headers (definition is the same as in request parameters). References to header is also allowed.
#[derive(JsonSchema)]
struct Response {
data: String
}
#[openapi(
responses(
response(
status = "200",
description = "Success",
content = "Json<Response>",
headers(
header(
name = "x-custom-message",
description = "Description",
required = true,
deprecated = false,
style = "simple",
schema = "std::string::String",
),
reference = "#/components/headers/ReusableHeader"
),
),
)
)]
async fn handler() {
}
§From type
Responses can be generated from type, which implement ToResponses
:
#[derive(JsonSchema)]
struct Response {
data: String
}
#[openapi(
responses(
from_type = "Json<String>",
)
)]
async fn handler() {
}
Json<String>
generates single 200 response with JSON with single string.
§Reference
Reference to response have following attributes:
- status (string, mandatory) - HTTP status (or pattern like 2XX, 3XX). To define defautl fallback type, use special
default
value; - reference (string, mandatory).
#[openapi(
responses(
reference(
status = "200",
reference = "#/components/responses/Reference"
)
)
)]
async fn handler() {
}
§Multiple responses
If mutliple manual responses is specified (or specified both return type and manual responses),
they are all merged using okapi::merge::merge_responses
. If multiple responses specified for same HTTP status,
first occurence is used. Responses merged in following order:
- from return type;
- manual single responses;
- references;
- from types.
#[derive(JsonSchema)]
struct Response {
data: String
}
#[openapi(
responses(
response(
status = "500",
description = "Internal server error",
content = "Json<String>",
),
reference(
status = "401",
reference = "#/components/responses/AuthError"
),
reference(
status = "403",
reference = "#/components/responses/AuthError"
)
)
)]
async fn handler() -> Json<Response> {
}
§Security scheme
Security scheme have following attributes:
- name (string, mandatory) - name of used security scheme;
- scopes (string, optional) - comma separated list of scopes. Have meaning only for
OAuth2
andOpenID Connect
.
If multiple schemes specified, they are combined as OR. AND is not currently supported.
#[openapi(
security(
security_scheme(
name = "BasicAuth",
),
security_scheme(
name = "OAuth2",
scopes = "scope1,scope2",
),
),
)]
async fn handler() {}
§Building OpenAPI specification
For convenience this crate provide builder-like OpenApiBuilder
type for creating OpenAPI specification:
#[derive(JsonSchema)]
struct Request {
user_id: String
}
#[openapi]
async fn handler1(
#[request_body(
description = "JSON with user ID",
required = true,
)] body: Json<Request>
) {
}
#[openapi]
async fn handler2() -> Json<String> {
}
fn generate_openapi_specification() -> Result<OpenApi, anyhow::Error> {
OpenApiBuilder::new("Demo", "1.0.0")
.add_operation("/handle/1", Method::POST, handler1__openapi)?
.add_operation("/handle/2", Method::GET, handler2__openapi)?
.generate_spec()
}
assert!(generate_openapi_specification().is_ok());
§Features
macro
: enables re-import ofopenapi
macro (enabled by default);axum-integration
: enables integration withaxum
crate (implement traits for certainaxum
types). Seecrate::axum_integration
for details.
§TODO
-
support examples on MediaType or Parameter (examples supported on types via
JsonSchema
macro) - support inferring schemas of parameters from function definitions
- support for renaming or changing paths to okapi/schemars/okapi-operations in macro
- more examples
- …
Re-exports§
pub use okapi;
pub use okapi::openapi3::OpenApi;
pub use okapi::schemars;
pub use okapi::schemars::JsonSchema;
pub use okapi::schemars::JsonSchema;
pub use anyhow;
Modules§
- axum_
integration - Integration with axum
Macros§
- impl_
to_ media_ types_ for_ wrapper - Generate
ToMediaTypes
implementation for newtype. - impl_
to_ responses_ for_ wrapper - Generate
ToResponses
implementation for newtype. - openapi_
handler - Macro for expanding and binding OpenAPI operation specification generator to handler or service.
- openapi_
service Deprecated - Macro for expanding and binding OpenAPI operation specification generator to handler or service.
Structs§
- Components
- Storage for reusable components (schemas/parameters/responses/…).
- Components
Builder - Builder for
Components
- Open
ApiBuilder - OpenAPI specificatrion builder.
Traits§
- ToMedia
Types - Generate
MediaType
for type. - ToResponses
- Generate
Responses
for type.
Type Aliases§
- Empty
- Empty type alias (for using in attribute values).
- Operation
Generator - Operation generator signature.