-
Notifications
You must be signed in to change notification settings - Fork 225
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add
utoipa-axum
binding example and update docs (#1007)
This PR adds new `axum` app example to demonstrate new seamless integration between axum and utoipa. Along the above this commit streamlines the user facing API in `utoipa-axum`, fixes some minor bugs, update docs and adds one extra feature which allows using expressions e.g. `const` references in `tags((name = ...))` attribute. This allows sharing a single `const` reference between OpenApi and the handlers annotated with `#[utoipa::path(...)]` Fixes #780
- Loading branch information
Showing
12 changed files
with
274 additions
and
55 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
[package] | ||
name = "axum-utoipa-bindings" | ||
version = "0.1.0" | ||
edition = "2021" | ||
|
||
[dependencies] | ||
axum = "0.7" | ||
tokio = { version = "1", features = ["full"] } | ||
tower = "0.5" | ||
utoipa = { path = "../../utoipa", features = ["axum_extras", "debug"] } | ||
utoipa-swagger-ui = { path = "../../utoipa-swagger-ui", features = ["axum"] } | ||
utoipa-axum = { path = "../../utoipa-axum" ,features = ["debug"] } | ||
serde = "1" | ||
serde_json = "1" | ||
|
||
[workspace] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
# utoipa with axum bindings | ||
|
||
This demo `axum` application demonstrates `utoipa` and `axum` seamless integration with `utoipa-axum` crate. | ||
API doc is served via Swagger UI. | ||
|
||
Run the app | ||
```bash | ||
cargo run | ||
``` | ||
|
||
Browse the API docs. | ||
``` | ||
http://localhost:8080/swagger-ui | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
use std::io; | ||
use std::net::Ipv4Addr; | ||
|
||
use tokio::net::TcpListener; | ||
use utoipa::OpenApi; | ||
use utoipa_axum::router::OpenApiRouter; | ||
use utoipa_axum::routes; | ||
use utoipa_swagger_ui::SwaggerUi; | ||
|
||
const CUSTOMER_TAG: &str = "customer"; | ||
const ORDER_TAG: &str = "order"; | ||
|
||
#[derive(OpenApi)] | ||
#[openapi( | ||
tags( | ||
(name = CUSTOMER_TAG, description = "Customer API endpoints"), | ||
(name = ORDER_TAG, description = "Order API endpoints") | ||
) | ||
)] | ||
struct ApiDoc; | ||
|
||
/// Get health of the API. | ||
#[utoipa::path( | ||
method(get, head), | ||
path = "/api/health", | ||
responses( | ||
(status = OK, description = "Success", body = str, content_type = "text/plain") | ||
) | ||
)] | ||
async fn health() -> &'static str { | ||
"ok" | ||
} | ||
|
||
#[tokio::main] | ||
async fn main() -> Result<(), io::Error> { | ||
let (router, api) = OpenApiRouter::with_openapi(ApiDoc::openapi()) | ||
.routes(routes!(health)) | ||
.nest("/api/customer", customer::router()) | ||
.nest("/api/order", order::router()) | ||
.split_for_parts(); | ||
|
||
let router = router.merge(SwaggerUi::new("/swagger-ui").url("/apidoc/openapi.json", api)); | ||
|
||
let listener = TcpListener::bind((Ipv4Addr::LOCALHOST, 8080)).await?; | ||
axum::serve(listener, router).await | ||
} | ||
|
||
mod customer { | ||
use axum::Json; | ||
use serde::Serialize; | ||
use utoipa::{OpenApi, ToSchema}; | ||
use utoipa_axum::router::OpenApiRouter; | ||
use utoipa_axum::routes; | ||
|
||
#[derive(OpenApi)] | ||
#[openapi(components(schemas(Customer)))] | ||
struct CustomerApi; | ||
|
||
/// This is the customer | ||
#[derive(ToSchema, Serialize)] | ||
struct Customer { | ||
name: String, | ||
} | ||
|
||
/// expose the Customer OpenAPI to parent module | ||
pub fn router() -> OpenApiRouter { | ||
OpenApiRouter::with_openapi(CustomerApi::openapi()).routes(routes!(get_customer)) | ||
} | ||
|
||
/// Get customer | ||
/// | ||
/// Just return a static Customer object | ||
#[utoipa::path(get, path = "", responses((status = OK, body = Customer)), tag = super::CUSTOMER_TAG)] | ||
async fn get_customer() -> Json<Customer> { | ||
Json(Customer { | ||
name: String::from("Bill Book"), | ||
}) | ||
} | ||
} | ||
|
||
mod order { | ||
use axum::Json; | ||
use serde::{Deserialize, Serialize}; | ||
use utoipa::{OpenApi, ToSchema}; | ||
use utoipa_axum::router::OpenApiRouter; | ||
use utoipa_axum::routes; | ||
|
||
#[derive(OpenApi)] | ||
#[openapi(components(schemas(Order, OrderRequest)))] | ||
struct OrderApi; | ||
|
||
/// This is the order | ||
#[derive(ToSchema, Serialize)] | ||
struct Order { | ||
id: i32, | ||
name: String, | ||
} | ||
|
||
#[derive(ToSchema, Deserialize, Serialize)] | ||
struct OrderRequest { | ||
name: String, | ||
} | ||
|
||
/// expose the Order OpenAPI to parent module | ||
pub fn router() -> OpenApiRouter { | ||
OpenApiRouter::with_openapi(OrderApi::openapi()).routes(routes!(get_order, create_order)) | ||
} | ||
|
||
/// Get static order object | ||
#[utoipa::path(get, path = "", responses((status = OK, body = Order)), tag = super::ORDER_TAG)] | ||
async fn get_order() -> Json<Order> { | ||
Json(Order { | ||
id: 100, | ||
name: String::from("Bill Book"), | ||
}) | ||
} | ||
|
||
/// Create an order. | ||
/// | ||
/// Create an order by basically passing through the name of the request with static id. | ||
#[utoipa::path(post, path = "", responses((status = OK, body = OrderRequest)), tag = super::ORDER_TAG)] | ||
async fn create_order(Json(order): Json<OrderRequest>) -> Json<Order> { | ||
Json(Order { | ||
id: 120, | ||
name: order.name, | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.