| name | fastapi-sqlmodel-crud-patterns |
| description | Standard patterns for building and maintaining CRUD APIs with FastAPI and SQLModel: models, routers, database access, and error handling in a reusable way. |
FastAPI + SQLModel CRUD Patterns Skill
When to use this Skill
Use this Skill whenever you are:
- Creating or modifying CRUD (Create, Read, Update, Delete) endpoints in a FastAPI application that uses SQLModel for persistence.
- Designing new resources (e.g. Task, UserProfile, Project, Order) and their REST endpoints.
- Refactoring existing API code to be more consistent and reliable.
- Adding tests or changing database access patterns related to CRUD.
This Skill must work for any FastAPI + SQLModel project, not just a single repository.
Core goals
- Keep CRUD code consistent, predictable, and easy to reuse across many projects.
- Separate concerns:
- Models (SQLModel) in one place.
- Routers (FastAPI endpoints) in another.
- Database session management in a dedicated module.
- Use clear REST semantics (HTTP verbs, status codes, resource paths).
- Provide strong typing via SQLModel and Pydantic models.
- Handle errors and not-found cases cleanly, without crashes. [web:53][web:59]
Architecture assumptions
- Web framework: FastAPI.
- ORM: SQLModel (sync or async, but pick one style per project).
- Database: Any SQL database supported by SQLModel (e.g. PostgreSQL). [web:53][web:57]
- Structure:
db.pyor similar: session creation and engine.models.pyormodels/: SQLModel models.routers/orroutes/: FastAPI routers per resource.main.py: FastAPI app entrypoint registering routers.
The exact filenames can differ between projects; the patterns stay the same.
Resource and endpoint conventions
Each logical resource (e.g.
Task,Item,User) should have its own router module, for example:routers/tasks.pywith aAPIRouter(prefix="/tasks", tags=["tasks"]).
Typical REST endpoints per resource:
GET /<resource>→ list items.POST /<resource>→ create item.GET /<resource>/{id}→ get single item.PUT /<resource>/{id}→ replace item.PATCH /<resource>/{id}→ partial update (optional).DELETE /<resource>/{id}→ delete item. [web:53][web:59]
Resource names should be plural in paths (
/tasks,/users), with singular nouns used in model/type names (Task,User).
Models and schemas
Use SQLModel models for database tables, with:
- Primary key fields (
idor similar). - Optional timestamps (e.g.
created_at,updated_at) when useful. - Reasonable defaults and constraints (e.g.
nullable,max_length).
- Primary key fields (
When needed, define separate Pydantic/SQLModel schemas for:
- Create input (e.g.
TaskCreate) – fields required for creation. - Update input (e.g.
TaskUpdate) – optional fields for partial updates. - Response model (e.g.
TaskRead) – what the API returns.
- Create input (e.g.
Avoid exposing internal-only fields (e.g. secrets) in response models.
Database session handling
Provide a shared dependency for DB sessions, for example:
get_session()indb.pythat yields aSessionobject.
Use
Depends(get_session)in routers to access the database.Do not create database engines or sessions directly inside routers or endpoint functions. Keep connection logic centralized. [web:53][web:57]
CRUD behaviour patterns
For each resource, the default CRUD behaviour should follow this pattern:
Create (
POST):- Validate input using a dedicated schema if needed.
- Construct the SQLModel instance from the validated data.
- Add and commit the instance using the shared session.
- Refresh the instance to return updated fields (e.g. autoincrement id).
List (
GETcollection):- Return a list of items, optionally with pagination, filtering, or sorting based on query parameters.
- Avoid returning unbounded, huge result sets when possible.
Get (
GETsingle):- Fetch the item by primary key.
- If not found, raise
HTTPException(status_code=404)with a clear message.
Update (
PUT/PATCH):- Load the existing item; if not found, return 404.
- Apply allowed changes from the input schema.
- Commit and refresh before returning the updated item.
Delete (
DELETE):- Load the existing item; if not found, return 404.
- Either hard-delete or soft-delete depending on the project’s rules.
- Return appropriate status (e.g. 204 No Content for hard delete).
Error handling
Never let database or Python exceptions leak directly to clients. Use
HTTPExceptionwith appropriate status codes and simple, safe error messages. [web:53][web:59]Common error cases:
- Resource not found → 404.
- Validation errors → 422 (FastAPI will handle many of these).
- Unauthorized/forbidden (if auth is applied) → 401/403.
Log details server-side if needed, but keep responses simple.
Typing and response models
Always declare response models in router decorators when practical:
response_model=TaskReadorList[TaskRead].
This improves:
- OpenAPI docs.
- Type checking in clients.
- Clarity of what each endpoint returns.
Avoid returning raw dicts or mixing data shapes; keep responses consistent.
Filtering, sorting, and pagination (optional)
When adding filtering/sorting:
- Use query parameters (e.g.
status,sort_by,order). - Document default behaviours and limits in the resource spec.
- Use query parameters (e.g.
For pagination:
- Use standard patterns like
limitandoffsetor page/size pairs. - Enforce maximum limits to avoid performance issues.
- Use standard patterns like
Things to avoid
- Creating engines or sessions inside endpoint functions.
- Mixing business logic, validation, and database operations in large monolithic functions; prefer small helpers where appropriate.
- Returning raw SQLModel instances that include internal fields that should not be exposed.
- Using inconsistent status codes for the same error conditions.
References inside the repo
When present, this Skill should align with:
db.pyor equivalent – engine andget_sessiondependency.models.pyormodels/– SQLModel models for resources.routers/orroutes/– resource-specific routers.
If these files are missing, propose creating them using these patterns rather than inventing a new, ad-hoc CRUD style for each resource.