Skip to main content
nestrs-openapi generates an OpenAPI 3.1 specification from the routes registered in RouteRegistry and serves a Swagger UI at a configurable path. Enable it with a single call to NestApplication::enable_openapi() or enable_openapi_with_options(options).
# Cargo.toml
[dependencies]
nestrs = { version = "*", features = ["openapi"] }

Quick start

use nestrs::prelude::*;

#[module]
struct AppModule;

#[tokio::main]
async fn main() {
    NestFactory::create::<AppModule>()
        .enable_openapi()  // mounts /openapi.json and /docs
        .listen(3000)
        .await;
}
After starting the server:
  • GET /openapi.json — the generated OpenAPI 3.1 JSON spec
  • GET /docs — Swagger UI (served via CDN from unpkg.com/swagger-ui-dist)

OpenApiOptions

Configures titles, paths, servers, security, and more.
pub struct OpenApiOptions {
    pub title: String,           // default: "nestrs API"
    pub version: String,         // default: CARGO_PKG_VERSION
    pub json_path: String,       // default: "/openapi.json"
    pub docs_path: String,       // default: "/docs"
    pub api_prefix: String,      // set automatically from set_global_prefix / enable_uri_versioning
    pub servers: Option<Vec<Value>>,
    pub document_tags: Option<Vec<Value>>,
    pub components: Option<Value>,
    pub security: Option<Vec<Value>>,
    pub infer_route_security_from_roles: bool,  // default: false
    pub roles_security_scheme: String,          // default: "bearerAuth"
}
title
String
The info.title field in the generated spec.
version
String
The info.version field. Defaults to the value of CARGO_PKG_VERSION at compile time.
json_path
String
The path where the OpenAPI JSON spec is served. Defaults to "/openapi.json".
docs_path
String
The path where Swagger UI is served. Defaults to "/docs".
servers
Option<Vec<Value>>
OpenAPI Server Objects. Pass a JSON array.
components
Option<Value>
OpenAPI Components Object for security schemes, schemas, and other reusable definitions.
security
Option<Vec<Value>>
Root-level Security Requirement Objects applied to all operations by default.
infer_route_security_from_roles
bool
When true, routes with #[roles] metadata get an operation-level security entry referencing roles_security_scheme. Requires that scheme to be declared in components.securitySchemes.

enable_openapi vs enable_openapi_with_options

// Minimal — uses all defaults
pub fn enable_openapi(self) -> Self

// Full control
pub fn enable_openapi_with_options(self, options: OpenApiOptions) -> Self
use nestrs::prelude::*;
use nestrs_openapi::OpenApiOptions;
use serde_json::json;

#[module]
struct AppModule;

#[tokio::main]
async fn main() {
    let options = OpenApiOptions {
        title: "My API".to_string(),
        version: "2.0.0".to_string(),
        servers: Some(vec![
            json!({ "url": "https://api.example.com", "description": "Production" }),
            json!({ "url": "http://localhost:3000", "description": "Local" }),
        ]),
        components: Some(json!({
            "securitySchemes": {
                "bearerAuth": {
                    "type": "http",
                    "scheme": "bearer",
                    "bearerFormat": "JWT"
                }
            }
        })),
        security: Some(vec![json!({ "bearerAuth": [] })]),
        ..OpenApiOptions::default()
    };

    NestFactory::create::<AppModule>()
        .enable_openapi_with_options(options)
        .listen(3000)
        .await;
}

#[openapi] macro

Annotate individual route handlers to override the auto-generated summary, set a tag, and declare response status codes.
#[routes(state = AppState)]
impl UsersController {
    #[get("/:id")]
    #[openapi(
        summary = "Get a user by ID",
        tag = "users",
        responses = (
            (200, "User found"),
            (404, "User not found"),
        )
    )]
    async fn find_one(
        #[param::param] params: UserIdParam,
    ) -> axum::Json<serde_json::Value> {
        axum::Json(serde_json::json!({ "id": params.id }))
    }
}
summary
&str (optional)
Human-readable summary for the operation. Defaults to a humanized version of the handler function name (e.g., find_one"Find one").
tag
&str (optional)
Groups the operation in Swagger UI. Defaults to the first meaningful path segment after an optional version segment (e.g., /v1/users/1"users").
responses
((status, description), ...) (optional)
Declares response status codes and their descriptions. Defaults to 200 OK only when not specified.

What is generated automatically

For each route registered in RouteRegistry, nestrs-openapi generates:
  • operationId — the full Rust module path + handler name (e.g., my_app::controllers::UsersController::find_one)
  • summary — humanized handler name or the #[openapi(summary = ...)] override
  • tags — inferred from the URL path or the #[openapi(tag = ...)] override
  • responses200 OK by default, or the list from #[openapi(responses = ...)]

Security scheme documentation

Add a bearerAuth scheme to components.securitySchemes and enable infer_route_security_from_roles to show the Swagger lock icon on routes protected with #[roles]:
use nestrs::prelude::*;
use nestrs_openapi::OpenApiOptions;
use serde_json::json;

#[controller(prefix = "/api")]
struct ApiController;

#[routes(state = AppState)]
impl ApiController {
    #[get("/public")]
    async fn public() -> &'static str { "no auth required" }

    #[get("/private")]
    #[roles("admin")]
    async fn private() -> &'static str { "auth required" }
}

#[module(controllers = [ApiController], providers = [AppState])]
struct AppModule;

#[tokio::main]
async fn main() {
    let options = OpenApiOptions {
        components: Some(json!({
            "securitySchemes": {
                "bearerAuth": {
                    "type": "http",
                    "scheme": "bearer",
                    "bearerFormat": "JWT"
                }
            }
        })),
        infer_route_security_from_roles: true,
        roles_security_scheme: "bearerAuth".to_string(),
        ..OpenApiOptions::default()
    };

    NestFactory::create::<AppModule>()
        .enable_openapi_with_options(options)
        .listen(3000)
        .await;
}
The /api/private operation will include security: [{ "bearerAuth": [] }] in the generated spec, rendering the lock icon in Swagger UI.

Accessing the generated spec

The spec is served as JSON at the json_path (default: /openapi.json) and is not mounted under set_global_prefix or enable_uri_versioning. You can fetch it directly:
curl http://localhost:3000/openapi.json | jq .

What nestrs-openapi does not do

nestrs-openapi does not generate JSON schemas from Rust types. Unlike @nestjs/swagger with @ApiProperty, there is no runtime type reflection. To add schemas to components.schemas, either hand-author them in OpenApiOptions::components or use a separate schema generation crate such as utoipa or okapi and merge the output.
The NestJS vs nestrs-openapi parity table:
NestJS / @nestjs/swaggernestrs-openapi
@ApiTagsInferred from URL path or #[openapi(tag = "...")]
@ApiOperationAuto summary or #[openapi(summary = "...")]
@ApiResponseDefault 200 OK or #[openapi(responses = ...)]
DTO schema generationNot built-in — hand-author components.schemas
@ApiBearerAuthcomponents.securitySchemes + infer_route_security_from_roles
Swagger UIYes — bundled at docs_path
Plugins (NestJS CLI)No