60.9 Automatic OpenAPI / Swagger Documentation
Right, so you’ve built a few endpoints. They work. You’ve tested them with curl or HTTPie and high-fived yourself. Now comes the part where you have to tell other humans (or, more likely, your future self at 3 AM) how to use your creation. In the bad old days, this meant opening a text editor and writing a documentation file that would be outdated before you even saved it.
FastAPI, in a moment of pure, unadulterated genius, says, “Nah, we’re not doing that.” Instead, it automatically generates a full, interactive OpenAPI (formerly Swagger) documentation for your API. It’s not just a party trick; it’s a fundamental shift in how you think about API docs. The docs are the code, and the code is the docs. If you change your endpoint’s expected input, the docs change instantly to match. It’s black magic, and we’re here for it.
How It Actually Works (The Witchcraft, Explained)
The magic isn’t free; it’s built on the foundation of type hints and Pydantic models. When you define a Pydantic model for your request body or use type hints for your path and query parameters, FastAPI isn’t just using that for validation. It’s introspecting those definitions to understand the exact structure, data types, and constraints of your API’s inputs and outputs.
It then builds a massive JSON object that conforms to the OpenAPI specification. This JSON is a complete, machine-readable description of your entire API. The interactive docs you see in the browser are just a fancy UI (Swagger UI) that reads this JSON and renders it. Think of the JSON as the data, and the HTML page as a particularly slick view.
You can actually see the raw JSON yourself. Fire up your app and go to /openapi.json. Behold the glory. Every path, every parameter, every possible response code—it’s all there in standardized, structured data.
Your Two New Favorite URLs
Spin up your FastAPI app in development. I’ll wait. Now, open your browser and navigate to:
/docs- This is the Swagger UI interface. It’s the one you’ll use 99% of the time. It’s interactive, letting you click on an endpoint, click “Try it out,” fill in the parameters, and execute the request right there in the browser. It’s the fastest way to test your API without leaving your code editor./redoc- This is the ReDoc interface. It provides a cleaner, more documentation-focused view. It’s less about interactive testing and more about presenting a beautiful, readable API reference. Some teams prefer it for external-facing documentation.
Here’s a tiny app that generates a massive amount of documentation from a tiny amount of code:
from fastapi import FastAPI, status
from pydantic import BaseModel, Field
app = FastAPI(title="My Awesome API", version="0.1.0")
class Item(BaseModel):
name: str = Field(..., example="Foo", description="The name of the item. Duh.")
price: float = Field(..., gt=0, example=35.4, description="Price must be positive. We're not a charity.")
is_offer: bool | None = Field(default=None, example=True, description="Is it on sale?")
@app.post("/items/{item_id}", response_model=Item, status_code=status.HTTP_201_CREATED)
async def create_item(item_id: int, item: Item):
"""
Create an item with all the information:
- **item_id**: The ID of the item (must be unique, good luck with that)
- **item**: A JSON object containing the item's name, price, and optional sale status
"""
return item
Go to /docs after running this. Look at the POST /items/{item_id} endpoint. See how the path parameter, the required request body with its fields, their types, the examples, and even the descriptions we wrote are all seamlessly integrated? That’s the power of leaning into the type system.
Customizing the Beast
The default setup is brilliant, but you might need to tweak it. FastAPI’s FastAPI() constructor has parameters for this.
app = FastAPI(
title="My Over-Engineered API",
description="This API does... things. Hopefully the right things.",
version="0.0.1",
docs_url="/api/docs", # Change the docs URL to /api/docs
redoc_url="/api/redoc", # Similarly for ReDoc
# openapi_url="/api/openapi.json", # Uncomment to change the JSON spec's URL
)
Want to hide the docs entirely in production? It’s as simple as setting the URLs to None.
app = FastAPI(docs_url=None, redoc_url=None)
The Rough Edges and Pitfalls
It’s not all rainbows and unicorns. The system is only as good as the information you give it.
- The
exampleConundrum: Notice I usedField(..., example="Foo"). There’s also anexamplesparameter that takes a complexdictfor multiple examples, and a separateexample(no ’s’) JSONable object you can put in aConfigclass. The naming is, frankly, a mess and easy to mix up. Always double-check the Pydantic docs when you need multiple examples. - Garbage In, Garbage Out: If you use a generic type like
Dict[str, Any]orobjectas a response model, the OpenAPI docs will just say “response body: object.” Wow, thanks. That’s useless. Be as specific as possible with your Pydantic models. The docs are the reward for your precision. - The Lazy Programmer’s Trap: You can write endpoints without type hints or Pydantic models. FastAPI will still work. But the docs will be barebones, showing “response body: string” for everything. You’ve chosen to bypass the framework’s superpower. Don’t do that.
- Dependency Documentation: Dependencies for user authentication or database sessions can clutter your endpoint docs if they’re not explicitly hidden. You can use
include_in_schema=Falseon a dependency to tidy things up.
The bottom line is this: the automatic docs are a killer feature, but they demand discipline. They force you to write better, more strictly typed code. In return, they give you living, breathing documentation that never lies. It’s one of the best trades you’ll ever make.