A production-ready ORM for FastAPI with async support, automatic Pydantic integration, and Django-like syntax
Project description
FastAPI ORM
A production-ready ORM library built on SQLAlchemy 2.x with full async support, automatic Pydantic integration, and Django-like syntax designed specifically for FastAPI applications.
Why FastAPI ORM?
FastAPI ORM combines the power of SQLAlchemy 2.x with the simplicity of Django's ORM, providing:
- ⚡ Truly Async - Built from the ground up with asyncio, not a wrapper
- 🎯 Django-like Syntax - Clean, intuitive model definitions
- 🔄 Automatic Pydantic - Seamless FastAPI response serialization with
.to_response() - 💉 FastAPI Native - Works perfectly with dependency injection
- 🛡️ Type-Safe - Fully typed with excellent IDE support
- 🚀 Production-Ready - Multi-tenancy, audit logging, caching, and more
Quick Start
Installation
pip install sqlalchemy>=2.0.0 fastapi>=0.100.0 pydantic>=2.0.0
pip install asyncpg aiosqlite alembic uvicorn
Define Models
from fastapi_orm import Model, IntegerField, StringField, DateTimeField
class User(Model):
__tablename__ = "users"
id: int = IntegerField(primary_key=True)
username: str = StringField(max_length=100, unique=True, nullable=False)
email: str = StringField(max_length=255, unique=True, nullable=False)
is_active: bool = BooleanField(default=True)
created_at = DateTimeField(auto_now_add=True)
Use in FastAPI
from fastapi import FastAPI, Depends
from fastapi_orm import Database
from sqlalchemy.ext.asyncio import AsyncSession
app = FastAPI()
db = Database("sqlite+aiosqlite:///./app.db")
@app.on_event("startup")
async def startup():
await db.create_tables()
async def get_db():
async for session in db.get_session():
yield session
@app.post("/users")
async def create_user(
username: str,
email: str,
session: AsyncSession = Depends(get_db)
):
user = await User.create(session, username=username, email=email)
return user.to_response()
@app.get("/users/{user_id}")
async def get_user(user_id: int, session: AsyncSession = Depends(get_db)):
user = await User.get(session, user_id)
return user.to_response() if user else {"error": "User not found"}
@app.get("/users")
async def list_users(session: AsyncSession = Depends(get_db)):
users = await User.all(session)
return [user.to_response() for user in users]
Core Features
Complete CRUD Operations
# Create
user = await User.create(session, username="john", email="john@example.com")
# Read
user = await User.get(session, user_id)
all_users = await User.all(session)
active_users = await User.filter(session, is_active=True)
# Update
await user.update_fields(session, email="newemail@example.com")
# Delete
await user.delete(session)
Advanced Filtering
# Operators: gt, gte, lt, lte, contains, startswith, endswith, in
adults = await User.filter_by(session, age={"gte": 18})
johns = await User.filter_by(session, username={"contains": "john"})
admins = await User.filter_by(session, role={"in": ["admin", "moderator"]})
# Ordering
users = await User.filter_by(session, order_by=["-created_at", "username"])
# Pagination
result = await User.paginate(session, page=1, page_size=20)
# Returns: {"items": [...], "total": 100, "page": 1, "pages": 5}
Relationships
from fastapi_orm import ForeignKeyField, OneToMany, ManyToOne
class User(Model):
__tablename__ = "users"
id: int = IntegerField(primary_key=True)
posts = OneToMany("Post", back_populates="author")
class Post(Model):
__tablename__ = "posts"
id: int = IntegerField(primary_key=True)
author_id: int = ForeignKeyField("users")
author = ManyToOne("User", back_populates="posts")
# Use relationships
user = await User.get(session, 1)
for post in user.posts:
print(post.title)
Composite Primary Keys
from fastapi_orm import composite_primary_key, CompositeKeyMixin
class OrderItem(Model, CompositeKeyMixin):
__tablename__ = "order_items"
order_id: int = IntegerField()
product_id: int = IntegerField()
quantity: int = IntegerField()
__table_args__ = (composite_primary_key("order_id", "product_id"),)
@classmethod
def _composite_key_fields(cls):
return ("order_id", "product_id")
# Query by composite key
item = await OrderItem.get_by_composite_key(session, order_id=123, product_id=456)
Bulk Operations
# Bulk create (much faster than individual creates)
users = await User.bulk_create(session, [
{"username": "user1", "email": "user1@example.com"},
{"username": "user2", "email": "user2@example.com"},
{"username": "user3", "email": "user3@example.com"},
])
# Bulk update
await User.bulk_update(session, [
{"id": 1, "is_active": True},
{"id": 2, "is_active": False},
])
# Bulk delete
await User.bulk_delete(session, [1, 2, 3, 4, 5])
Soft Delete
from fastapi_orm import SoftDeleteMixin
class Post(Model, SoftDeleteMixin):
__tablename__ = "posts"
title: str = StringField(max_length=200)
# Soft delete (sets deleted_at timestamp)
await post.soft_delete(session)
# Restore
await post.restore(session)
# Query only active (not deleted)
active_posts = await Post.all(session)
# Query only deleted
deleted_posts = await Post.only_deleted(session)
Transactions
from fastapi_orm import transactional, atomic
# Using decorator
@transactional(session)
async def transfer_funds(from_id, to_id, amount):
from_user = await User.get(session, from_id)
to_user = await User.get(session, to_id)
await from_user.update_fields(session, balance=from_user.balance - amount)
await to_user.update_fields(session, balance=to_user.balance + amount)
# Using context manager
async with atomic(db) as session:
user = await User.create(session, username="john")
post = await Post.create(session, title="First", author_id=user.id)
Advanced Features
Caching
from fastapi_orm import QueryCache, DistributedCache
# In-memory cache
cache = QueryCache(ttl=300) # 5 minutes
@cache.cached(key="all_users")
async def get_all_users(session):
return await User.all(session)
# Distributed cache (Redis)
dist_cache = DistributedCache("redis://localhost:6379/0")
@dist_cache.cached(key="user_{user_id}")
async def get_user_cached(session, user_id: int):
return await User.get(session, user_id)
Multi-Tenancy
from fastapi_orm import TenantMixin, set_current_tenant
class Document(Model, TenantMixin):
__tablename__ = "documents"
title: str = StringField(max_length=200)
# Set tenant context
set_current_tenant(tenant_id=1)
# All queries automatically filtered by tenant
documents = await Document.all(session) # Only tenant 1's documents
Audit Logging
from fastapi_orm import AuditMixin, set_audit_user, get_audit_trail
class User(Model, AuditMixin):
__tablename__ = "users"
username: str = StringField(max_length=100)
# Set current user
set_audit_user(current_user_id)
# All changes automatically logged
user = await User.create(session, username="john")
await user.update_fields(session, username="john_updated")
# Retrieve audit trail
trail = await get_audit_trail(session, "User", user.id)
Field Validation
from fastapi_orm import EmailValidator, PasswordStrengthValidator
class User(Model):
__tablename__ = "users"
email: str = StringField(
max_length=255,
validators=[EmailValidator()]
)
password: str = StringField(
validators=[PasswordStrengthValidator(min_length=8)]
)
Read Replicas
from fastapi_orm import Database
db = Database(
"postgresql+asyncpg://user:pass@primary/db",
read_replicas=[
"postgresql+asyncpg://user:pass@replica1/db",
"postgresql+asyncpg://user:pass@replica2/db",
]
)
# Reads automatically distributed across replicas
users = await User.all(session) # Uses read replica
# Writes go to primary
user = await User.create(session, username="john") # Uses primary
Feature Highlights
Database Operations
- ✅ Full async CRUD (create, read, update, delete)
- ✅ Advanced query builder with operators
- ✅ Bulk create, update, delete
- ✅ Soft delete with restore
- ✅ Offset and cursor-based pagination
- ✅ Aggregations (count, sum, avg, max, min)
- ✅ Window functions (ROW_NUMBER, RANK, etc.)
Database Features
- ✅ Composite primary keys
- ✅ Composite unique constraints
- ✅ Check constraints
- ✅ Advanced indexing (composite, partial, GIN, covering)
- ✅ Full-text search (PostgreSQL)
- ✅ JSON/JSONB operations
- ✅ Database views
Performance
- ✅ In-memory query caching (TTL support)
- ✅ Distributed caching (Redis)
- ✅ Hybrid L1/L2 caching
- ✅ Read replica support with load balancing
- ✅ Connection pool monitoring
- ✅ Query streaming for large datasets
- ✅ Optimistic locking
Production Features
- ✅ Multi-tenancy (row-level isolation)
- ✅ Comprehensive audit logging
- ✅ Transaction management (@transactional, atomic())
- ✅ Rate limiting (multiple strategies)
- ✅ WebSocket support for real-time updates
- ✅ Circuit breaker pattern
- ✅ Automatic retry with exponential backoff
Developer Experience
- ✅ Field validators (email, URL, phone, credit card, etc.)
- ✅ Model factories for testing
- ✅ Database seeding utilities
- ✅ CLI tools (model generation, CRUD scaffolding)
- ✅ GraphQL integration (Strawberry)
- ✅ File upload handling (local, S3)
Documentation
- GitHub Repository: https://github.com/Alqudimi/FastApiOrm
- API Reference: https://github.com/Alqudimi/FastApiOrm/tree/main/doc/api
- Usage Guides: https://github.com/Alqudimi/FastApiOrm/tree/main/doc/usage-guide
- Examples: https://github.com/Alqudimi/FastApiOrm/tree/main/examples
- Changelog: See CHANGELOG_*.md files in repository
Requirements
- Python 3.11 or higher
- SQLAlchemy 2.0 or higher
- FastAPI 0.100 or higher
- Pydantic 2.0 or higher
Optional Dependencies
Install as needed:
# Distributed caching
pip install redis>=5.0.0
# WebSocket support
pip install websockets>=12.0
# GraphQL integration
pip install strawberry-graphql>=0.200.0
# File uploads and image processing
pip install aiofiles>=23.0.0 boto3>=1.28.0 pillow>=10.0.0
# All optional features
pip install redis websockets strawberry-graphql aiofiles boto3 pillow
Database Support
- ✅ PostgreSQL (via asyncpg) - Recommended for production
- ✅ SQLite (via aiosqlite) - Great for development
- ✅ MySQL (via aiomysql)
- ✅ Other SQLAlchemy-supported databases
License
MIT License - see LICENSE file for details
Author
Abdulaziz Al-Qadimi
- Email: eng7mi@gmail.com
- GitHub: @Alqudimi
Links
- Repository: https://github.com/Alqudimi/FastApiOrm
- Issues: https://github.com/Alqudimi/FastApiOrm/issues
- Documentation: https://github.com/Alqudimi/FastApiOrm/tree/main/doc
Support
For questions or issues:
- Open an issue on GitHub: https://github.com/Alqudimi/FastApiOrm/issues
- Email: eng7mi@gmail.com
Made with ❤️ by Abdulaziz Al-Qadimi
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file fastapi_orm-0.11.0.tar.gz.
File metadata
- Download URL: fastapi_orm-0.11.0.tar.gz
- Upload date:
- Size: 181.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a8c30c3a8547ead915d6b1455a479eac4a8185c1d0eaaa9dd73baa6edcf4797c
|
|
| MD5 |
8571caa9722345435c950d0496933c07
|
|
| BLAKE2b-256 |
692b8cba4a4cdbb96381ad82e03f702465a39fea7883f899fdf1a422a4b13fb9
|
File details
Details for the file fastapi_orm-0.11.0-py3-none-any.whl.
File metadata
- Download URL: fastapi_orm-0.11.0-py3-none-any.whl
- Upload date:
- Size: 143.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7d650c9be10fa6a4264f613622b2cfb2ead78030df0908102f87347253792931
|
|
| MD5 |
debb34193452ee6795f8afc4fdd737dc
|
|
| BLAKE2b-256 |
d51150337ef93d48909cf791a31769eae25e365fea2f5488e0f4942d93700e13
|