Getting Started
A complete example from scratch using SQLModel. The approach is the same for SQLAlchemy and Tortoise — see ORM Adapters.
Installation
Model
from sqlmodel import Field, SQLModel
class Article(SQLModel, table=True):
id: int | None = Field(default=None, primary_key=True)
title: str
content: str
published: bool = False
No Pydantic schemas needed — they are generated automatically.
Session
from sqlalchemy.ext.asyncio import create_async_engine
from sqlmodel.ext.asyncio.session import AsyncSession
from collections.abc import AsyncGenerator
engine = create_async_engine("sqlite+aiosqlite:///db.sqlite3")
async def get_session() -> AsyncGenerator[AsyncSession, None]:
async with AsyncSession(engine) as session:
yield session
Wiring up CRUD
from fastapi import FastAPI
from fastapi_crud_generator import CRUDCollection
from fastapi_crud_generator.orm.sqlmodel import SQLModelAdapter
app = FastAPI()
crud = CRUDCollection(
orm_adapter=SQLModelAdapter(model=Article, get_session=get_session),
)
app.include_router(crud.get_router(prefix="/articles", tags=["articles"]))
What you get
Five endpoints with full OpenAPI docs at /docs:
| Method | URL | Body | Response |
|---|---|---|---|
GET |
/articles |
— | { page, per_page, count, data: [...] } |
GET |
/articles/{article_id} |
— | article object |
POST |
/articles |
{ title, content, published } |
created object |
PATCH |
/articles/{article_id} |
any fields (all optional) | null |
DELETE |
/articles/{article_id} |
— | deleted object |
Schemas
The adapter decides which fields go into each schema:
- public (GET responses) — all model fields including
id - create (POST) — all fields except
id(generated by the database) - update (PATCH) — same fields as create, but all optional
Filtering
Sorting
GET /articles?sort=title
GET /articles?sort=title:desc
GET /articles?sort=title:asc&sort=published:desc
Pagination
Next steps
- Schema Generation — control which fields appear in each schema
- Filtering & Sorting — custom filters
- Nested Resources — nested routes