stonehm - Documentation-Driven OpenAPI Generation for Axum
stonehm automatically generates comprehensive OpenAPI 3.0 specifications for Axum web applications by analyzing handler functions and their documentation. The core principle is "documentation is the spec" - write clear, natural documentation and get complete OpenAPI specs automatically.
Key Features
- Generate OpenAPI 3.0 specs from rustdoc comments
- Automatic error handling from
Result<T, E>return types - Type-safe schema generation via derive macros
- Compile-time processing with zero runtime overhead
- Drop-in replacement for
axum::Router
Quick Start
Installation
Add to your Cargo.toml:
[]
= "0.1"
= "0.1"
= "0.7"
= { = "1", = ["macros", "rt-multi-thread"] }
= { = "1.0", = ["derive"] }
30-Second Example
use ;
use ;
use ;
use ;
// Define your data types
/// Get user by ID
///
/// Retrieves a user's information using their unique identifier.
/// Returns detailed user data including name and email.
async
async
That's it! You now have a fully documented API with automatic OpenAPI generation.
Automatic OpenAPI generation includes:
- Path parameter documentation
- 200 response with User schema
- 400 Bad Request with ApiError schema
- 500 Internal Server Error with ApiError schema
Documentation Approaches
stonehm supports three documentation approaches to fit different needs:
1. Automatic Documentation (Recommended)
Let stonehm infer everything from your code structure:
/// Get user profile
///
/// Retrieves the current user's profile information.
async
Automatically generates:
- 200 response with User schema
- 400 Bad Request with ApiError schema
- 500 Internal Server Error with ApiError schema
### 2. Structured Documentation
Add detailed parameter and response documentation:
```rust
/// Update user profile
///
/// Updates the user's profile information. Only provided fields
/// will be updated, others remain unchanged.
///
/// # Parameters
/// - id (path): The user's unique identifier
/// - version (query): API version to use
/// - authorization (header): Bearer token for authentication
///
/// # Request Body
/// Content-Type: application/json
/// User update data with optional fields for name, email, and preferences.
///
/// # Responses
/// - 200: User successfully updated
/// - 400: Invalid user data provided
/// - 401: Authentication required
/// - 404: User not found
/// - 422: Validation failed
#[api_handler]
async fn update_profile(
Path(id): Path<u32>,
Json(request): Json<UpdateUserRequest>
) -> Result<Json<User>, ApiError> {
Ok(Json(User::default()))
}
3. Elaborate Documentation
For complex APIs requiring detailed error schemas:
/// Delete user account
///
/// Permanently removes a user account and all associated data.
/// This action cannot be undone.
///
/// # Parameters
/// - id (path): The unique user identifier to delete
///
/// # Responses
/// - 204: User successfully deleted
/// - 404:
/// description: User not found
/// content:
/// application/json:
/// schema: NotFoundError
/// - 403:
/// description: Insufficient permissions to delete user
/// content:
/// application/json:
/// schema: PermissionError
/// - 409:
/// description: Cannot delete user with active subscriptions
/// content:
/// application/json:
/// schema: ConflictError
async
Schema Generation
stonehm uses the StonehmSchema derive macro for automatic schema generation:
use ;
use StonehmSchema;
Supported types: All primitive types, Option<T>, Vec<T>, nested structs, and enums.
Router Setup
Basic Setup
use api_router;
async
Custom OpenAPI Endpoints
// Default endpoints
.with_openapi_routes // Creates /openapi.json and /openapi.yaml
// Custom prefix
.with_openapi_routes_prefix // Creates /api/docs.json and /api/docs.yaml
// Custom paths
.with_openapi_routes_prefix // Creates /v1/spec.json and /v1/spec.yaml
Documentation Format Reference
Summary and Description
/// Brief one-line summary
///
/// Detailed description that can span multiple paragraphs.
/// This becomes the OpenAPI description field.
Parameters Section
/// # Parameters
/// - id (path): The unique user identifier
/// - page (query): Page number for pagination
/// - limit (query): Maximum results per page
/// - authorization (header): Bearer token for authentication
Request Body Section
/// # Request Body
/// Content-Type: application/json
/// Detailed description of the expected request body structure
/// and any validation requirements.
Response Documentation
Simple format (covers most use cases):
/// # Responses
/// - 200: User successfully created
/// - 400: Invalid user data provided
/// - 409: Email address already exists
Elaborate format (for detailed error documentation):
/// # Responses
/// - 201: User successfully created
/// - 400:
/// description: Validation failed
/// content:
/// application/json:
/// schema: ValidationError
/// - 409:
/// description: Email already exists
/// content:
/// application/json:
/// schema: ConflictError
Best Practices
1. Use Result Types for Error Handling
Return Result<Json<T>, E> to get automatic error responses:
/// Recommended - Automatic error handling
async
/// Manual - Requires explicit response documentation
async
Generated OpenAPI for automatic error handling:
responses:
'200':
description: Success
content:
application/json:
schema:
$ref: '#/components/schemas/User'
'400':
description: Bad Request
content:
application/json:
schema:
$ref: '#/components/schemas/ApiError'
'500':
description: Internal Server Error
content:
application/json:
schema:
$ref: '#/components/schemas/ApiError'
Manual documentation requires explicit responses:
responses:
'200':
description: Success
content:
application/json:
schema:
$ref: '#/components/schemas/User'
2. Use api_error Macro for Error Types
use api_error;
The api_error macro automatically generates IntoResponse, Serialize, and StonehmSchema implementations, eliminating all boilerplate.
3. Keep Documentation Natural
Focus on business logic, not OpenAPI details:
/// Good - describes what the endpoint does
/// Creates a new user account with email verification
/// Avoid - implementation details
/// Returns HTTP 201 with application/json content-type
4. Choose the Right Documentation Level
/// Simple for basic APIs
/// # Responses
/// - 200: Success
/// - 400: Bad request
/// Elaborate for complex error handling
/// # Responses
/// - 400:
/// description: Validation failed
/// content:
/// application/json:
/// schema: ValidationError
Automatic vs Manual Response Documentation
| Return Type | Automatic Behavior | When to Use Manual |
|---|---|---|
Json<T> |
200 response with T schema | Simple endpoints |
Result<Json<T>, E> |
200 with T schema400, 500 with E schema | Most endpoints (recommended) |
() or StatusCode |
200 empty response | DELETE operations |
| Custom types | Depends on implementation | Advanced use cases |
Common Troubleshooting
Q: My error responses aren't appearing
A: Ensure your function returns Result<Json<T>, E> and E implements IntoResponse.
Q: Schemas aren't in the OpenAPI spec
A: Add #[derive(StonehmSchema)] to your types and use them in function signatures.
Q: Path parameters not documented
A: Add them to the # Parameters section with (path) type specification.
Q: Custom response schemas not working
A: Use the elaborate response format with explicit schema references.
API Reference
Macros
| Macro | Purpose | Example |
|---|---|---|
api_router!(title, version) |
Create documented router | api_router!("My API", "1.0.0") |
#[api_handler] |
Mark handler for documentation | #[api_handler] async fn get_user() {} |
#[derive(StonehmSchema)] |
Generate JSON schema | #[derive(Serialize, StonehmSchema)] struct User {} |
Router Methods
let app = api_router!
.get // GET route
.post // POST route
.put // PUT route
.delete // DELETE route
.patch // PATCH route
.with_openapi_routes // Add OpenAPI endpoints
.into_router; // Convert to axum::Router
OpenAPI Endpoints
| Method | Creates | Description |
|---|---|---|
.with_openapi_routes() |
/openapi.json/openapi.yaml |
Default OpenAPI endpoints |
.with_openapi_routes_prefix("/api") |
/api.json/api.yaml |
Custom prefix |
Response Type Mapping
| Rust Type | OpenAPI Response | Automatic Errors |
|---|---|---|
Json<T> |
200 with T schema | None |
Result<Json<T>, E> |
200 with T schema | 400, 500 with E schema |
() |
204 No Content | None |
StatusCode |
Custom status | None |
Examples
Full REST API Example
use ;
use ;
use ;
use StonehmSchema;
/// List users with pagination
///
/// Retrieves a paginated list of users from the database.
///
/// # Parameters
/// - page (query): Page number (default: 1)
/// - limit (query): Users per page (default: 10, max: 100)
async
/// Get user by ID
///
/// Retrieves detailed user information by ID.
async
/// Create new user
///
/// Creates a new user account with the provided information.
///
/// # Request Body
/// Content-Type: application/json
/// User creation data with required name and email fields.
async
async
Development
Running Examples
# Clone the repository
# Run the example server
# Test OpenAPI generation
# Use default endpoints (/openapi.json, /openapi.yaml)
Testing Schema Generation
# Generate and view the OpenAPI spec
|
# Check specific endpoints
|
# View all schemas
|
Contributing
We welcome contributions! Please feel free to submit issues and pull requests.
Development Setup
# Run tests
# Check formatting
# Run clippy
# Test all examples
License
This project is licensed under the MIT License - see the LICENSE file for details.