How to Use FastCRUD with Pydantic v2 Schemas: Complete Implementation Guide

FastCRUD is built exclusively for Pydantic v2, leveraging the model_dump() API and generic type aliases bound to BaseModel to provide type-safe CRUD operations with SQLAlchemy models.

FastCRUD is a modern Python library designed to accelerate FastAPI development by providing generic CRUD operations that integrate seamlessly with SQLAlchemy and Pydantic v2 schemas. Unlike legacy solutions that rely on Pydantic v1's dict() method, FastCRUD leverages the v2 model_dump() API for robust schema handling. This guide walks you through the architectural patterns, implementation files, and practical code examples needed to implement FastCRUD with Pydantic v2 schemas in your FastAPI applications.

Architectural Overview of FastCRUD with Pydantic v2

FastCRUD's architecture centers on strict Pydantic v2 compliance. The library uses generic type parameters to ensure that all schema inputs and outputs conform to Pydantic v2's BaseModel interface.

Core Type System and Generic Bindings

In fastcrud/types.py, FastCRUD defines generic type aliases that are explicitly bounded to Pydantic v2's BaseModel:

  • CreateSchemaType: Used for create() operations
  • UpdateSchemaType: Used for update() operations
  • SelectSchemaType: Used for get() and get_multi() return types
  • DeleteSchemaType: Used for soft-delete configurations

These aliases ensure that any schema passed to FastCRUD methods inherits from pydantic.BaseModel and supports the v2 API.

Schema Serialization Pipeline

When creating or updating records, FastCRUD converts Pydantic v2 schemas to dictionaries using the modern model_dump() method. In fastcrud/crud/fast_crud.py, the create() method calls object.model_dump() (lines 106-108) to extract a plain Python dictionary that can be passed directly to the SQLAlchemy model constructor.

This approach replaces the deprecated Pydantic v1 dict() method and ensures compatibility with Pydantic v2's serialization behavior, including custom serializers and computed fields.

Output Model Instantiation

FastCRUD supports returning Pydantic v2 models directly from database queries via the return_as_model parameter. When return_as_model=True and a schema_to_select is provided, FastCRUD instantiates the schema using the v2 constructor: schema_to_select(**out).

This pattern appears in fastcrud/crud/fast_crud.py within the get() and get_multi() methods (around lines 100-105), allowing you to work with fully-typed Pydantic objects throughout your application layer.

Implementing FastCRUD with Pydantic v2 Schemas in FastAPI

Integrating FastCRUD into a FastAPI application requires defining Pydantic v2 schemas that match your SQLAlchemy models, then wiring them into the generic FastCRUD class.

Defining Pydantic v2 Schemas

Create schemas that inherit from pydantic.BaseModel and use Pydantic v2 configuration patterns. The fastcrud/examples/user/schemas.py file demonstrates the expected structure:

from pydantic import BaseModel, ConfigDict
from datetime import datetime
from typing import Optional

class CreateUserSchema(BaseModel):
    name: str
    email: str
    is_active: bool = True

class ReadUserSchema(BaseModel):
    id: int
    name: str
    email: str
    created_at: datetime
    
    model_config = ConfigDict(from_attributes=True)

class UpdateUserSchema(BaseModel):
    name: Optional[str] = None
    email: Optional[str] = None

class DeleteUserSchema(BaseModel):
    pass

The model_config = ConfigDict(from_attributes=True) setting enables ORM mode, allowing Pydantic to read attributes from SQLAlchemy model instances when converting query results to schemas.

Configuring the FastCRUD Instance

Instantiate FastCRUD with generic type parameters that map your SQLAlchemy model to its corresponding Pydantic v2 schemas:

from fastcrud import FastCRUD
from fastcrud.examples.user.model import User  # SQLAlchemy model

from fastcrud.examples.user.schemas import (
    CreateUserSchema,
    ReadUserSchema,
    UpdateUserSchema,
    DeleteUserSchema,
)

# Generic signature: Model, CreateSchema, UpdateSchema, SelectSchema, DeleteSchema

