Skip to main content

FastAPI Interactive Documentation

Introduction

One of FastAPI's most powerful features is its automatic interactive API documentation. When you build an API with FastAPI, you get not just one, but two different interactive documentation interfaces for free: Swagger UI and ReDoc. These tools allow developers to visualize and interact with your API's endpoints without having to write any additional code or use external tools like Postman.

In this tutorial, we'll explore how FastAPI generates this documentation, how to access it, customize it, and use it effectively during development.

Understanding FastAPI's Documentation Systems

FastAPI provides two documentation interfaces out of the box:

  1. Swagger UI - A rich interactive interface where you can explore, test and understand your API
  2. ReDoc - A responsive, well-organized alternative documentation interface

Both of these interfaces are automatically generated from your API's OpenAPI schema (formerly known as Swagger), which FastAPI also generates automatically based on your code.

Accessing the Documentation

Let's start with a basic FastAPI application and see how to access the documentation:

python
from fastapi import FastAPI

app = FastAPI()

@app.get("/")
def read_root():
return {"Hello": "World"}

@app.get("/items/{item_id}")
def read_item(item_id: int, q: str = None):
return {"item_id": item_id, "q": q}

After running this application with Uvicorn:

bash
uvicorn main:app --reload

You can access the documentation at:

  • Swagger UI: http://127.0.0.1:8000/docs
  • ReDoc: http://127.0.0.1:8000/redoc

Exploring Swagger UI

When you navigate to the /docs endpoint, you'll see the Swagger UI interface that looks like this:

Swagger UI Interface

Key features of Swagger UI include:

1. API Overview

At the top, you'll see general information about your API, including its title, description, and version.

2. Endpoints Grouped by Tags

