FastAPI External Documentation
Introduction
Documentation is a crucial part of any API development. FastAPI excels in this area by automatically generating interactive API documentation based on your code. In this guide, we'll explore how FastAPI handles external documentation, the built-in documentation tools it provides, and how you can customize them to fit your project needs.
FastAPI offers two built-in documentation interfaces out of the box:
- Swagger UI: Interactive documentation that allows users to test your API directly from the browser
- ReDoc: A responsive, well-organized alternative documentation view
Both of these are automatically generated from the OpenAPI schema that FastAPI creates based on your Python code and type annotations.
Built-in Documentation Systems
Swagger UI
By default, FastAPI automatically creates a Swagger UI documentation available at /docs
. This interactive documentation allows users to:
- See all available endpoints
- Test API calls directly from the browser
- View request and response models
- Understand authentication requirements
Let's look at how this works with a basic example:
from fastapi import FastAPI
app = FastAPI(
title="My Awesome API",
description="This is a sample API for demonstration purposes",
version="0.1.0"
)
@app.get("/items/", tags=["Items"])
async def read_items():
"""
Retrieve a list of items.
This endpoint returns a list of all available items.
"""
return [{"name": "Item 1"}, {"name": "Item 2"}]
When you run this application and navigate to http://127.0.0.1:8000/docs
, you'll see a nicely formatted Swagger UI interface displaying your API information, endpoints, and the ability to try out the API.
ReDoc
FastAPI also provides ReDoc, an alternative documentation UI, available at the /redoc
endpoint. ReDoc offers:
- A clean, responsive interface
- Well-organized documentation
- A different layout that some users may prefer
ReDoc uses the same OpenAPI schema as Swagger UI, so you don't need to write additional documentation for it.
Customizing API Documentation
Adding Metadata
You can customize how your API documentation appears by providing metadata to the FastAPI constructor:
from fastapi import FastAPI
app = FastAPI(
title="Product Inventory API",
description="API for managing product inventory with detailed documentation",
version="1.0.0",
terms_of_service="http://example.com/terms/",
contact={
"name": "API Support Team",
"url": "http://example.com/support",
"email": "[email protected]",
},
license_info={
"name": "Apache 2.0",
"url": "https://www.apache.org/licenses/LICENSE-2.0.html",
},
)
This metadata will be displayed in both Swagger UI and ReDoc, giving users important information about your API.
Documenting Individual Endpoints
FastAPI uses your function docstrings and parameter type hints to generate documentation. You can enhance this with additional information:
from typing import Optional
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/products/", tags=["Products"])
async def get_products(
search: Optional[str] = Query(
None,
description="Search products by name",
min_length=3,
max_length=50
),
category: Optional[str] = Query(
None,
description="Filter products by category"
),
skip: int = Query(0, description="Number of products to skip", ge=0),
limit: int = Query(10, description="Maximum number of products to return", le=100)
):
"""
Retrieve products with optional filtering.
This endpoint returns a list of products that match the given search criteria.
Use search parameter to filter by product name.
Use category parameter to filter by product category.
"""
# Mock response for demonstration
products = [
{"id": 1, "name": "Laptop", "category": "Electronics"},
{"id": 2, "name": "Headphones", "category": "Electronics"},
{"id": 3, "name": "T-shirt", "category": "Clothing"}
]
return products
In this example:
- We've added detailed parameter descriptions using the
Query
class - We've included parameter validations (min_length, max_length, ge, le)
- We've provided a comprehensive docstring explaining the endpoint
- We've organized the endpoint under the "Products" tag
Tags and Categories
Tags help organize your API documentation into logical sections:
from fastapi import FastAPI
app = FastAPI()
# Define tags metadata for better documentation
tags_metadata = [
{
"name": "Users",
"description": "Operations related to users",
},
{
"name": "Products",
"description": "Manage product inventory",
"externalDocs": {
"description": "Products external docs",
"url": "https://example.com/products/",
},
},
]
app = FastAPI(openapi_tags=tags_metadata)
@app.get("/users/", tags=["Users"])
async def get_users():
return [{"id": 1, "name": "Alice"}, {"id": 2, "name": "Bob"}]
@app.get("/products/", tags=["Products"])
async def get_products():
return [{"id": 1, "name": "Laptop"}, {"id": 2, "name": "Headphones"}]
This organizes your documentation into "Users" and "Products" sections, making it easier to navigate.
Disabling Documentation
In some cases, you might want to disable the automatic documentation in production environments:
from fastapi import FastAPI
import os
# Determine if we're in production
is_production = os.getenv("ENVIRONMENT") == "production"
app = FastAPI(
docs_url=None if is_production else "/docs",
redoc_url=None if is_production else "/redoc"
)
This code disables both Swagger UI and ReDoc in production but keeps them available in development.
Custom Documentation URLs
You can also customize the URLs where documentation is available:
from fastapi import FastAPI
app = FastAPI(
docs_url="/api-docs",
redoc_url="/api-redoc"
)
With this configuration, Swagger UI will be available at /api-docs
and ReDoc at /api-redoc
.
Including Examples in Documentation
Adding examples to your request and response models makes your documentation more useful:
from pydantic import BaseModel, Field
from fastapi import FastAPI
from typing import List
app = FastAPI()
class ProductCreate(BaseModel):
name: str = Field(..., example="Wireless Headphones")
description: str = Field(..., example="Noise-canceling wireless headphones with 20-hour battery life")
price: float = Field(..., example=99.99, gt=0)
category: str = Field(..., example="Electronics")
class Config:
schema_extra = {
"example": {
"name": "Smartphone",
"description": "Latest model with high-resolution camera",
"price": 799.99,
"category": "Electronics"
}
}
class Product(ProductCreate):
id: int = Field(..., example=1)
in_stock: bool = Field(default=True, example=True)
@app.post("/products/", response_model=Product, tags=["Products"])
async def create_product(product: ProductCreate):
"""
Create a new product.
This endpoint allows you to add a new product to the inventory.
"""
# In a real application, you would save the product to a database
return {**product.dict(), "id": 42, "in_stock": True}
@app.get("/products/", response_model=List[Product], tags=["Products"])
async def get_products():
"""Get all available products in the inventory"""
return [
{
"id": 1,
"name": "Laptop",
"description": "Powerful laptop for developers",
"price": 1299.99,
"category": "Electronics",
"in_stock": True
}
]
This example shows two ways to add examples:
- Using the
Field(..., example="value")
syntax for individual fields - Using the
schema_extra
in the Config class for a complete model example
Advanced: Custom OpenAPI Schema
For advanced customization, you can modify the OpenAPI schema directly:
from fastapi import FastAPI
from fastapi.openapi.utils import get_openapi
app = FastAPI()
@app.get("/items/")
async def read_items():
return [{"name": "Item 1"}]
def custom_openapi():
if app.openapi_schema:
return app.openapi_schema
openapi_schema = get_openapi(
title="Custom API Documentation",
version="2.5.0",
description="This is a custom OpenAPI schema",
routes=app.routes,
)
# Custom documentation customization
openapi_schema["info"]["x-logo"] = {
"url": "https://example.com/logo.png"
}
# Add a custom extension
openapi_schema["info"]["x-custom-field"] = "Custom value"
app.openapi_schema = openapi_schema
return app.openapi_schema
app.openapi = custom_openapi
This allows for deeper customization of how your API documentation is presented.
Practical Example: Building a Todo API with Comprehensive Documentation
Let's create a more complete example of a Todo API with well-documented endpoints:
from fastapi import FastAPI, Path, Query, HTTPException, status
from pydantic import BaseModel, Field
from typing import List, Optional
from enum import Enum
import uuid
# Initialize FastAPI
app = FastAPI(
title="Todo API",
description="A simple API to manage your todos with comprehensive documentation",
version="1.0.0",
contact={
"name": "Developer Team",
"email": "[email protected]",
},
)
# Define models
class PriorityLevel(str, Enum):
low = "low"
medium = "medium"
high = "high"
class TodoBase(BaseModel):
title: str = Field(...,
min_length=1,
max_length=100,
example="Complete FastAPI tutorial")
description: Optional[str] = Field(None,
example="Work through the documentation sections",
max_length=500)
priority: PriorityLevel = Field(PriorityLevel.medium,
example=PriorityLevel.medium,
description="Priority level of the task")
class TodoCreate(TodoBase):
pass
class Todo(TodoBase):
id: str = Field(..., example="550e8400-e29b-41d4-a716-446655440000")
completed: bool = Field(False, example=False)
# Mock database
todo_db = [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"title": "Learn FastAPI",
"description": "Go through FastAPI documentation",
"priority": "high",
"completed": False
},
{
"id": "550e8400-e29b-41d4-a716-446655440001",
"title": "Build API project",
"description": "Create a simple REST API",
"priority": "medium",
"completed": False
}
]
# API endpoints
@app.get("/todos/",
response_model=List[Todo],
tags=["Todos"],
summary="List all todos",
response_description="List of all todos")
async def get_todos(
skip: int = Query(0, ge=0, description="Number of todos to skip"),
limit: int = Query(10, ge=1, le=100, description="Maximum number of todos to return"),
priority: Optional[PriorityLevel] = Query(None, description="Filter by priority level")
):
"""
Retrieve all todos with optional filtering.
- **skip**: Number of todos to skip for pagination
- **limit**: Maximum number of todos to return
- **priority**: Filter by priority level (low, medium, high)
"""
results = todo_db
if priority:
results = [todo for todo in results if todo["priority"] == priority]
return results[skip : skip + limit]
@app.get("/todos/{todo_id}",
response_model=Todo,
tags=["Todos"],
summary="Get a specific todo",
response_description="The requested todo")
async def get_todo(
todo_id: str = Path(..., description="The ID of the todo to retrieve", example="550e8400-e29b-41d4-a716-446655440000")
):
"""
Retrieve a specific todo by its ID.
- **todo_id**: UUID of the todo to retrieve
Will raise a 404 error if the todo doesn't exist.
"""
for todo in todo_db:
if todo["id"] == todo_id:
return todo
raise HTTPException(status_code=404, detail="Todo not found")
@app.post("/todos/",
response_model=Todo,
status_code=status.HTTP_201_CREATED,
tags=["Todos"],
summary="Create a new todo",
response_description="The created todo")
async def create_todo(todo: TodoCreate):
"""
Create a new todo.
- **title**: Title of the todo (required)
- **description**: Detailed description of the todo (optional)
- **priority**: Priority level (low, medium, high)
Returns the created todo with a generated ID.
"""
new_todo = {
"id": str(uuid.uuid4()),
**todo.dict(),
"completed": False
}
todo_db.append(new_todo)
return new_todo
This comprehensive example demonstrates:
- Detailed field descriptions and validations
- Use of examples throughout models and parameters
- Enum types for constrained values
- Path and Query parameter validations
- Detailed docstrings with formatted descriptions
- Status code specifications
- Error handling with proper documentation
Summary
FastAPI's automatic documentation generation is one of its strongest features, providing interactive, up-to-date API documentation with minimal effort. In this guide, we've covered:
- The built-in documentation systems: Swagger UI and ReDoc
- How to customize API metadata and descriptions
- Adding examples to your models and endpoints
- Organizing endpoints with tags
- Advanced customization of the OpenAPI schema
- A comprehensive real-world example
By leveraging FastAPI's documentation capabilities, you can create APIs that are not only functional but also well-documented and easy for other developers to use.
Additional Resources
- FastAPI Official Documentation on OpenAPI
- Swagger UI Documentation
- ReDoc Documentation
- OpenAPI Specification
Exercises
-
Basic Documentation: Create a simple FastAPI application with at least three endpoints and add proper documentation including descriptions, examples, and tags.
-
Custom OpenAPI Schema: Modify the OpenAPI schema to include a custom logo and additional metadata.
-
Advanced Documentation: Create an API with nested models and document them with examples and field descriptions.
-
Production Configuration: Set up a FastAPI application that disables documentation in production but enables it in development.
-
Authentication Documentation: Add OAuth2 authentication to your API and document the security requirements in the OpenAPI schema.
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)