PostgreSQL RLS transparent multitenancy via contextvars for FastAPI + SQLAlchemy
Project description
ulfblk-multitenant
PostgreSQL Row-Level Security (RLS) transparent multitenancy for FastAPI + SQLAlchemy. The developer never writes tenant filtering code manually.
Part of Bloques Reciclables - an open source ecosystem of reusable code blocks.
Installation
uv add ulfblk-multitenant
# or
pip install ulfblk-multitenant
How It Works
- TenantMiddleware extracts
tenant_idfrom the request (e.g., from JWT via auth middleware) - TenantContext stores it in a
contextvar(async-safe) - SQLAlchemy event listener injects
SET LOCAL app.current_tenanton each transaction - PostgreSQL RLS policies filter rows automatically
The developer writes normal queries - RLS handles isolation transparently.
Quick Start
1. Generate RLS SQL
from ulfblk_multitenant.rls import generate_rls_sql
# Generate and execute in your migration
sql = generate_rls_sql("orders", tenant_column="tenant_id")
# Enables RLS, creates tenant_isolation policy
2. Configure SQLAlchemy
from sqlalchemy.ext.asyncio import create_async_engine
from ulfblk_multitenant.rls import apply_rls
engine = create_async_engine("postgresql+asyncpg://...")
apply_rls(engine) # registers the event listener
3. Add Middleware
from fastapi import FastAPI
from ulfblk_multitenant.rls import TenantMiddleware
app = FastAPI()
# Default: reads tenant_id from request.state.tenant_id
app.add_middleware(TenantMiddleware)
# Or custom extractor:
app.add_middleware(
TenantMiddleware,
tenant_extractor=lambda req: req.headers.get("X-Tenant-ID"),
)
4. Use TenantContext Directly
from ulfblk_multitenant.context import get_current_tenant, set_current_tenant, clear_current_tenant
# Set manually (useful in background tasks, scripts)
ctx = set_current_tenant(
tenant_id="acme-123",
tenant_slug="acme",
tenant_name="ACME Corp",
)
# Read current tenant
tenant = get_current_tenant()
if tenant:
print(f"Current tenant: {tenant.tenant_id}")
# Clear
clear_current_tenant()
With ulfblk-auth
The typical setup combines ulfblk-auth JWT middleware with ulfblk-multitenant:
from fastapi import FastAPI
from ulfblk_core.middleware import RequestIDMiddleware
from ulfblk_multitenant.rls import TenantMiddleware, apply_rls
app = FastAPI()
# Middleware stack (order matters - last added runs first)
app.add_middleware(TenantMiddleware) # 3. Set tenant context from JWT
# ... auth middleware sets request.state.tenant_id ...
app.add_middleware(RequestIDMiddleware) # 1. Set request ID
# SQLAlchemy RLS
apply_rls(engine)
# Now all queries are automatically filtered by tenant
@app.get("/orders")
async def list_orders(session: AsyncSession = Depends(get_session)):
result = await session.execute(select(Order))
return result.scalars().all() # only current tenant's orders
Dependencies
- ulfblk-core
- SQLAlchemy 2.0+ (async)
- asyncpg
Requirements
- Python 3.11+
- PostgreSQL 14+ (RLS support)
License
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 ulfblk_multitenant-0.1.0.tar.gz.
File metadata
- Download URL: ulfblk_multitenant-0.1.0.tar.gz
- Upload date:
- Size: 4.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.10.11 {"installer":{"name":"uv","version":"0.10.11","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0d97da7cd4f2048386cb0bc22c44fd28339d37701d4d56cc8b4775792b56536e
|
|
| MD5 |
75375cae3a66aa3d15c5b7b916861eb7
|
|
| BLAKE2b-256 |
a350259408d58122b68921ac57970200e6558814ceec71fd1dd4ab4e35478bfb
|
File details
Details for the file ulfblk_multitenant-0.1.0-py3-none-any.whl.
File metadata
- Download URL: ulfblk_multitenant-0.1.0-py3-none-any.whl
- Upload date:
- Size: 6.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.10.11 {"installer":{"name":"uv","version":"0.10.11","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
be832c020d3dd984732c9454bbe0cfcb6c4647bd0463f4149ff57719ef0588e5
|
|
| MD5 |
4d3661acc38664fd390b4579997cf219
|
|
| BLAKE2b-256 |
338649e57ceeb65b3a7201f1da3d8a4e441bca9c7d0339ff4a66d0a32b1f63a1
|