FastAPI with SQLModel Project Structure
SQLModel integration—one model for DB and API. Less boilerplate, same Pydantic validation.
Project Directory
myproject/
main.py
App factory, lifespan
app/
Application code
__init__.py
config.py
Pydantic settings
core/
__init__.py
database.py
Engine, session
models/
SQLModel models
__init__.py
user.py
User, UserCreate, UserRead
item.py
link_tables.py
Many-to-many
api/
__init__.py
deps.py
Session dependency
v1/
__init__.py
router.py
users.py
items.py
services/
__init__.py
user_service.py
item_service.py
tests/
__init__.py
conftest.py
Test DB fixtures
api/
__init__.py
test_users.py
test_items.py
requirements.txt
requirements-dev.txt
.env.example
.gitignore
pyproject.toml
Why This Structure?
SQLModel unifies Pydantic and SQLAlchemy—one class defines both your database table and API schema. No more duplicate model definitions. Created by the same author as FastAPI, it's designed to feel native.
Key Directories
- app/models/-SQLModel classes—DB tables AND Pydantic schemas
- app/models/user.py-User (table), UserCreate, UserRead (schemas)
- app/api/deps.py-Session dependency for routes
- app/services/-Business logic with typed models
Getting Started
python -m venv venv && source venv/bin/activatepip install -r requirements.txtcp .env.example .envuvicorn main:app --reload- Tables auto-created via
SQLModel.metadata.create_all()
SQLModel Pattern
# app/models/user.py
from sqlmodel import SQLModel, Field
class UserBase(SQLModel):
email: str
name: str | None = None
class User(UserBase, table=True):
id: int | None = Field(default=None, primary_key=True)
class UserCreate(UserBase):
password: str
class UserRead(UserBase):
id: int
Table vs Schema Models
Use table=True only on the main model that maps to a database table. Create/Read/Update variants inherit from a base class without table=True. This keeps your API schemas clean while reusing field definitions.
When To Use This
- New FastAPI projects wanting minimal boilerplate
- Teams tired of syncing Pydantic schemas with SQLAlchemy models
- Projects with straightforward CRUD operations
- When you want one source of truth for data shape
- Prototypes that may grow into production apps
Trade-offs
- Less mature-Smaller ecosystem than raw SQLAlchemy
- Complex queries-May need to drop to SQLAlchemy for advanced cases
- No Alembic integration-Migrations require manual setup
Testing Strategy
- conftest.py-In-memory SQLite for fast tests
- tests/api/-Test endpoints with test client
- Model reuse-Same models in tests as production