UserCRUD = FastCRUD[User, CreateUserSchema, UpdateUserSchema, None, DeleteUserSchema]
user_crud = UserCRUD(User)

The None value for the fourth parameter indicates we will specify the select schema at call time rather than baking it into the instance.

Auto-Generating CRUD Endpoints

Use crud_router from fastcrud/endpoint/crud_router.py to automatically create FastAPI routes that validate requests and responses using your Pydantic v2 schemas:

from fastcrud import crud_router
from fastapi import FastAPI

router = crud_router(
    session=get_db,  # Your async session dependency

    model=User,
    create_schema=CreateUserSchema,
    read_schema=ReadUserSchema,      # Used for GET /users/{id}

    update_schema=UpdateUserSchema,
    delete_schema=DeleteUserSchema,
    path="/users",
    tags=["users"],
)

app = FastAPI()
app.include_router(router)

This router automatically handles the conversion between Pydantic v2 schemas and SQLAlchemy models using FastCRUD's internal model_dump() calls.

Complete FastAPI Application Example

Below is a minimal, asynchronous FastAPI application demonstrating the full lifecycle of FastCRUD with Pydantic v2 schemas, including database setup and automatic endpoint generation.


# app.py

import datetime
from fastapi import FastAPI, Depends
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine, async_sessionmaker

from fastcrud import FastCRUD, crud_router
from fastcrud.examples.user.model import User  # SQLAlchemy model

from fastcrud.examples.user.schemas import (
    CreateUserSchema,
    ReadUserSchema,
    UpdateUserSchema,
    DeleteUserSchema,
)

# -------------------- Database setup --------------------

DATABASE_URL = "sqlite+aiosqlite:///./test.db"
engine = create_async_engine(DATABASE_URL, echo=True)
AsyncSessionLocal = async_sessionmaker(engine, expire_on_commit=False)

async def get_db() -> AsyncSession:
    async with AsyncSessionLocal() as session:
        yield session

# -------------------- FastCRUD instance --------------------

UserCRUD = FastCRUD[User, CreateUserSchema, UpdateUserSchema, None, DeleteUserSchema]
user_crud = UserCRUD(User)

# -------------------- FastAPI router --------------------

router = crud_router(
    session=get_db,
    model=User,
    create_schema=CreateUserSchema,
    read_schema=ReadUserSchema,
    update_schema=UpdateUserSchema,
    delete_schema=DeleteUserSchema,
)

# -------------------- Application --------------------

app = FastAPI()
app.include_router(router, prefix="/users", tags=["users"])

This example demonstrates how FastCRUD handles the entire data flow: Pydantic v2 schemas validate incoming requests, model_dump() converts them to dictionaries for SQLAlchemy, and return_as_model instantiates schemas from database rows.

Advanced Query Patterns with Pydantic v2 Schemas

FastCRUD extends beyond basic CRUD operations by supporting complex filtering, pagination, and relationship handling while maintaining Pydantic v2 compatibility.

Filter Operators and Joined Models

FastCRUD automatically translates filter operators into SQLAlchemy expressions. You can use double-underscore notation to apply operators like __contains, __eq, __gt, and even traverse relationships:


# Filter by substring

users = await user_crud.get_multi(
    db,
    email__contains="example.com",
    return_as_model=True,
    schema_to_select=ReadUserSchema,
)

# Filter across relationships (automatic JOIN handling)

items = await item_crud.get_multi(
    db,
    profile__company__name__eq="Acme",
    return_as_model=True,
    schema_to_select=ReadItemSchema,
)

This filtering logic is implemented in fastcrud/core/query/builder.py, which parses the filter keys, validates the relationship paths, and constructs the necessary SQLAlchemy JOINs.

Soft Delete Configurations

For applications requiring soft deletes, FastCRUD accepts a DeleteSchemaType that can include audit fields. Define a deletion schema and configure it in your CRUD instance:

from pydantic import BaseModel
from datetime import datetime

class DeleteUserSchema(BaseModel):
    deleted_at: datetime
    is_deleted: bool = True

# Use in FastCRUD generic parameters

UserCRUD = FastCRUD[User, CreateUserSchema, UpdateUserSchema, None, DeleteUserSchema]

