Python for JS Devs · FastAPI
Zod, but built into the framework. Define a model once and get validation, parsing, serialization, and docs — all from type hints.
If you've used Zod or class-validator, Pydantic will feel instantly familiar — except it's the native currency of FastAPI. A BaseModel looks like a dataclass (Lesson 07) but validates at runtime.
const User = z.object({
email: z.string().email(),
name: z.string().min(2),
age: z.number().optional(),
});
User.parse(data);from pydantic import BaseModel, EmailStr, Field
class User(BaseModel):
email: EmailStr
name: str = Field(min_length=2)
age: int | None = None
User(**data) # validates & coerces, or raises ValidationErrorField(...), and the string one is min_length, not min. The AI-generated project you had wrote Field(min=5) — an invalid constraint that would blow up. Now you'll spot it instantly. (Numeric constraints are ge/le/gt/lt.)
Declare a model as a parameter and FastAPI reads it as the request body — parsing JSON, validating it, and handing you a typed object. No req.body, no manual checks.
@app.post("/users")
def create_user(user: User): # body validated against User
# `user` is a fully-typed, validated User instance here
return {"created": user.email}
Send bad JSON (missing email, name too short) and FastAPI returns a precise 422 with a field-by-field error list — generated from the model.
u = User(email="a@b.co", name="kd", age="30") # "30" coerced → 30
u.model_dump() # → dict (v1 was .dict())
u.model_dump_json() # → JSON string
User.model_validate(raw) # parse from an untrusted dict
.model_dump() / .model_dump_json() / .model_validate() (v1 used .dict() / .json() / .parse_obj()). If a tutorial uses the old names, it's Pydantic v1 — mentally translate.
Define the User model with name: str = Field(min_length=2) and an email: EmailStr (needs uv add "pydantic[email]"). Add the POST /users route, open /docs, and submit both a valid body and an invalid one (1-char name). Reading FastAPI's auto-generated 422 error is the win — you wrote no validation code.
Field(min_length=...). min is not a valid Pydantic constraint..model_dump(); .dict() is the v1 name.