Request Body and JSON
In FastAPI, the request body is typically handled by Pydantic models. However, the Body class (found in fastapi.params) provides explicit control over how data is extracted from the request body, allowing for multiple parameters, scalar values, and specific JSON structures.
The Body Parameter
The Body class inherits from Pydantic's FieldInfo and is used to signal that a function parameter should be retrieved from the JSON body of the request. While FastAPI automatically treats Pydantic models as body parameters, you must use Body explicitly for scalar types (like int or str) or when you need to customize the extraction logic.
By default, if you declare a single Pydantic model as a parameter, FastAPI expects the entire request body to match that model.
Embedding Single Parameters
When you have a single body parameter, you might still want it to be expected as a key within a JSON object rather than the entire body. The embed parameter in Body controls this behavior.
In docs_src/body_multiple_params/tutorial005_an_py310.py, the embed=True argument is used to wrap the Item model:
from typing import Annotated
from fastapi import Body, FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Annotated[Item, Body(embed=True)]):
results = {"item_id": item_id, "item": item}
return results
With embed=True, the expected JSON body changes from:
{
"name": "Foo",
"price": 42.0
}
To:
{
"item": {
"name": "Foo",
"price": 42.0
}
}
Multiple Body Parameters
FastAPI allows you to define multiple body parameters in a single path operation. When multiple parameters are detected, FastAPI automatically "embeds" them, expecting a JSON object where each key corresponds to a parameter name.
As seen in docs_src/body_multiple_params/tutorial003_an_py310.py, you can mix models and scalar values:
@app.put("/items/{item_id}")
async def update_item(
item_id: int, item: Item, user: User, importance: Annotated[int, Body()]
):
results = {"item_id": item_id, "item": item, "user": user, "importance": importance}
return results
In this case, the request body must contain keys for item, user, and importance.
Validation and Metadata
Because Body inherits from FieldInfo, it supports the full range of Pydantic validation and OpenAPI metadata. This is particularly useful for scalar body parameters that don't belong to a model.
Common validation parameters include:
- Numeric constraints:
gt(greater than),ge(greater than or equal),lt,le. - String constraints:
min_length,max_length,pattern(replaces the deprecatedregex). - Metadata:
title,description,examples(replaces the deprecatedexample).
Example from docs_src/body_multiple_params/tutorial004_an_py310.py showing a numeric constraint:
@app.put("/items/{item_id}")
async def update_item(
*,
item_id: int,
item: Item,
user: User,
importance: Annotated[int, Body(gt=0)],
q: str | None = None,
):
results = {"item_id": item_id, "item": item, "user": user, "importance": importance}
# ...
The Body implementation in fastapi/params.py explicitly handles these validations and ensures they are passed to the underlying Pydantic field logic.
Custom Media Types
While the default media_type for Body is application/json, you can override it for specialized APIs. This is useful when working with JSON-based media types like application/vnd.api+json.
As demonstrated in tests/test_request_body_parameters_media_type.py:
@app.post("/products")
async def create_product(
data: Product = Body(media_type="application/vnd.api+json", embed=True)
):
return data
This configuration informs the OpenAPI schema generation that the endpoint expects a specific content type while still treating the payload as JSON for validation purposes.