Chapter 2 · First App and Auto-Generated Documentation
Chapter goals: Master the correspondence between path operation decorators and HTTP verbs, understand route matching order, and learn to use the three auto-generated artifacts: /docs, /redoc, and openapi.json.
2.1 Path Operation Decorators
A "path operation" refers to "which HTTP verb accesses which path." FastAPI declares them with decorators:
from fastapi import FastAPI
app = FastAPI()
@app.get("/items") # GET — read resources
def list_items():
return [{"id": 1, "name": "apple"}]
@app.post("/items") # POST — create a resource
def create_item():
return {"created": True}
@app.put("/items/{item_id}") # PUT — full replacement
def replace_item(item_id: int):
return {"replaced": item_id}
@app.patch("/items/{item_id}") # PATCH — partial update
def update_item(item_id: int):
return {"patched": item_id}
@app.delete("/items/{item_id}") # DELETE — remove
def remove_item(item_id: int):
return {"deleted": item_id}@app.get("/items") tells FastAPI: "when a GET /items request arrives, call the function below and send back the return value as JSON." Other available verbs are head, options, trace — but the five above cover 99% of REST API usage.
Decorator parameters
Each decorator supports response_model, status_code, tags, summary, and other parameters (introduced from Chapter 6 onward). They affect only documentation and response behavior, not route matching itself.
2.2 Route Matching Order: Top to Bottom
FastAPI tries routes in declaration order. This creates a classic pitfall:
from fastapi import FastAPI
app = FastAPI()
# ❌ Wrong: /users/me declared AFTER /users/{user_id}
@app.get("/users/{user_id}")
def read_user(user_id: str):
return {"user_id": user_id}
@app.get("/users/me")
def read_current_user():
# Never reached! A request to GET /users/me
# matches /users/{user_id} first, with user_id="me"
return {"user": "the authenticated user"}The fix is simple — static paths must be declared before parameterized ones:
from fastapi import FastAPI
app = FastAPI()
# ✅ Correct: static path declared first
@app.get("/users/me")
def read_current_user():
return {"user": "the authenticated user"}
@app.get("/users/{user_id}")
def read_user(user_id: str):
return {"user_id": user_id}This is not a FastAPI bug — it is a shared rule of all ordered-matching routers (Express, Flask in some scenarios behave the same way).
2.3 Automatic Response Serialization
Path functions can return dict, list, scalars, Pydantic models, or even ORM objects — FastAPI auto-converts to JSON:
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
price: float
@app.get("/dict-demo")
def as_dict():
return {"ok": True, "code": 0} # dict → JSON object
@app.get("/list-demo")
def as_list():
return [1, 2, 3] # list → JSON array
@app.get("/model-demo", response_model=Item)
def as_model():
# Pydantic model → JSON (via Pydantic's Rust-powered serializer, extremely fast)
return Item(name="pen", price=3.5)2.4 Auto-Generated Documentation: /docs, /redoc, openapi.json
Once routes are declared, FastAPI automatically generates interactive documentation at three endpoints:
| URL | What It Shows |
|---|---|
/docs | Swagger UI — interactive form-based API explorer |
/redoc | ReDoc — clean, scrollable reference documentation |
/openapi.json | Raw OpenAPI 3 schema (machine-readable) |
Visit http://localhost:8000/docs after starting the app — you will see every route with its parameters, request body schema, and response model, all auto-generated from your code. Click "Try it out" to send real requests from the browser.
2.5 Chapter Summary
- Five REST verbs:
@app.get/post/put/patch/delete; each declares one path operation; - Route matching is top-to-bottom: static paths before parameterized ones;
- Return values auto-serialize to JSON;
response_modelfilters output; /docs(Swagger UI) and/redocare free, auto-generated, and interactive.
🧪 随堂测验
点击你认为正确的选项。答错时会展示正确答案与原因解析。
1. What happens if you declare /users/{user_id} before /users/me?
2. What URL shows the interactive Swagger UI?
3. Which decorator handles partial resource updates?
4. What does returning a Pydantic BaseModel from a route do automatically?
🛠️ Hands-On Exercises
- Create an app with CRUD routes for
/items(GET list, POST create, GET by ID, PUT update, DELETE); open/docsand use the interactive UI to test each endpoint. - Deliberately reverse the declaration order of
/items/{item_id}and/items/special; observe which one matches/items/specialin/docs. - Export the raw schema with
curl http://localhost:8000/openapi.json | jq .pathsand verify all routes appear.
Proceed to Chapter 3: Path Parameters and Query Parameters.