Skip to main content

FastAPI ReDoc

In this tutorial, you'll learn how to use FastAPI's built-in ReDoc integration to create beautiful, interactive API documentation for your application.

Introduction to ReDoc

ReDoc is a powerful, open-source tool that generates API documentation from OpenAPI specifications. FastAPI automatically integrates ReDoc, providing your API with professional and interactive documentation without any additional effort.

Unlike Swagger UI (which is also included in FastAPI), ReDoc offers a different visual style and user experience:

  • A three-panel, responsive layout
  • Better support for nested objects
  • Improved readability for large APIs
  • Support for deep linking

How FastAPI Integrates ReDoc

FastAPI automatically generates OpenAPI documentation (previously known as Swagger) for your API and makes it available in the ReDoc format. This happens behind the scenes with no extra code required from you!

Accessing ReDoc Documentation

When you create a FastAPI application, the ReDoc documentation is automatically available at the /redoc endpoint. For example, if your API is running on http://localhost:8000, you can access the ReDoc documentation at:

http://localhost:8000/redoc

Basic Setup

Let's create a simple API and see how ReDoc documentation works:

python
from fastapi import FastAPI

app = FastAPI(
title="My Amazing API",
description="This is a sample API to demonstrate ReDoc documentation",
version="0.1.0"
)

@app.get("/")
def read_root():
"""
Return a simple greeting message.
"""
return {"message": "Hello World"}

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

- **item_id**: The ID of the item to retrieve
- **query**: Optional search query parameter
"""
return {"item_id": item_id, "query": query}

When you run this application and navigate to /redoc, you'll see beautiful documentation automatically generated for your API, including:

  1. API title, description, and version
  2. All endpoints organized by tags
  3. Request parameters and response models
  4. Docstring comments as descriptions

Customizing ReDoc Documentation

Adding Metadata

You can customize the overall API documentation by providing metadata in the FastAPI constructor:

python
app = FastAPI(
title="Pet Store API",
description="""
This API helps you manage your pet store inventory.

## Features

* **Create** and manage pets
* **Track** inventory
* **Process** orders
""",
version="1.0.0",
terms_of_service="http://example.com/terms/",
contact={
"name": "API Support",
"url": "http://example.com/support",
"email": "[email protected]",
},
license_info={
"name": "Apache 2.0",
"url": "https://www.apache.org/licenses/LICENSE-2.0.html",
},
)

Documenting Path Operations

To make your ReDoc documentation more useful, add docstrings and parameter descriptions:

python
from typing import Optional
from fastapi import FastAPI, Query

app = FastAPI()

@app.get("/users/{user_id}")
async def read_user(
user_id: int,
active: Optional[bool] = Query(
None,
title="Active Status",
description="Filter users by active status"
)
):
"""
Retrieve a user by ID.

This endpoint will return user details based on the path parameter.
If the active query parameter is provided, it will filter by status.

**Note**: This endpoint requires authentication.
"""
return {"user_id": user_id, "active": active}

Adding Examples

You can enhance your documentation by providing examples:

python
from fastapi import FastAPI, Body
from pydantic import BaseModel, Field
from typing import Optional

app = FastAPI()

class Item(BaseModel):
name: str = Field(..., example="Smartphone")
description: Optional[str] = Field(None, example="A high-end smartphone with great camera")
price: float = Field(..., example=999.99)
tax: Optional[float] = Field(None, example=87.5)

class Config:
schema_extra = {
"example": {
"name": "Laptop",
"description": "A powerful laptop for developers",
"price": 1299.99,
"tax": 115.99
}
}

@app.post("/items/")
async def create_item(item: Item):
"""
Create a new item in the inventory.

