OpenAPI & Swagger UI Guide
Generate OpenAPI 3.0 specifications and serve interactive API documentation with Swagger UI.
Table of Contents
- Overview
- Features
- Quick Start
- Programmatic Builder
- Swagger UI Integration
- Schema Definition
- Security
- Best Practices
- Examples
Overview
The armature-openapi module provides tools for generating OpenAPI 3.0 specifications and serving interactive API documentation. It allows you to document your APIs programmatically and serve beautiful, interactive documentation via Swagger UI.
Why OpenAPI?
- Industry Standard: OpenAPI (formerly Swagger) is the de facto standard for REST API documentation
- Interactive: Test your API directly from the documentation
- Code Generation: Generate client SDKs in multiple languages
- Validation: Validate requests/responses against the specification
- Discoverability: Make your API easy to understand and use
Features
โ Programmatic Builder
- Fluent API for building OpenAPI specs
- Type-safe specification construction
- Helper functions for common patterns
โ Swagger UI Integration
- Beautiful, interactive API documentation
- Test endpoints directly from the browser
- No configuration required
โ Multiple Export Formats
- JSON export
- YAML export
- HTML (Swagger UI)
โ Full OpenAPI 3.0 Support
- All HTTP methods (GET, POST, PUT, DELETE, PATCH)
- Request/response schemas
- Authentication (Bearer, API Key, OAuth2)
- Tags and organization
Quick Start
Installation
Add to your Cargo.toml:
[dependencies]
armature-framework = { version = "0.1", features = ["openapi"] }
Basic Example
use armature_framework::prelude::*;
use armature_framework::armature_openapi::*;
// Build OpenAPI specification
let spec = OpenApiBuilder::new("My API", "1.0.0")
.description("A wonderful API")
.server("http://localhost:3000", None)
.tag("users", Some("User endpoints".to_string()))
.build();
// Create Swagger UI config
let config = SwaggerConfig::new("/api-docs", spec);
// Serve Swagger UI
#[get("/api-docs")]
async fn swagger_ui(config: SwaggerConfig) -> Result<HttpResponse, Error> {
swagger_ui_response(&config)
}
Programmatic Builder
Creating a Specification
use armature_openapi::*;
let spec = OpenApiBuilder::new("User API", "1.0.0")
// Basic info
.description("A comprehensive user management API")
.terms_of_service("https://example.com/terms")
// Contact
.contact(
Some("API Support".to_string()),
Some("https://example.com/support".to_string()),
Some("support@example.com".to_string()),
)
// License
.license("MIT", Some("https://opensource.org/licenses/MIT".to_string()))
// Servers
.server("https://api.example.com", Some("Production".to_string()))
.server("http://localhost:3000", Some("Development".to_string()))
// Tags
.tag("users", Some("User management".to_string()))
.tag("auth", Some("Authentication".to_string()))
.build();
Adding Endpoints
let spec = OpenApiBuilder::new("API", "1.0.0")
.path(
"/users",
PathItemBuilder::new()
.get(
OperationBuilder::new()
.summary("List all users")
.description("Returns a paginated list of users")
.tag("users")
.operation_id("listUsers")
.parameter(Parameter {
name: "page".to_string(),
location: ParameterLocation::Query,
description: Some("Page number".to_string()),
required: Some(false),
schema: Some(integer_schema()),
})
.response(
"200",
Response {
description: "Successful response".to_string(),
content: Some({
let mut content = HashMap::new();
content.insert(
"application/json".to_string(),
MediaType {
schema: Some(array_schema(ref_schema("User"))),
},
);
content
}),
},
)
.build(),
)
.post(
OperationBuilder::new()
.summary("Create a user")
.tag("users")
.operation_id("createUser")
.request_body(RequestBody {
description: Some("User to create".to_string()),
content: {
let mut content = HashMap::new();
content.insert(
"application/json".to_string(),
MediaType {
schema: Some(ref_schema("User")),
},
);
content
},
required: Some(true),
})
.response(
"201",
Response {
description: "User created".to_string(),
content: Some({
let mut content = HashMap::new();
content.insert(
"application/json".to_string(),
MediaType {
schema: Some(ref_schema("User")),
},
);
content
}),
},
)
.build(),
)
.build(),
)
.build();
Path Parameters
.path(
"/users/{id}",
PathItemBuilder::new()
.get(
OperationBuilder::new()
.summary("Get user by ID")
.tag("users")
.parameter(Parameter {
name: "id".to_string(),
location: ParameterLocation::Path,
description: Some("User ID".to_string()),
required: Some(true),
schema: Some(integer_schema()),
})
.response(
"200",
Response {
description: "User found".to_string(),
content: Some({
let mut content = HashMap::new();
content.insert(
"application/json".to_string(),
MediaType {
schema: Some(ref_schema("User")),
},
);
content
}),
},
)
.response(
"404",
Response {
description: "User not found".to_string(),
content: None,
},
)
.build(),
)
.build(),
)
Schema Definition
Primitive Types
use armature_openapi::*;
// String
let name_schema = string_schema();
// Integer
let id_schema = integer_schema();
// Number (floating point)
let price_schema = number_schema();
// Boolean
let active_schema = boolean_schema();
Arrays
// Array of strings
let tags_schema = array_schema(string_schema());
// Array of objects
let users_schema = array_schema(ref_schema("User"));
Objects
let user_schema = object_schema(
{
let mut props = HashMap::new();
props.insert("id".to_string(), integer_schema());
props.insert("name".to_string(), string_schema());
props.insert("email".to_string(), string_schema());
props.insert("age".to_string(), integer_schema());
props.insert("active".to_string(), boolean_schema());
props
},
vec![
"id".to_string(),
"name".to_string(),
"email".to_string(),
],
);
Nested Objects
let address_schema = object_schema(
{
let mut props = HashMap::new();
props.insert("street".to_string(), string_schema());
props.insert("city".to_string(), string_schema());
props.insert("country".to_string(), string_schema());
props
},
vec!["street".to_string(), "city".to_string()],
);
let user_with_address = object_schema(
{
let mut props = HashMap::new();
props.insert("id".to_string(), integer_schema());
props.insert("name".to_string(), string_schema());
props.insert("address".to_string(), address_schema);
props
},
vec!["id".to_string(), "name".to_string()],
);
Reusable Schemas
let spec = OpenApiBuilder::new("API", "1.0.0")
// Define schema once
.schema("User", object_schema(
{
let mut props = HashMap::new();
props.insert("id".to_string(), integer_schema());
props.insert("name".to_string(), string_schema());
props.insert("email".to_string(), string_schema());
props
},
vec!["id".to_string(), "name".to_string(), "email".to_string()],
))
.schema("Address", object_schema(
{
let mut props = HashMap::new();
props.insert("street".to_string(), string_schema());
props.insert("city".to_string(), string_schema());
props
},
vec!["street".to_string(), "city".to_string()],
))
// Reference schemas in endpoints
.path(
"/users",
PathItemBuilder::new()
.get(
OperationBuilder::new()
.summary("List users")
.response(
"200",
Response {
description: "Success".to_string(),
content: Some({
let mut content = HashMap::new();
content.insert(
"application/json".to_string(),
MediaType {
schema: Some(array_schema(ref_schema("User"))),
},
);
content
}),
},
)
.build(),
)
.build(),
)
.build();
Swagger UI Integration
Basic Setup
use armature_framework::prelude::*;
use armature_framework::armature_openapi::*;
#[controller("/api-docs")]
struct ApiDocsController {
config: SwaggerConfig,
}
impl ApiDocsController {
#[get("/")]
async fn swagger_ui(&self) -> Result<HttpResponse, Error> {
swagger_ui_response(&self.config)
}
#[get("/openapi.json")]
async fn openapi_json(&self) -> Result<HttpResponse, Error> {
spec_json_response(&self.config)
}
#[get("/openapi.yaml")]
async fn openapi_yaml(&self) -> Result<HttpResponse, Error> {
spec_yaml_response(&self.config)
}
}
Custom Title
let config = SwaggerConfig::new("/api-docs", spec)
.with_title("My Amazing API Documentation");
Multiple Versions
// Version 1
let spec_v1 = OpenApiBuilder::new("My API", "1.0.0")
.server("https://api.example.com/v1", None)
.build();
let config_v1 = SwaggerConfig::new("/api-docs/v1", spec_v1);
// Version 2
let spec_v2 = OpenApiBuilder::new("My API", "2.0.0")
.server("https://api.example.com/v2", None)
.build();
let config_v2 = SwaggerConfig::new("/api-docs/v2", spec_v2);
Security
Bearer Authentication (JWT)
let spec = OpenApiBuilder::new("API", "1.0.0")
// Add security scheme
.add_bearer_auth("bearer")
// Apply to specific endpoint
.path(
"/users",
PathItemBuilder::new()
.get(
OperationBuilder::new()
.summary("List users")
.security({
let mut req = HashMap::new();
req.insert("bearer".to_string(), vec![]);
req
})
.response("200", Response {
description: "Success".to_string(),
content: None,
})
.build(),
)
.build(),
)
.build();
API Key Authentication
let spec = OpenApiBuilder::new("API", "1.0.0")
.add_api_key_auth(
"api_key",
"X-API-Key",
ApiKeyLocation::Header,
)
.path(
"/users",
PathItemBuilder::new()
.get(
OperationBuilder::new()
.summary("List users")
.security({
let mut req = HashMap::new();
req.insert("api_key".to_string(), vec![]);
req
})
.response("200", Response {
description: "Success".to_string(),
content: None,
})
.build(),
)
.build(),
)
.build();
OAuth2
let spec = OpenApiBuilder::new("API", "1.0.0")
.security_scheme(
"oauth2",
SecurityScheme::OAuth2 {
flows: OAuthFlows {
authorization_code: Some(OAuthFlow {
authorization_url: Some("https://example.com/oauth/authorize".to_string()),
token_url: Some("https://example.com/oauth/token".to_string()),
refresh_url: None,
scopes: {
let mut scopes = HashMap::new();
scopes.insert("read".to_string(), "Read access".to_string());
scopes.insert("write".to_string(), "Write access".to_string());
scopes
},
}),
..Default::default()
},
},
)
.build();
Global Security
// Apply authentication to all endpoints by default
let spec = OpenApiBuilder::new("API", "1.0.0")
.add_bearer_auth("bearer")
.security({
let mut req = HashMap::new();
req.insert("bearer".to_string(), vec![]);
req
})
.build();
Best Practices
1. Use Descriptive Names
// โ
Good
.operation_id("getUserById")
.summary("Get a user by their unique identifier")
// โ Bad
.operation_id("get1")
.summary("Get")
2. Provide Examples
// Add examples to schemas
let user_schema = object_schema(
{
let mut props = HashMap::new();
props.insert("id".to_string(), integer_schema());
props.insert("name".to_string(), string_schema());
props
},
vec!["id".to_string(), "name".to_string()],
);
3. Document Error Responses
.response("200", Response {
description: "Successful response".to_string(),
content: Some(/* ... */),
})
.response("400", Response {
description: "Invalid request parameters".to_string(),
content: None,
})
.response("401", Response {
description: "Authentication required".to_string(),
content: None,
})
.response("403", Response {
description: "Insufficient permissions".to_string(),
content: None,
})
.response("404", Response {
description: "Resource not found".to_string(),
content: None,
})
.response("500", Response {
description: "Internal server error".to_string(),
content: None,
})
4. Use Tags for Organization
let spec = OpenApiBuilder::new("E-commerce API", "1.0.0")
.tag("products", Some("Product catalog".to_string()))
.tag("orders", Some("Order management".to_string()))
.tag("users", Some("User accounts".to_string()))
.tag("auth", Some("Authentication".to_string()))
.build();
5. Version Your API
// Include version in URL
.server("https://api.example.com/v1", Some("Version 1".to_string()))
.server("https://api.example.com/v2", Some("Version 2".to_string()))
6. Keep Schemas DRY
// Define common schemas once
let spec = OpenApiBuilder::new("API", "1.0.0")
.schema("Error", object_schema(/* ... */))
.schema("User", object_schema(/* ... */))
.schema("Product", object_schema(/* ... */))
// Then reference them
.path("/users", /* use ref_schema("User") */)
.build();
Examples
Complete REST API
use armature_openapi::*;
let spec = OpenApiBuilder::new("Task Manager API", "1.0.0")
.description("A simple task management API")
.server("http://localhost:3000", Some("Development".to_string()))
// Tags
.tag("tasks", Some("Task operations".to_string()))
.tag("users", Some("User operations".to_string()))
// Auth
.add_bearer_auth("bearer")
// Schemas
.schema("Task", object_schema(
{
let mut props = HashMap::new();
props.insert("id".to_string(), integer_schema());
props.insert("title".to_string(), string_schema());
props.insert("completed".to_string(), boolean_schema());
props
},
vec!["id".to_string(), "title".to_string()],
))
// Endpoints
.path(
"/tasks",
PathItemBuilder::new()
.get(
OperationBuilder::new()
.summary("List tasks")
.tag("tasks")
.security({
let mut req = HashMap::new();
req.insert("bearer".to_string(), vec![]);
req
})
.response("200", Response {
description: "Success".to_string(),
content: Some({
let mut content = HashMap::new();
content.insert(
"application/json".to_string(),
MediaType {
schema: Some(array_schema(ref_schema("Task"))),
},
);
content
}),
})
.build(),
)
.post(
OperationBuilder::new()
.summary("Create task")
.tag("tasks")
.security({
let mut req = HashMap::new();
req.insert("bearer".to_string(), vec![]);
req
})
.request_body(RequestBody {
description: Some("Task to create".to_string()),
content: {
let mut content = HashMap::new();
content.insert(
"application/json".to_string(),
MediaType {
schema: Some(ref_schema("Task")),
},
);
content
},
required: Some(true),
})
.response("201", Response {
description: "Created".to_string(),
content: Some({
let mut content = HashMap::new();
content.insert(
"application/json".to_string(),
MediaType {
schema: Some(ref_schema("Task")),
},
);
content
}),
})
.build(),
)
.build(),
)
.build();
Summary
Key Features:
- โ Programmatic OpenAPI 3.0 spec generation
- โ Interactive Swagger UI documentation
- โ JSON/YAML export
- โ Full security scheme support
- โ Type-safe builders
When to Use:
- Documenting REST APIs
- Generating client SDKs
- API contract validation
- Developer onboarding
- API discovery
Next Steps:
- Define your API schemas
- Document each endpoint
- Add security schemes
- Serve Swagger UI
- Export OpenAPI spec
Happy documenting! ๐โจ