Path, Query, and Body Parameters in FastAPI
FastAPI's parameter inference works like a smart assistant but needs clear instructions for ambiguous types. For Pydantic models, it assumes Body. For simple types, it checks path, then query. For a generic dict, it defaults to query unless told otherwise.
The Setup
You want to implement a POST endpoint that accepts a dynamic query parameter and a raw JSON dictionary payload. You write the route utilizing standard type annotations, assuming FastAPI will parse the dictionary as the request body.
What Does This Print?
from fastapi import FastAPI
app = FastAPI()
@app.post("/items/{item_id}")
async def update_item(item_id: int, payload: dict):
return {"item_id": item_id, "payload": payload}
/items/42 with a JSON payload of {"name": "heavy_duty"}.
The Output
The server returns a 422 Unprocessable Entity status code with the following response body:
FastAPI expects the payload parameter to be passed inside the URL query string, completely ignoring the HTTP request body payload you provided.
Why Python Does This
FastAPI parses the endpoint signature at application startup using Python's inspect module and Pydantic validation rules. If a parameter is declared in the path (e.g., {item_id}), FastAPI treats it as a path parameter. For other parameters, FastAPI checks if the type annotation inherits from Pydantic's BaseModel. Since dict is a native Python dictionary type and does not inherit from BaseModel, FastAPI defaults to parsing it as a query parameter. To resolve this parameter as an incoming JSON request body instead, you must explicitly declare it with FastAPI's Body marker.
The Fix
from fastapi import FastAPI, Body
app = FastAPI()
@app.post("/items/{item_id}")
async def update_item(item_id: int, payload: dict = Body(...)):
# Explicitly using Body(...) forces FastAPI to read the JSON request payload
return {"item_id": item_id, "payload": payload}
Explicitly annotating payload: dict with Body() tells FastAPI to look for this parameter in the request body. This overrides the default inference logic, allowing FastAPI's internal data extraction and validation to correctly process the incoming JSON payload.
How This Fails in Real Systems
A payment gateway endpoint received a map of custom metadata fields from a web client. Because the metadata parameter was annotated as dict instead of Body(), the API threw 422 errors for every checkout action. The issue went unnoticed until the staging environment was deployed with real client integrations.
Key Takeaway
dict to be the request body, similar to how it handles Pydantic models.