FASTAPI / 1. ROUTING & PATH PARAMS

Routing, Path & Query Parameters

Defining endpoints and extracting data from the URL


EXPLANATION

In FastAPI, a route is just a Python function decorated with an HTTP method. FastAPI reads the function signature to figure out where to get each parameter from.

Path Parameters — part of the URL itself:
  /users/{user_id}  →  def get_user(user_id: int)
FastAPI extracts user_id from the URL, converts it to int, and validates it. If the client sends /users/abc, FastAPI returns a 422 Unprocessable Entity automatically — no if/else needed.

Query Parameters — come after the ? in the URL:
  /items?skip=0&limit=10  →  def get_items(skip: int = 0, limit: int = 10)
Defaults make them optional. No default = required query param.

Type annotations do three things simultaneously:
① They tell FastAPI where to get the value (path vs query vs body)
② They define the type to convert to (str, int, float, bool, UUID…)
③ They define what's required vs optional (no default = required)

Order matters: FastAPI checks if the param name matches a path segment first. If yes → path param. If no → query param. If it's a Pydantic model → request body.

Enum path params: inherit from str and Enum to restrict allowed values — FastAPI validates and documents them automatically.

Path() and Query() let you add metadata: min/max values, regex patterns, descriptions, examples — all reflected in /docs.

ARCHITECTURE

URL: GET /users/42/posts?skip=0&limit=5&published=true

  ┌──────────────────────────────────────────────────────┐
  │  Path Params        Query Params                     │
  │  ───────────        ────────────                     │
  │  user_id = 42       skip = 0  (int, default 0)       │
  │                     limit = 5 (int, default 10)      │
  │                     published = True (bool, optional) │
  └──────────────────────────────────────────────────────┘

  FastAPI function signature maps 1:1:
  def get_user_posts(
      user_id: int,          ← path param (in URL)
      skip: int = 0,         ← query param (optional)
      limit: int = 10,       ← query param (optional)
      published: bool = None ← query param (optional)
  )

  Type coercion happens automatically:
  "42"   → int(42)     ✓
  "true" → bool(True)  ✓
  "abc"  → int fails   → 422 Unprocessable Entity

CODE

PYTHON
1from fastapi import FastAPI, Path, Query
2from enum import Enum
3
4app = FastAPI()
5
6# ── Simple path parameter ──────────────────────────────
7@app.get("/users/{user_id}")
8def get_user(user_id: int):
9 return {"user_id": user_id}
10
11# ── Path + Query params ────────────────────────────────
12@app.get("/users/{user_id}/posts")
13def get_user_posts(
14 user_id: int,
15 skip: int = 0,
16 limit: int = Query(default=10, ge=1, le=100), # 1 ≤ limit ≤ 100
17 published: bool | None = None,
18):
19 return {"user_id": user_id, "skip": skip, "limit": limit, "published": published}
20
21# ── Enum path param ────────────────────────────────────
22class ModelName(str, Enum):
23 resnet = "resnet"
24 alexnet = "alexnet"
25 lenet = "lenet"
26
27@app.get("/models/{model_name}")
28def get_model(model_name: ModelName):
29 if model_name == ModelName.resnet:
30 return {"model": model_name, "message": "Deep residual learning!"}
31 return {"model": model_name}
32
33# ── Path() with validation & docs metadata ─────────────
34@app.get("/items/{item_id}")
35def get_item(
36 item_id: int = Path(title="The item ID", ge=1),
37 q: str | None = Query(default=None, min_length=3, max_length=50),
38):
39 return {"item_id": item_id, "q": q}
← PREVOverviewNEXT →2. Pydantic & Request Bodies