Swagger/OpenAPI Generation for Rust: Utoipa — A Modern Tool for Automatic Documentation
When developing web applications in Rust, API documentation is not a luxury but a necessity. The Utoipa library (short for "Utopia") provides a powerful and elegant way to generate OpenAPI specifications directly from code. It integrates with popular web frameworks such as actix-web, axum, rocket, and warp, and allows you to describe endpoints, data schemas, and parameters using attributes and macros.
Key Features of Utoipa
- Automatic OpenAPI 3.0/3.1 generation — no manual YAML/JSON writing.
- Support for multiple frameworks — actix-web, axum, rocket, warp.
- Integration with Swagger UI — built-in component for visualizing documentation.
- Deep schema description — support for nested structures, enums, optional fields.
- Macros for quick description —
#[utoipa::path],#[openapi]. - Specification validation — checking the correctness of the generated document.
Installation and Setup
Add dependencies to Cargo.toml. For this example, we'll choose the actix-web framework:
[dependencies]utoipa = "4"utoipa-swagger-ui = { version = "4", features = ["actix-web"] }actix-web = "4"serde = { version = "1", features = ["derive"] }Basic Example: Creating an API with Documentation
Let's look at a simple CRUD for a "User" entity. We'll start by defining the data model:
use serde::{Deserialize, Serialize};use utoipa::ToSchema;
#[derive(Serialize, Deserialize, ToSchema)]pub struct User { #[schema(example = "1")] pub id: i32, #[schema(example = "john_doe")] pub username: String, #[schema(example = "john@example.com")] pub email: String,}Now let's describe the endpoint for getting a list of users using the #[utoipa::path] macro:
use actix_web::{get, web, HttpResponse, Responder};use utoipa::OpenApi;
#[utoipa::path( get, path = "/users", responses( (status = 200, description = "List of users", body = Vec<User>) ))]#[get("/users")]async fn get_users() -> impl Responder { let users = vec![ User { id: 1, username: "alice".into(), email: "alice@example.com".into() }, User { id: 2, username: "bob".into(), email: "bob@example.com".into() }, ]; HttpResponse::Ok().json(users)}Building the OpenAPI Document and Swagger UI
Create a structure to describe the entire API:
#[derive(OpenApi)]#[openapi( paths(get_users), components(schemas(User)))]pub struct ApiDoc;
// In the main function, configure Swagger UIuse utoipa_swagger_ui::SwaggerUi;
#[actix_web::main]async fn main() -> std::io::Result<()> { use actix_web::App;
HttpServer::new(|| { App::new() .service(get_users) .service( SwaggerUi::new("/swagger-ui/{_:.*}") .url("/api-docs/openapi.json", ApiDoc::openapi()) ) }) .bind("127.0.0.1:8080")? .run() .await}After starting the application, the documentation will be available at http://localhost:8080/swagger-ui/.
Advanced Features: Nested Schemas and Parameters
Utoipa supports complex data types. For example, let's add a field with a nested structure:
#[derive(Serialize, Deserialize, ToSchema)]pub struct Address { #[schema(example = "Moscow")] pub city: String, #[schema(example = "Lenina St., 1")] pub street: String,}
Also in library
Elasticsearch client for Go (Golang): complete guide
OAuth Client for C# (.NET): Implementing Authorization via Third-Party Services
Working with Excel in TypeScript: Libraries, Tools, and Code Examples
DomPDF for PHP: Complete Guide with Examples