← Python Code FastAPI
Browse Python Concepts

Path, Query, and Body Parameters in FastAPI

Mental Model

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.

Rule: Always explicitly annotate non-Pydantic input parameters with Body() if they must be extracted from the request payload.

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?

Broken code
Python
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}
Predict the server response when sending a POST request to /items/42 with a JSON payload of {"name": "heavy_duty"}.

The Output

What actually happens
{ "detail": [ { "type": "missing", "loc": ["query", "payload"], "msg": "Field required", "input": null } ] }

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

Corrected pattern
Python
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

Always explicitly annotate non-Pydantic input parameters with Body() if they must be extracted from the request payload.
Common mistake: Assuming FastAPI automatically infers the source of complex type hints like dict to be the request body, similar to how it handles Pydantic models.