FastAPI Request Body Fields and Nested Models

Inside a Pydantic model, you can useFieldto declare validation rules and metadata for fields, and you can also nest models to handle complex JSON data structures.


Using Field to Declare Field Validation

Used in path operation functionsQuery、PathThe way to declare validation is the same; inside Pydantic models you useFieldDeclare field validation:

Example

from typing import Annotated
from fastapi import FastAPI
from pydantic import BaseModel, Field

app = FastAPI()


class Item(BaseModel):
    name: str = Field(min_length=1, max_length=100, description="Product Name")  # Required, 1-100 characters
    description: str | None = Field(default=None, max_length=300, description="Product Description")  # Optional
    price: float = Field(gt=0, description="Product Price")  # Required, must be greater than 0
    tax: float | None = Field(default=None, ge=0, description="Tax")  # Optional, >= 0


@app.post("/items/")
async def create_item(item: Item):
    return item

FieldSupported validation parameters:

Parameter TypeParameterDescription
String validationmin_lengthMinimum length
max_lengthMaximum length
patternRegular expression matching
Numeric validationgtgreater than
gegreater than or equal to
ltless than
leless than or equal to
metadatatitleField title
descriptionField description

NoteFieldfrompydanticImport, rather than fromfastapiImport. This isQuery、Pathetc. fromfastapiImport differs.


List type field

You can declare a field as a list and specify the element type:

Example

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    description: str | None = None
    price: float
    tax: float | None = None
    tags: list[str] = []  # String list, default empty list


@app.post("/items/")
async def create_item(item: Item):
    return item

Request body example:

{
    "name": "Foo",
    "price": 42.0,
    "tags": ["rock", "metal", "bar"]
}

Use Set to deduplicate

If tags should not be duplicated, usesetType:

Example

class Item(BaseModel):
    name: str
    description: str | None = None
    price: float
    tax: float | None = None
    tags: set[str] = set()  # Automatically remove duplicate tags

Even if the request contains duplicate tags, they will be automatically deduplicated in the response.


Nested model

A Pydantic model's field type can be another Pydantic model, supporting nesting to any depth:

Example

from fastapi import FastAPI
from pydantic import BaseModel, HttpUrl

app = FastAPI()


# Sub-model: Image
class Image(BaseModel):
    url: HttpUrl      # Automatically validate whether it is a valid URL
    name: str


# Main model: Product, includes image submodel
class Item(BaseModel):
    name: str
    description: str | None = None
    price: float
    tax: float | None = None
    tags: set[str] = set()
    image: Image | None = None  # Optional image information


@app.post("/items/")
async def create_item(item: Item):
    return item

Corresponding request body structure:

{
    "name": "Foo",
    "description": "The pretender",
    "price": 42.0,
    "tax": 3.2,
    "tags": ["rock", "metal", "bar"],
    "image": {
        "url": "http://example.com/baz.jpg",
        "name": "The Foo live"
    }
}

Submodel in a list

You can put sub-models in a list:

Example

from fastapi import FastAPI
from pydantic import BaseModel, HttpUrl

app = FastAPI()


class Image(BaseModel):
    url: HttpUrl
    name: str


class Item(BaseModel):
    name: str
    description: str | None = None
    price: float
    tax: float | None = None
    tags: set[str] = set()
    images: list[Image] | None = None  # Image List


@app.post("/items/")
async def create_item(item: Item):
    return item

Corresponding request body:

{
    "name": "Foo",
    "price": 42.0,
    "images": [
        {"url": "http://example.com/baz.jpg", "name": "The Foo live"},
        {"url": "http://example.com/dave.jpg", "name": "The Baz"}
    ]
}

Deeply nested models

You can define nested structures of any depth:

Example

from fastapi import FastAPI
from pydantic import BaseModel, HttpUrl

app = FastAPI()


class Image(BaseModel):
    url: HttpUrl
    name: str


class Item(BaseModel):
    name: str
    description: str | None = None
    price: float
    tax: float | None = None
    tags: set[str] = set()
    images: list[Image] | None = None


class Offer(BaseModel):
    name: str
    description: str | None = None
    price: float
    items: list[Item]  # Offer contains multiple Items, and an Item contains multiple Images


@app.post("/offers/")
async def create_offer(offer: Offer):
    return offer

Pure list request body

If the outermost layer of the request body is a JSON array, you can directly declare a list type in the function parameter:

Example

from fastapi import FastAPI
from pydantic import BaseModel, HttpUrl

app = FastAPI()


class Image(BaseModel):
    url: HttpUrl
    name: str


# Request body is directly a list of Images
@app.post("/images/multiple/")
async def create_multiple_images(images: list[Image]):
    return images

Request body composed of an arbitrary dict

When you need to receive dictionary data with uncertain key names, you can declaredictRequest body of type:

Example

from fastapi import FastAPI

app = FastAPI()


# Receive a dict with int keys and float values
@app.post("/index-weights/")
async def create_index_weights(weights: dict[int, float]):
    return weights

Request body example:

{
    "1": 0.5,
    "2": 1.5,
    "3": 2.0
}

JSON only supports string-type keys, but Pydantic will automatically convert string keys to the declared type (such as theint)。


Summary

  • Using Pydantic'sFieldAdd validation and metadata to model fields
  • list[str]、set[str]Collection types such as these can be declared as list/set fields
  • Models can be nested, supporting JSON structures of any depth
  • HttpUrland other special types automatically validate the format
  • Plain lists and arbitrary dictionaries can also be used as request body types
other extensions