Skip to main content
FastAPI is designed around a simple idea:
Validate all incoming data, process it internally, and return only the data that clients should see.
Throughout this chapter, we’ll build a simple Employee Management System to understand how FastAPI validates and processes data. Our API supports:
  • Retrieve an employee
  • Search employees
  • Create an employee
Internally, an employee contains much more information than what the client sends or receives.

Request Processing Flow

Every request passes through multiple validation stages before reaching your business logic. FastAPI validates incoming data, your application performs the business logic, and FastAPI validates the outgoing response before sending it back to the client.

1. Path Parameter Validation

Path parameters identify a specific resource. Example request

What FastAPI validates

  • Must be an integer.
  • Must be greater than zero.
If the client sends
or
FastAPI automatically returns a 422 Unprocessable Entity response.

2. Query Parameter Validation

Query parameters are used to filter, search, sort, or paginate results. Example request

What FastAPI validates

  • department is optional.
  • limit must be between 1 and 100.

3. Request Body Validation

When creating or updating resources, clients send JSON in the request body. Example request
To validate this JSON, create a Request Model.
Use it in your endpoint.
FastAPI automatically:
  • Reads the JSON request body.
  • Validates the incoming data.
  • Creates an EmployeeCreate object.
  • Passes the validated object to the route function.
If validation fails, FastAPI immediately returns 422 Unprocessable Entity without executing your function.

4. Why Isn’t the Request Model Enough?

The Request Model represents only the data that the client is allowed to send. For simple applications, this is often enough.
However, a real application usually needs additional information. When creating an employee, the application may generate:
  • Employee ID
  • Employee Code
  • Tax ID
  • Joining Date
  • Created Timestamp
These values should never come from the client. Therefore, the request model is not the application’s complete working model.

5. Internal Model

After validation, the application creates its own internal model.
Business logic transforms the request model into the internal model.
The client never sends:
  • id
  • employee_code
  • tax_id
  • joined_at
These values are generated by the application.

6. Response Models

The application should not expose its internal model directly. Instead, create a Response Model containing only the fields that clients should receive.
Use it with response_model.
Although EmployeeInternal contains
  • salary
  • employee_code
  • tax_id
  • joined_at
the client only receives
FastAPI automatically filters the response using EmployeeResponse.

Complete Employee Lifecycle


Validation Tools

FastAPI provides different validation helpers depending on where the data comes from.

Field() – Request Body Validation

Used inside Pydantic models.

Query() – Query Parameter Validation


Path() – Path Parameter Validation


Annotated (Recommended)

In Pydantic v2, the recommended way to specify validation is using Annotated. Syntax
Example
Older syntax
Both work, but Annotated is now the recommended style.

Common Validation Options


Common Pydantic Types

Example
Note: EmailStr requires the email-validator package.

Summary

Best Practice: As your application grows, use separate models for Request, Internal Processing, and Response. Each model has a single responsibility:
  • Request Model → validates incoming client data.
  • Internal Model → represents the application’s complete working object.
  • Response Model → exposes only the data that clients should receive.

Understanding model_config and from_attributes in Pydantic v2

What is model_config?

model_config contains configuration settings that control how a Pydantic model behaves. In Pydantic v2, these settings are defined using ConfigDict.
Some common configuration options include:
  • from_attributes=True
  • extra="forbid"
  • validate_assignment=True
  • frozen=True
  • populate_by_name=True

What is from_attributes=True?

By default, Pydantic expects input data to be a dictionary.
Internally, Pydantic reads values using dictionary keys.
ORM libraries like SQLAlchemy return objects, not dictionaries.
Adding
tells Pydantic to read values from an object’s attributes instead of dictionary keys. Internally, it changes from
to
This is why from_attributes=True is commonly used in response models that receive ORM objects.

What is model_validate()?

model_validate() is a class method that validates input data and creates a Pydantic model.
It performs the following steps:
For example,
Output
Pydantic automatically converts "20" into 20. If validation fails, it raises a ValidationError.

Why use model_validate() instead of the constructor?

For a dictionary, both of these work:
The constructor (**data) only accepts keyword arguments (typically a dictionary). model_validate() is more flexible because it can create models from different kinds of input, such as:
  • Dictionaries
  • SQLAlchemy objects
  • SQLModel objects
  • Dataclasses
  • Existing Pydantic models
For example, if SQLAlchemy returns
this won’t work:
But this will:
If from_attributes=True is configured, Pydantic automatically reads the object’s attributes, validates them, and creates the model.

Typical FastAPI Flow

Pydantic v1 vs Pydantic v2

from_attributes=True replaces orm_mode=True in Pydantic v2.

Summary

  • model_config stores configuration settings for a Pydantic model.
  • ConfigDict is used to define those settings in Pydantic v2.
  • from_attributes=True tells Pydantic to read values from object attributes instead of dictionary keys.
  • model_validate() validates input data, converts compatible types, and returns a Pydantic model instance.
  • model_validate() is preferred over the constructor because it works with dictionaries and ORM objects.
  • from_attributes=True and model_validate() are commonly used together to convert SQLAlchemy objects into API response models.
  • Request models usually don’t need from_attributes=True; response models commonly do because they are built from ORM objects.