FastAPI Basics: Build Your First Web API
Request Bodies and Validation
In this lesson you will
- Define a Pydantic model for a request body
- Add field constraints and read the errors
- Use a response model and the 201 status code
Introduction
For creating or changing data, clients send a request body, usually JSON, with POST, PUT or PATCH. FastAPI uses Pydantic models to describe the expected shape. A model is a Python class with typed fields, and Pydantic validates incoming data against it.
A request model
Python
from pydantic import BaseModel, Field
class BookIn(BaseModel):
title: str = Field(min_length=1, max_length=100)
year: int = Field(ge=1450, le=2100)
tags: list[str] = []titleandyearare required, because they have no default.Field(...)adds rules: a non-empty title up to 100 characters, a year in a sensible range.tagsis optional with an empty list as default. (Pydantic safely copies mutable defaults.)
Using it in an endpoint
Python
from fastapi import status
class BookOut(BaseModel):
id: int
title: str
year: int
BOOKS: dict[int, BookOut] = {}
@app.post("/books", response_model=BookOut, status_code=status.HTTP_201_CREATED)
def create_book(book: BookIn):
new_id = len(BOOKS) + 1
out = BookOut(id=new_id, title=book.title, year=book.year)
BOOKS[new_id] = out
return outA parameter whose type is a Pydantic model is read from the request body. Sending {"title": "Dune", "year": 1965} returned status 201 and:
JSON
{"id": 1, "title": "Dune", "year": 1965}Sending {"title": "", "year": 1000} returned status 422 with two errors, one per bad field:
JSON
{"detail": [
{"type": "string_too_short", "loc": ["body", "title"],
"msg": "String should have at least 1 character"},
{"type": "greater_than_equal", "loc": ["body", "year"],
"msg": "Input should be greater than or equal to 1450"}
]}(Some fields are omitted above for readability.) The loc list tells the client exactly which field to fix.
Quick quiz
Which status code is usually returned when a POST request creates a new resource?
Separate input and output models
Notice there are two models. BookIn is what the client may send, and BookOut is what we return. This is important:
- The client must not set the id. The server decides it.
- Later you may store things clients should never see, such as a password hash. A response model filters the output to only the declared fields, so sensitive extras never leak by accident.
The in-memory dictionary here resets whenever the server restarts and is only for learning. A later course uses a real database.
Mixing parameters
An endpoint can combine path, query and body parameters. FastAPI decides by type: path names are in the path template, simple types are query parameters, and Pydantic models are body.
Python
@app.put("/books/{book_id}")
def replace_book(book_id: int, book: BookIn, notify: bool = False):
...Common methods
| Method | Typical use |
|---|---|
| GET | read data, no side effects |
| POST | create a resource |
| PUT | replace a resource |
| PATCH | change part of a resource |
| DELETE | remove a resource |
Try it yourself
Add a rating field to BookIn that must be between 1 and 5 and is optional. Create a book with a rating of 9 in /docs and read the error.
Summary
- Pydantic models declare the expected JSON body, and FastAPI validates it before your code runs.
- Field constraints produce precise 422 errors that name the bad field.
- Use separate input and response models, and return 201 for created resources.
Lesson complete
+20 XP
Nice work. Keep going while it is fresh.
Sources and further reading