API Documentation and OpenAPI Customization — Creating Professional API Documentation
API documentation is like a product manual—without it, customers (developers) won't use your product; if it's poorly written, customers will vote with their feet. Automated documentation generation is FastAPI's secret weapon, but the default configuration is just the starting point; customization is what sets the pros apart.
1. What You'll Learn
- Swagger UI and ReDoc Configuration:
swagger_ui_parameters, Custom JS/CSS - OpenAPI Schema Customization:
openapi_route, Customgenerate_unique_id_function description/summary/tags/deprecatedBest Practices- Sample values and response templates:
OpenApiResponse+json_schema_extra - Alice Scenario: PriceTracker's Professional SaaS API Documentation
2. Alice's True Story
(1) Pain Point: The default documentation isn't professional enough
Alice's PriceTracker API documentation uses FastAPI's default Swagger UI, where all endpoints are mixed together without being grouped, so Bob can't find the endpoint he needs. To make matters worse, the documentation lacks sample requests, so Bob doesn't know what value to enter for the currency field; there's no versioning for request or response bodies; and new and deprecated endpoints are all mixed together.
(2) Custom Solutions for OpenAPI
FastAPI allows for deep customization of OpenAPI documentation—including tag grouping, example values, deprecation markers, and custom schemas—transforming API documentation from merely "readable" to "usable."
(3) Revenue
Bob can simply open the documentation to quickly locate endpoints by tag (Products/Prices/Auth). Sample values eliminate the need to guess when making requests, and deprecated endpoints are marked in gray to prevent accidental use. As a result, the quality of the documentation has been upgraded from "internal reference" to "public release" level.
3. Swagger UI and ReDoc Configuration
(1) OpenAPI Generation Process
flowchart LR
A[FastAPI Routes] --> B[Pydantic Models]
B --> C[OpenAPI 3.1 Schema]
C --> D[Swagger UI /docs]
C --> E[ReDoc /redoc]
C --> F[Raw JSON /openapi.json]
(1) ▶ Example: FastAPI Application-Level Documentation Configuration
from fastapi import FastAPI
app = FastAPI(
title="PriceTracker API",
description="""
## PriceTracker SaaS API
E-commerce price tracking service for monitoring million-level product prices.
### (1) Authentication
All protected endpoints require a Bearer token obtained from `/auth/login`.
### (2) Rate Limits
- Free: 100 requests/min
- Pro: 1000 requests/min
- Enterprise: unlimited
""",
version="1.0.0",
terms_of_service="https://pricetracker.example.com/terms",
contact={
"name": "PriceTracker Support",
"url": "https://pricetracker.example.com/support",
"email": "support@pricetracker.example.com",
},
license_info={
"name": "MIT License",
"url": "https://opensource.org/licenses/MIT",
},
docs_url="/docs",
redoc_url="/redoc",
openapi_url="/openapi.json",
)
Output:
# Execution Successful
(3) ▶ Example: Customizing Swagger UI Parameters
app = FastAPI(
swagger_ui_parameters={
"persistAuthorization": True, # Keep auth between page refreshes
"displayRequestDuration": True, # Show request duration
"filter": True, # Enable search filter
"syntaxHighlight.theme": "monokai", # Code highlighting theme
"defaultModelsExpandDepth": 1, # Expand models 1 level deep
"defaultModelExpandDepth": 1,
}
)
Output:
# Execution Successful
| Parameter | Default | Description |
|---|---|---|
persistAuthorization |
False | Refresh page while retaining authentication |
displayRequestDuration |
False | Show request duration |
filter |
False | Enable Search Filter |
defaultModelsExpandDepth |
1 | Model Unfolding Depth |
syntaxHighlight.theme |
"agate" | Code Highlighting Theme |
4. Tag Grouping and Deprecation Markers
(1) Tags: Grouped Endpoints
(1) ▶ Example: Defining Tags
from fastapi import FastAPI, APIRouter
tags_metadata = [
{
"name": "auth",
"description": "Authentication operations. Login, register, token refresh.",
},
{
"name": "products",
"description": "Product management. CRUD operations for tracked products.",
},
{
"name": "prices",
"description": "Price data. Query, import, and track price changes.",
},
{
"name": "admin",
"description": "Admin operations. Requires admin role.",
},
]
app = FastAPI(
title="PriceTracker API",
openapi_tags=tags_metadata,
)
Output:
# Execution Successful
(2) ▶ Example: Endpoint Assignment Tags
@app.post("/auth/login", tags=["auth"])
async def login(form_data: OAuth2PasswordRequestForm = Depends()):
...
@app.get("/api/v1/products", tags=["products"])
async def list_products():
...
@app.post("/api/v1/prices", tags=["prices"])
async def create_price():
...
@app.get("/api/v1/admin/stats", tags=["admin"])
async def admin_stats():
...
Output:
# Function defined successfully
(3) ▶ Example: Marking an endpoint as deprecated
@app.get(
"/api/v1/products/{product_id}/history",
tags=["prices"],
deprecated=True,
summary="[DEPRECATED] Use GET /prices?product_id={id} instead",
)
async def get_price_history_deprecated(product_id: int):
"""This endpoint is deprecated. Use the prices endpoint with product_id filter."""
...
Output:
# Function defined successfully
5. Sample Values and Response Templates
(1) json_schema_extra and examples
(1) ▶ Example: Sample values for Pydantic models
from pydantic import BaseModel, Field
class ProductCreate(BaseModel):
name: str = Field(
min_length=1, max_length=200,
description="Product display name",
examples=["Wireless Mouse", "USB-C Hub", "Mechanical Keyboard"],
)
category: str = Field(
max_length=100,
description="Product category",
examples=["electronics", "clothing", "food"],
)
base_price: float = Field(
gt=0,
description="Base price in USD",
examples=[9.99, 49.99, 199.99],
)
model_config = {
"json_schema_extra": {
"examples": [
{
"name": "Wireless Mouse",
"category": "electronics",
"base_price": 29.99,
}
]
}
}
Output:
# Execution Successful
(2) ▶ Example: Example of an endpoint-level response
from fastapi import FastAPI
from fastapi.responses import JSONResponse
@app.post(
"/api/v1/products",
response_model=ProductResponse,
status_code=201,
summary="Create a new product",
description="Create a new product in the PriceTracker database.",
responses={
201: {
"description": "Product created successfully",
"content": {
"application/json": {
"example": {
"id": 1,
"name": "Wireless Mouse",
"category": "electronics",
"base_price": 29.99,
}
}
},
},
401: {"description": "Authentication required"},
403: {"description": "Insufficient permissions"},
422: {"description": "Validation error"},
},
)
async def create_product(product: ProductCreate):
...
Output:
# Function defined successfully
6. Custom OpenAPI Schema
(1) generate_unique_id_function
(1) ▶ Example: Generating a Custom Endpoint ID
from fastapi import FastAPI
def custom_generate_unique_id(route):
# Format: {method}_{tag}_{path}
tags = route.tags or ["default"]
tag = tags[0]
method = route.methods.pop() if route.methods else "GET"
path = route.path.replace("/", "_").strip("_").replace("{", "").replace("}", "")
return f"{method}_{tag}_{path}"
app = FastAPI(
generate_unique_id_function=custom_generate_unique_id,
)
Output:
# Function defined successfully
(2) Comparison of Documentation Best Practices
| Dimension | Default | Best Practice |
|---|---|---|
| Endpoint Groups | None | Tags by Function |
| Abandoned endpoints | Mixed in with normal endpoints | deprecated=True Gray mark |
| Request Example | None | examples + json_schema_extra |
| Response Templates | 200 Only | Full Coverage for 201/401/403/422 |
| Description | Simple Function Name | Summary + Description |
| Certification Information | None | Documentation Home Page: Explanation of Certification Methods |
| Versioning | Single Version | URL Path Version /api/v1/ |
❓ FAQ
include_in_schema=False:@app.get("/internal", include_in_schema=False). This is suitable for internal health checks and monitoring endpoints.openapi function.swagger_ui_parameters to provide a CSS URL, or use get_swagger_ui_html to fully customize the HTML.APIRouter(prefix="/api/v1") for each version and distinguish between versions using tags. Alternatively, create a separate FastAPI sub-application for each version.📖 Summary
- FastAPI automatically generates OpenAPI 3.1 documentation; Swagger UI provides interactive testing; ReDoc offers a visually appealing reading experience
openapi_tagsGroup endpoints by function;deprecated=TrueMark deprecated endpointsField(examples=[...])andjson_schema_extraadd sample values to the modelresponses={status: {description, content}}Define multi-status code response templates for endpoints- Customize
generate_unique_id_functionto control the naming format for OpenAPI operation IDs
📝 Exercises
- Basic Question (Difficulty ⭐): Configure the FastAPI application-level documentation for PriceTracker—including the title, description, version number, and contact information—and add
persistAuthorization=Trueto ensure Swagger UI retains the authentication status. Hint:FastAPI(title=..., swagger_ui_parameters={...}) - Advanced Exercise (Difficulty ⭐⭐): Define 4 tags (auth/products/prices/admin), assign the correct tag to each endpoint, mark the deprecated endpoint
deprecated=True, and verify that the endpoints are grouped by tag in Swagger UI. Hint:openapi_tags=[...]+tags=["products"] - Challenge (Difficulty: ⭐⭐⭐): Add a complete example of
json_schema_extrato ProductCreate, and add response templates for status codes 201, 401, 403, and 422 (including sample content) to the POST /products endpoint to implement the customgenerate_unique_id_function. Hint:responses={201: {"content": {...}}}+generate_unique_id_function
---|



