FastAPI Essentials

Course Content

FastAPI Essentials

1 sections · 32 lessons

How do you define a route in FastAPI?


Routes are tried top to bottomGET/models/latest arrivestry GET/models/{model_id}GET/models/latestmatches:id = latestnever reachedFix: declare the fixed path above the parameterised one.
The first match wins, so a dynamic route declared early quietly swallows every fixed path that looks like it.

What you need to know

FastAPI calls a route a path operation: a path (/models/{model_id}) plus an operation, which is the HTTP method (GET, POST, PUT, PATCH, DELETE). You attach a function to the pair with a decorator.

Python
from fastapi import FastAPIfrom pydantic import BaseModelapp = FastAPI()class Question(BaseModel):    text: str@app.get("/models/latest")def latest_model():    return {"model": "sentiment-v3"}@app.get("/models/{model_id}")def read_model(model_id: str, verbose: bool = False):    return {"model": model_id, "verbose": verbose}@app.post("/models/{model_id}/answer")def answer(model_id: str, body: Question, top_k: int = 3):    return {"model": model_id, "question": body.text, "top_k": top_k}

Real responses from TestClient:

Text
GET  /models/latest                      -> {'model': 'sentiment-v3'}GET  /models/bert-small?verbose=yes      -> {'model': 'bert-small', 'verbose': True}GET  /models/bert-small?verbose=maybe    -> 422POST /models/bert-small/answer?top_k=5   -> {'model': 'bert-small', 'question': 'refund?', 'top_k': 5}

How FastAPI decides where each parameter comes from

Parameter looks likeFastAPI treats it asExample
Name appears in the path stringPath parameter, always requiredmodel_id
Simple type (int, str, bool) not in the pathQuery parameter; optional if it has a defaultverbose, top_k
A Pydantic modelJSON request bodybody: Question

The types are not decoration. verbose=yes became True, and verbose=maybe was rejected with a 422 before the function ran.

Other things a route declares

  • Status code: @app.post("/documents", status_code=201) for "created".
  • Response shape: a return type such as -> Sentiment (or response_model=) validates, filters and documents the output.
  • Grouping: in a real project routes live on an APIRouter per area (chat, RAG, admin), which is then added to the app.

Why order matters

FastAPI checks routes top to bottom and uses the first match. If /models/{model_id} is declared first, a request to /models/latest matches it with model_id="latest", and the latest_model function is never reached.

A real-life example

A team serves three sentiment models: English, Hindi and Tamil. Clients call GET /models/{model_id} to read a model's metadata. Later someone adds GET /models/latest at the bottom of the file so the mobile app can find the newest model.

In testing it returns 404 Model 'latest' not found, because the request is caught by the dynamic route, which looks up a model literally named "latest". Nothing crashes, so the bug is easy to miss. Moving the fixed route above the dynamic one fixes it. A good habit is one router per resource, with fixed paths listed first.

Follow-up questions to expect

  • "What happens if the client uses the wrong method?" — FastAPI returns 405 Method Not Allowed when the path exists but not for that method.
  • "When do you use async def versus def for a route?" — async def when everything inside is awaitable I/O; plain def when you call blocking libraries, because FastAPI runs it in a threadpool.
  • "How do you add validation to a path or query parameter?" — Use Path() or Query() with limits, for example top_k: Annotated[int, Query(ge=1, le=20)] = 3.