YouTip LogoYouTip

Fastapi Body Fields

FastAPI Request Body Fields and Nested Models

In Pydantic models, you can use Field to declare validation rules and metadata for fields. You can also nest models to handle complex JSON data structures.


Using Field to Declare Field Validation

Similar to using Query and Path for validation in path operation functions, you use Field inside Pydantic models to declare 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

Validation parameters supported by Field:

Parameter Type Parameter Description
String Validation min_length Minimum length
max_length Maximum length
pattern Regular expression match
Numeric Validation gt Greater than
ge Greater than or equal
lt Less than
le Less than or equal
Metadata title Field title
description Field description

Note: Field is imported from pydantic, not from fastapi. This is different from Query, Path, etc., which are imported from fastapi.


List Type Fields

You can declare fields as lists 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 = []  # String list, defaults to 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"]
}

Using Set for Deduplication

If tags should not be duplicated, use the set type:

Example

class Item(BaseModel):

 name: str

 description: str | None = None

 price: float

 tax: float | None = None

 tags: set = set()  # Automatically removes duplicate tags

Even if the request contains duplicate tags, the response will automatically deduplicate them.


Nested Models

The field type of a Pydantic model can be another Pydantic model, supporting arbitrary nesting depth:

Example

from fastapi import FastAPI

from pydantic import BaseModel, HttpUrl

app = FastAPI()

# Sub-model: Image

class Image(BaseModel):

 url: HttpUrl  # Automatically validates if it's a valid URL

 name: str

# Main model: Product, containing Image sub-model

class Item(BaseModel):

 name: str

 description: str | None = None

 price: float

 tax: float | None = None

 tags: set = 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"
    }
}

Sub-models in Lists

You can place sub-models in lists:

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 = set()

 images: list | 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 arbitrary 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 = set()

 images: list | None = None

class Offer(BaseModel):

 name: str

 description: str | None = None

 price: float

 items: list  # Offer contains multiple Items, and each 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 the 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 an Image list

@app.post("/images/multiple/")

async def create_multiple_images(images: list):

    return images

Arbitrary dict Request Body

When you need to receive dictionary data with uncertain key names, you can declare a dict type request body:

Example

from fastapi import FastAPI

app = FastAPI()

# Receives a dictionary 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 (like int in this example).


Summary

  • Use Pydantic's Field to add validation and metadata to model fields
  • Collection types like list, set can declare list/set fields
  • Models can be nested, supporting JSON structures of arbitrary depth
  • Special types like HttpUrl automatically validate formats
  • Pure lists and arbitrary dictionaries can also be used as request body types
← Fastapi Status CodeFastapi Query Params Validatio β†’