All your endpoints are listed, organized by tags (which we'll learn to customize later).

3. Interactive Testing

For each endpoint, you can:

  • Click on it to expand details
  • Click the "Try it out" button
  • Fill in parameters
  • Execute the request
  • See the response directly in the browser

Let's test our /items/{item_id} endpoint:

  1. Click on the endpoint to expand it
  2. Click "Try it out" button
  3. Enter a value for item_id (e.g., 42)
  4. Optionally enter a value for query parameter q (e.g., test-query)
  5. Click "Execute"

You'll see:

  • The curl command that was generated
  • The request URL
  • The server response with status code
  • The response body showing: {"item_id": 42, "q": "test-query"}

This interactive testing capability is invaluable during development and for helping other developers understand how to use your API.

Working with ReDoc

ReDoc offers a more streamlined, documentation-focused interface. Navigate to /redoc to see it:

ReDoc Interface

ReDoc features:

  • Clean, three-panel layout
  • Automatic request/response examples
  • Search functionality
  • Mobile-friendly design

While ReDoc doesn't offer the interactive testing capabilities of Swagger UI, it provides excellent documentation that can be more approachable for non-technical stakeholders.

Enhancing Your Documentation

Now let's explore how to improve your API documentation through various FastAPI features.

Adding Operation Descriptions

You can add descriptions to your endpoints using docstrings:

python
@app.get("/items/{item_id}")
def read_item(item_id: int, q: str = None):
"""
Fetch an item by its ID.

- **item_id**: The unique identifier for the item
- **q**: Optional query string for filtering results
"""
return {"item_id": item_id, "q": q}

These descriptions will appear in both Swagger UI and ReDoc.

Using Tags for Organization

As your API grows, organizing endpoints becomes important. Tags help group related endpoints:

python
@app.get("/users/", tags=["users"])
def get_users():
return [{"name": "Jane"}, {"name": "John"}]

@app.post("/users/", tags=["users"])
def create_user(name: str):
return {"name": name}

@app.get("/items/", tags=["items"])
def get_items():
return [{"name": "Item 1"}, {"name": "Item 2"}]

Now in the documentation, you'll see endpoints grouped under "users" and "items" categories.

Adding Example Responses

You can provide example responses in your documentation:

python
from fastapi import FastAPI
from pydantic import BaseModel, Field
from typing import List

app = FastAPI()

class Item(BaseModel):
id: int
name: str
description: str = None
price: float
tax: float = None

class Config:
schema_extra = {
"example": {
"id": 1,
"name": "Smartphone",
"description": "Latest model with high-res camera",
"price": 799.99,
"tax": 79.99
}
}

@app.post("/items/")
def create_item(item: Item):
return item

The schema_extra with its example provides sample data in the documentation.

Customizing API Metadata

You can customize the title, description, version, and other metadata for your API:

python
app = FastAPI(
title="My Super API",
description="This is a very fancy API that does awesome stuff",
version="0.1.0",
terms_of_service="http://example.com/terms/",
contact={
"name": "API Support",
"url": "http://example.com/support",
"email": "[email protected]",
},
license_info={
"name": "MIT License",
"url": "https://opensource.org/licenses/MIT",
},
)

This information appears at the top of your documentation pages.

Real-World Example: Building a Todo API with Detailed Documentation

Let's create a more complete example of a Todo API with well-documented endpoints:

python
from fastapi import FastAPI, HTTPException, Query, Path, Body
from pydantic import BaseModel, Field
from typing import List, Optional
from enum import Enum

app = FastAPI(
title="Todo API",
description="A simple API for managing your todos",
version="1.0.0"
)

class PriorityLevel(str, Enum):
low = "low"
medium = "medium"
high = "high"

class TodoBase(BaseModel):
title: str = Field(..., example="Buy groceries")
description: Optional[str] = Field(None, example="Milk, Eggs, Bread")
priority: PriorityLevel = Field(default=PriorityLevel.medium, example="high")

class TodoCreate(TodoBase):
pass

class Todo(TodoBase):
id: int = Field(..., example=1)
completed: bool = Field(default=False, example=False)

class Config:
schema_extra = {
"example": {
"id": 1,
"title": "Buy groceries",
"description": "Milk, Eggs, Bread",
"priority": "high",
"completed": False
}
}

# In-memory database
todos = {}
todo_id_counter = 1

@app.post("/todos/", response_model=Todo, status_code=201, tags=["todos"])
def create_todo(todo: TodoCreate):
"""
Create a new todo item.

This endpoint allows you to create a new todo item with the given details.
The todo ID will be assigned automatically.
"""
global todo_id_counter
new_todo = Todo(
id=todo_id_counter,
**todo.dict()
)
todos[todo_id_counter] = new_todo
todo_id_counter += 1
return new_todo

@app.get("/todos/", response_model=List[Todo], tags=["todos"])
def read_todos(
skip: int = Query(0, description="Skip N items", ge=0),
limit: int = Query(10, description="Limit the number of items returned", ge=1, le=100),
priority: Optional[PriorityLevel] = Query(None, description="Filter by priority level")
):
"""
Get all todos with optional filtering.

You can paginate results using skip and limit parameters,
and filter by priority if needed.
"""
result = list(todos.values())

if priority:
result = [todo for todo in result if todo.priority == priority]

return result[skip:skip+limit]

@app.get("/todos/{todo_id}", response_model=Todo, tags=["todos"])
def read_todo(
todo_id: int = Path(..., description="The ID of the todo to retrieve", ge=1)
):
"""
Get a specific todo by ID.

Retrieves the details of a specific todo item by its unique identifier.
"""
if todo_id not in todos:
raise HTTPException(status_code=404, detail="Todo not found")
return todos[todo_id]

@app.put("/todos/{todo_id}", response_model=Todo, tags=["todos"])
def update_todo(
todo_id: int = Path(..., description="The ID of the todo to update", ge=1),
todo: TodoCreate = Body(..., description="Updated todo information")
):
"""
Update a todo.

Updates the details of an existing todo item. All fields will be replaced.
"""
if todo_id not in todos:
raise HTTPException(status_code=404, detail="Todo not found")

updated_todo = Todo(
id=todo_id,
completed=todos[todo_id].completed,
**todo.dict()
)
todos[todo_id] = updated_todo
return updated_todo

@app.patch("/todos/{todo_id}/complete", response_model=Todo, tags=["todos"])
def complete_todo(
todo_id: int = Path(..., description="The ID of the todo to mark as complete", ge=1)
):
"""
Mark a todo as complete.

This is a convenience endpoint to toggle a todo's completed status to true.
"""
if todo_id not in todos:
raise HTTPException(status_code=404, detail="Todo not found")

todos[todo_id].completed = True
return todos[todo_id]

@app.delete("/todos/{todo_id}", status_code=204, tags=["todos"])
def delete_todo(
todo_id: int = Path(..., description="The ID of the todo to delete", ge=1)
):
"""
Delete a todo.

Permanently removes a todo item from the system.
"""
if todo_id not in todos:
raise HTTPException(status_code=404, detail="Todo not found")

del todos[todo_id]
return None

This example demonstrates:

  • Detailed API metadata
  • Enum for constrained values
  • Rich documentation with examples
  • Proper response models and status codes
  • Parameter validation and descriptions
  • Organized endpoints with tags
  • Comprehensive docstrings

Disabling Documentation

In some cases, you might want to disable the interactive documentation for production environments:

python
app = FastAPI(docs_url=None, redoc_url=None)

This removes both the Swagger UI and ReDoc interfaces. You can also disable just one:

python
app = FastAPI(redoc_url=None)  # Disables only ReDoc

Summary

FastAPI's interactive documentation is one of its strongest features, providing:

  1. Automatic generation of OpenAPI schema from your code
  2. Two different interactive documentation interfaces (Swagger UI and ReDoc)
  3. The ability to test your API directly from the browser
  4. Customization options for improving documentation quality
  5. Tools to organize endpoints as your API grows

This documentation not only serves as a valuable development tool but also provides clear, up-to-date documentation for API consumers with zero additional effort.

Additional Resources

Practice Exercises

  1. Create a basic FastAPI application and experiment with both documentation interfaces
  2. Try enhancing your API documentation with detailed descriptions and examples
  3. Build an API with at least 5 endpoints, organizing them with appropriate tags
  4. Create custom response models with examples for all your endpoints
  5. Try customizing the API metadata with your own information


If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)