Pydantic

Pydantic validates and coerces data against typed models. Python’s type hints are ordinarily ignored at runtime, and Pydantic is the library that makes them enforce something: a model declares the shape data must have, and constructing one either produces a validated object or raises with a precise account of what was wrong.

Its right place is the boundary. Data arriving from outside — a request body, a config file, an environment variable, another service’s response — is untrusted and unshaped. Data inside the application should be neither. Pydantic is where the conversion happens, and putting it anywhere else means either validating repeatedly or trusting input that was never checked.

This is why FastAPI is built on it. Declaring an endpoint’s parameter as a Pydantic model gives request parsing, validation, error responses, and OpenAPI schema generation from one declaration, and the schema is what makes generated frontend clients possible. One definition, several consumers, no duplication to drift.

The v1 to v2 change

Pydantic v2 rewrote the validation core in Rust, which made it several times faster and changed enough API surface to make migration non-trivial. Validators use different decorators, config moved from an inner class to model_config, and several method names changed. Worth knowing which version any example assumes, because v1 material is still widely circulated and fails confusingly against v2.

The trade worth thinking about

Sharing one model across API contract, domain logic, and persistence is convenient and becomes a problem as a system grows. The API schema is a public contract that should change slowly and deliberately; the persistence model follows the database; the domain model follows the business rules. Collapsing them means a database column rename becomes an API breaking change.

For small applications the convenience wins clearly. For larger ones the separation is worth its boilerplate, and the transition point is easy to miss because nothing breaks — it just gets progressively harder to change anything. SQLModel deliberately collapses the API and persistence layers, which is exactly the trade it exists to offer.

The other caveat is that validation is only as good as the model. Declaring a field as str when it should be an enum, or dict when it should be a nested model, passes validation and checks nothing. Types treated as incidental annotation give the appearance of safety without the substance.

See also: FastAPI, SQLModel, APIs, and schemas.