The deletion logic references fastcrud/core/config/crud_configs.py for configuration structures, allowing you to inject timestamps or user IDs during soft-delete operations.

Key Source Files for FastCRUD Pydantic v2 Integration

Understanding the source code helps when debugging schema conversion issues or extending functionality. These files contain the core Pydantic v2 integration points:

  • fastcrud/crud/fast_crud.py – Contains the generic FastCRUD class implementation. Look here for the model_dump() calls on lines 106-108 during create operations, and the schema_to_select(**out) instantiation pattern in get() and get_multi() methods around lines 100-105.

  • fastcrud/types.py – Defines the generic type aliases CreateSchemaType, UpdateSchemaType, SelectSchemaType, and DeleteSchemaType (lines 14-18), all explicitly bounded to Pydantic v2's BaseModel.

  • fastcrud/examples/user/schemas.py – Reference implementation showing concrete Pydantic v2 schemas (CreateUserSchema, ReadUserSchema, UpdateUserSchema, DeleteUserSchema) with proper ConfigDict usage.

  • fastcrud/endpoint/crud_router.py – Implements the crud_router function that automatically generates FastAPI endpoints from your Pydantic v2 schemas and FastCRUD instance.

  • fastcrud/core/query/builder.py – Handles the parsing of filter operators (like __contains, __eq) and automatic JOIN construction for relationship filtering.

Summary

FastCRUD provides a type-safe, generic CRUD layer built specifically for Pydantic v2 and SQLAlchemy. Key implementation points include:

  • Generic type binding: CreateSchemaType, UpdateSchemaType, and related aliases in fastcrud/types.py enforce Pydantic v2 BaseModel constraints at the type level.

  • Modern serialization: The library uses model_dump() (not the deprecated dict()) to convert Pydantic v2 schemas to dictionaries before passing them to SQLAlchemy constructors, as seen in fastcrud/crud/fast_crud.py.

  • Automatic endpoint generation: The crud_router function in fastcrud/endpoint/crud_router.py consumes your Pydantic v2 schemas to generate fully-typed FastAPI endpoints with request validation and response serialization.

  • Advanced filtering: Support for Pydantic v2 schemas in filter operations, including relationship traversal via double-underscore notation (profile__company__name__eq), implemented in fastcrud/core/query/builder.py.

Frequently Asked Questions

Does FastCRUD support Pydantic v1?

No. FastCRUD is built exclusively for Pydantic v2. The library relies on the model_dump() method introduced in Pydantic v2 for schema serialization, and all generic type aliases in fastcrud/types.py are explicitly bounded to Pydantic v2's BaseModel. If you attempt to use Pydantic v1 schemas, you will encounter attribute errors when FastCRUD attempts to call model_dump().

How do I enable ORM mode for my Pydantic v2 schemas?

Add a model_config class attribute using ConfigDict with from_attributes=True to your schema definition. This allows Pydantic v2 to read attributes from SQLAlchemy model instances when FastCRUD converts database rows to schema instances via schema_to_select(**out). For example:

from pydantic import BaseModel, ConfigDict

class ReadUserSchema(BaseModel):
    id: int
    name: str
    
    model_config = ConfigDict(from_attributes=True)

Can I use custom Pydantic v2 validators with FastCRUD?

Yes. FastCRUD passes your Pydantic v2 schemas directly to the SQLAlchemy model constructors after calling model_dump(), but validation occurs when you instantiate the schema itself. You can use Pydantic v2's @field_validator and @model_validator decorators in your CreateSchemaType or UpdateSchemaType classes, and FastCRUD will respect these validations because it receives already-validated schema instances from FastAPI's dependency injection system.

What is the performance impact of using return_as_model?

When you set return_as_model=True and provide a schema_to_select parameter, FastCRUD instantiates Pydantic v2 models for every row returned by the database query. This adds a small serialization overhead compared to returning raw dictionaries, but provides full type safety and IDE autocomplete. For high-throughput endpoints where microseconds matter, set return_as_model=False and work with dictionaries, converting to Pydantic models only at the service layer when necessary.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →