Skip to main content

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:

python
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:

python
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:

python
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:

python
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:

python
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:

python
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:

python
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:

  1. Using the Field(..., example="value") syntax for individual fields
  2. 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:

python
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:

python
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:

  1. Detailed field descriptions and validations
  2. Use of examples throughout models and parameters
  3. Enum types for constrained values
  4. Path and Query parameter validations
  5. Detailed docstrings with formatted descriptions
  6. Status code specifications
  7. 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

Exercises

  1. Basic Documentation: Create a simple FastAPI application with at least three endpoints and add proper documentation including descriptions, examples, and tags.

  2. Custom OpenAPI Schema: Modify the OpenAPI schema to include a custom logo and additional metadata.

  3. Advanced Documentation: Create an API with nested models and document them with examples and field descriptions.

  4. Production Configuration: Set up a FastAPI application that disables documentation in production but enables it in development.

  5. 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! :)