jsondecode.com logo

JSON in FastAPI — Pydantic Models, Request Bodies, and Response Serialization

FastAPI uses Pydantic models as the bridge between JSON and Python. When you declare a Pydantic model as a parameter, FastAPI automatically parses the JSON request body, validates it, and provides it as a typed Python object — with no boilerplate.

Defining Pydantic models for JSON request bodies

Declare a class that inherits from BaseModel. FastAPI reads the request body as JSON and validates it against the model automatically.

from fastapi import FastAPI
from pydantic import BaseModel, EmailStr
from typing import Optional

app = FastAPI()

class CreateUserRequest(BaseModel):
    name: str
    email: EmailStr
    age: Optional[int] = None

@app.post("/users")
async def create_user(user: CreateUserRequest):
    # user is fully typed and validated
    print(user.name, user.email)
    return {"id": 1, "name": user.name}

Returning JSON responses

Return any Pydantic model, dict, list, or primitive from a route function. FastAPI serialises it to JSON automatically using the response_model for filtering.

from pydantic import BaseModel

class UserResponse(BaseModel):
    id: int
    name: str
    email: str

@app.get("/users/{user_id}", response_model=UserResponse)
async def get_user(user_id: int):
    # response_model filters and validates the output shape
    return {"id": user_id, "name": "Alice", "email": "alice@example.com"}

# Return a list
@app.get("/users", response_model=list[UserResponse])
async def list_users():
    return [{"id": 1, "name": "Alice", "email": "alice@example.com"}]

Nested models and optional fields

Compose complex JSON shapes by nesting Pydantic models. Use Optional[T] (or T | None in Python 3.10+) for fields that may be absent.

from pydantic import BaseModel
from typing import Optional

class Address(BaseModel):
    street: str
    city: str
    country: str = "US"  # default value

class User(BaseModel):
    id: int
    name: str
    address: Optional[Address] = None  # nested, optional

@app.post("/users")
async def create_user(user: User):
    if user.address:
        print(user.address.city)
    return user

Custom JSON serializers

Override JSON serialisation for types that Pydantic doesn't handle by default using model_config and custom validators (Pydantic v2).

from pydantic import BaseModel
from decimal import Decimal

class Product(BaseModel):
    name: str
    price: Decimal

    model_config = {
        "json_encoders": {
            Decimal: lambda v: float(v),
        }
    }

product = Product(name="Widget", price=Decimal("9.99"))
print(product.model_dump_json())
# {"name":"Widget","price":9.99}

Handling datetime and UUID fields

Pydantic natively supports datetime and UUID types. FastAPI serialises datetime to ISO 8601 strings and UUID to hyphenated strings in JSON responses.

from fastapi import FastAPI
from pydantic import BaseModel
from datetime import datetime
from uuid import UUID, uuid4

app = FastAPI()

class Event(BaseModel):
    id: UUID
    name: str
    created_at: datetime

@app.get("/events/{event_id}")
async def get_event(event_id: UUID) -> Event:
    return Event(
        id=event_id,
        name="Launch",
        created_at=datetime(2026, 1, 15, 10, 30, 0),
    )
# Response: {"id":"...", "name":"Launch", "created_at":"2026-01-15T10:30:00"}

Frequently Asked Questions

How does FastAPI parse JSON request bodies?

Declare a Pydantic BaseModel subclass as a route parameter. FastAPI reads the Content-Type: application/json request body, parses it, and validates it against the model. If validation fails, it returns a 422 Unprocessable Entity response automatically.

How do I return a JSON response from a FastAPI route?

Return a Pydantic model instance, a dict, a list, or a primitive value from your route function. FastAPI serialises it to JSON. Use response_model= to declare the expected output shape and filter extra fields.

How do I handle optional JSON fields in FastAPI?

Declare the field as Optional[T] = None (or T | None = None in Python 3.10+). FastAPI and Pydantic treat the field as optional — it will be None if absent from the request body.

How do I serialise datetime objects to JSON in FastAPI?

Use datetime fields directly in your Pydantic model. FastAPI serialises them to ISO 8601 strings (e.g. '2026-01-15T10:30:00') automatically. No custom encoder is needed for standard datetime objects.

What is response_model in FastAPI?

response_model= on a route decorator tells FastAPI what shape the response should have. It filters out fields not in the model (useful for hiding internal fields like passwords) and validates the output. Set response_model_exclude_unset=True to omit unset optional fields.

Format and validate your JSON instantly

Free, no ads, no sign-up. Also converts JSON to TypeScript, YAML, CSV, and more.

Open JSON Formatter →

If jsondecode.com saved you time, share it with your team

Free forever. No ads. No sign-up. Help other developers find it.