Skip to main content

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 deprecated regex).
  • Metadata: title, description, examples (replaces the deprecated example).

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.