The request body should contain item details.
"""
return item

Organizing with Tags

You can organize your endpoints in ReDoc using tags:

python
from fastapi import FastAPI

app = FastAPI()

@app.get("/users/", tags=["users"])
async def get_users():
"""Get all users."""
return [{"id": 1, "name": "Alice"}, {"id": 2, "name": "Bob"}]

@app.post("/users/", tags=["users"])
async def create_user():
"""Create a new user."""
return {"id": 3, "name": "Charlie"}

@app.get("/items/", tags=["items"])
async def get_items():
"""Get all items."""
return [{"id": 1, "name": "Hammer"}, {"id": 2, "name": "Screwdriver"}]

@app.post("/items/", tags=["items"])
async def create_item():
"""Create a new item."""
return {"id": 3, "name": "Wrench"}

In ReDoc, these endpoints will be grouped under "users" and "items" sections, making your documentation more organized.

Customizing ReDoc Appearance

Disabling ReDoc

If you want to disable ReDoc for some reason, you can do so by setting docs_url or redoc_url to None:

python
# Disable ReDoc, keep Swagger UI
app = FastAPI(redoc_url=None)

# Disable both Swagger UI and ReDoc
app = FastAPI(docs_url=None, redoc_url=None)

Changing the URL Path

You can customize the URL path for ReDoc:

python
app = FastAPI(redoc_url="/api-docs")

Now ReDoc will be available at /api-docs instead of the default /redoc.

Real-World Example: Building a Movie API

Let's create a more complex example—a movie review API:

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

app = FastAPI(
title="MovieReviews API",
description="An API for managing movie reviews and ratings",
version="1.0.0"
)

class Genre(str, Enum):
ACTION = "action"
COMEDY = "comedy"
DRAMA = "drama"
HORROR = "horror"
SCIFI = "sci-fi"
ROMANCE = "romance"

class MovieBase(BaseModel):
title: str = Field(..., example="The Matrix")
director: str = Field(..., example="Lana and Lilly Wachowski")
year: int = Field(..., ge=1900, le=2100, example=1999)
genre: Genre = Field(..., example=Genre.SCIFI)

class Config:
schema_extra = {
"example": {
"title": "Inception",
"director": "Christopher Nolan",
"year": 2010,
"genre": "sci-fi"
}
}

class Movie(MovieBase):
id: int
rating: Optional[float] = Field(None, ge=0, le=10, example=8.5)

class ReviewBase(BaseModel):
text: str = Field(..., min_length=5, max_length=500, example="This movie was fantastic!")
rating: float = Field(..., ge=0, le=10, example=9.0)

class Review(ReviewBase):
id: int
movie_id: int
user_id: int

# Sample data
movies_db = [
{"id": 1, "title": "The Shawshank Redemption", "director": "Frank Darabont",
"year": 1994, "genre": "drama", "rating": 9.3},
{"id": 2, "title": "The Godfather", "director": "Francis Ford Coppola",
"year": 1972, "genre": "drama", "rating": 9.2},
]

reviews_db = [
{"id": 1, "movie_id": 1, "user_id": 1, "text": "A masterpiece!", "rating": 9.5},
{"id": 2, "movie_id": 1, "user_id": 2, "text": "Incredible story and acting", "rating": 9.0},
]

# Endpoints
@app.get("/movies/", response_model=List[Movie], tags=["movies"])
async def get_movies(
genre: Optional[Genre] = Query(None, description="Filter movies by genre"),
year: Optional[int] = Query(None, description="Filter movies by release year")
):
"""
Retrieve a list of all movies with optional filtering.

You can filter the results by genre and/or release year.
"""
filtered_movies = movies_db

if genre:
filtered_movies = [m for m in filtered_movies if m["genre"] == genre]

if year:
filtered_movies = [m for m in filtered_movies if m["year"] == year]

return filtered_movies

@app.get("/movies/{movie_id}", response_model=Movie, tags=["movies"])
async def get_movie(
movie_id: int = Path(..., title="The ID of the movie", ge=1)
):
"""
Retrieve detailed information about a specific movie by its ID.
"""
for movie in movies_db:
if movie["id"] == movie_id:
return movie

raise HTTPException(status_code=404, detail="Movie not found")

@app.get("/movies/{movie_id}/reviews", response_model=List[Review], tags=["reviews"])
async def get_movie_reviews(movie_id: int):
"""
Retrieve all reviews for a specific movie by its ID.
"""
# First check if movie exists
movie_exists = any(movie["id"] == movie_id for movie in movies_db)
if not movie_exists:
raise HTTPException(status_code=404, detail="Movie not found")

return [r for r in reviews_db if r["movie_id"] == movie_id]

@app.post("/movies/{movie_id}/reviews", response_model=Review, tags=["reviews"])
async def create_review(movie_id: int, review: ReviewBase):
"""
Create a new review for a specific movie.

The request body should include review text and rating.
"""
# This is simplified (in a real app, you would save to a database)
new_review = {
"id": len(reviews_db) + 1,
"movie_id": movie_id,
"user_id": 1, # In a real app, this would come from authentication
**review.dict()
}
reviews_db.append(new_review)
return new_review

When you navigate to the /redoc endpoint, you'll see a beautiful, organized API documentation with:

  1. Movie and review models clearly documented
  2. Endpoints grouped into "movies" and "reviews" sections
  3. Request and response schemas with examples
  4. Clear descriptions for each endpoint and parameter

Summary

FastAPI's ReDoc integration provides an elegant way to automatically document your API. In this tutorial, you've learned:

  • How FastAPI integrates with ReDoc out of the box
  • How to access and customize the ReDoc documentation
  • Different ways to enhance your API documentation with examples, descriptions, and tags
  • How to structure a real-world API with ReDoc documentation

By leveraging ReDoc, you can ensure that your API is not only functional but also easily understandable and usable by other developers.

Further Resources

Exercises

  1. Create a simple FastAPI application with at least two endpoints and view the generated ReDoc documentation.
  2. Enhance the documentation with detailed descriptions, examples, and parameter explanations.
  3. Organize your API endpoints using tags and observe how this affects the ReDoc layout.
  4. Try customizing the ReDoc URL path and experiment with disabling/enabling it.
  5. Create a more complex API with nested models and see how ReDoc handles the documentation for these complex structures.


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