Swagger/OpenAPI Generation for Rust: Utoipa Library

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

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,}

Recommendations