FastAPI Essentials

Course Content

FastAPI Essentials

1 sections · 32 lessons

How does FastAPI leverage type hints for automatic API documentation?


What you need to know

  1. Inspect — FastAPI reads each route's parameters and annotations using Python's inspect and typing tools.
  2. Classify — each parameter is marked as path, query, header, cookie or body.
  3. Build schemas — Pydantic generates a JSON Schema for every model and constrained type.
  4. Assemble — FastAPI combines paths, schemas, tags and summaries into one OpenAPI 3.1 document.
  5. Serve — /openapi.json returns it; /docs and /redoc render it.

Here is a reranking endpoint from a retrieval service:

Python
from typing import Annotatedfrom fastapi import FastAPI, Queryfrom pydantic import BaseModel, Fieldapp = FastAPI(title="Retrieval API", version="1.2.0")class RerankIn(BaseModel):    passages: list[str] = Field(min_length=1, description="Candidate chunks from the vector store")class RerankOut(BaseModel):    order: list[int]    scores: list[float]@app.post("/rerank", tags=["retrieval"], summary="Rerank candidate passages")def rerank(q: Annotated[str, Query(min_length=3, description="The user's question")],           body: RerankIn) -> RerankOut:    ...

Parts of what app.openapi() really produces:

Text
openapi: 3.1.0parameters: [{"name": "q", "in": "query", "required": true,              "schema": {"type": "string", "minLength": 3, ...}}]responses:  ['200', '422']200 schema: {'$ref': '#/components/schemas/RerankOut'}RerankIn:   {'passages': {'type': 'array', 'items': {'type': 'string'},             'minItems': 1, 'description': 'Candidate chunks from the vector store'}}

Every detail came from the code: q is a required query parameter because it has no default; minLength: 3 came from Query(min_length=3); the 422 response was added because the route has validation; the 200 schema came from the -> RerankOut return type.

Making the docs richer

  • A docstring on the function becomes the endpoint description.
  • Field(description=..., examples=[...]) documents single fields.
  • tags=[...] groups endpoints in the UI.
  • responses={404: {"description": "Index not found"}} documents extra error codes.

To hide the docs on a private service, use FastAPI(docs_url=None, redoc_url=None, openapi_url=None).

A real-life example

A retrieval team builds the rerank service above. The front-end team and an internal agents team both call it. Instead of a shared document, both generate typed clients from /openapi.json (for example with openapi-typescript).

When the retrieval team adds top_k: int = Query(10, le=50), the new parameter and its limit appear in the spec on the next deploy. The front-end build regenerates its client, and the compiler shows every place that should pass top_k. The agents team turns the same schema into a tool definition for the LLM. One set of type hints feeds validation, docs, two clients and an agent tool.

Follow-up questions to expect

  • "What if a route has no return type?" — The 200 response schema is empty ({}), so consumers learn nothing about the output. Add a return type or response_model.
  • "How do you add examples to the docs?" — Field(examples=[...]) on a field, or model_config = {"json_schema_extra": {"examples": [...]}} on the model.
  • "Can you customise the whole schema?" — Yes, by overriding app.openapi with a function that edits the generated dict. FastAPI builds the schema once and caches it.