FastAPI cookbook · Advanced
Isolate tenants in a multi-tenant FastAPI app
Serve many customer organizations from one database without any data leaking between them.
Approach
Resolve the tenant from the authenticated user (never from an untrusted header alone), expose it through a dependency, and scope every query by tenant_id via repository helpers. Add PostgreSQL Row-Level Security as defense in depth, setting the tenant per transaction with SET LOCAL.
Solution
from sqlalchemy import text
async def get_tenant_db(user: CurrentUser) -> AsyncIterator[AsyncSession]:
async with SessionLocal() as session:
async with session.begin():
# RLS policies read this setting: USING (tenant_id = current_setting('app.tenant_id')::bigint)
await session.execute(text("SELECT set_config('app.tenant_id', :tid, true)"), {"tid": str(user.tenant_id)})
yield session
TenantDB = Annotated[AsyncSession, Depends(get_tenant_db)]
@router.get("/projects")
async def list_projects(db: TenantDB, user: CurrentUser) -> list[ProjectOut]:
stmt = select(Project).where(Project.tenant_id == user.tenant_id).order_by(Project.name) # app-level scoping too
return list(await db.scalars(stmt))
# Migration:
# ALTER TABLE projects ENABLE ROW LEVEL SECURITY;
# CREATE POLICY tenant_isolation ON projects USING (tenant_id = current_setting('app.tenant_id')::bigint);
# (connect as a role without BYPASSRLS; table owners bypass RLS unless FORCE ROW LEVEL SECURITY is set)
Pitfalls
- Trusting an
X-Tenant-Idheader without checking membership. - Background jobs and admin scripts forgetting tenant scoping.
- Connection poolers in transaction mode with session-level
SET(useSET LOCAL/set_config(..., true)).
Variations
- Schema-per-tenant or database-per-tenant for stronger isolation at higher operational cost.
Further reading: PostgreSQL: Row security policies
Reference: https://www.postgresql.org/docs/current/ddl-rowsecurity.html
More FastAPI recipes
- Add liveness and readiness endpoints to FastAPI
- Add OpenTelemetry tracing to FastAPI
- Add request IDs and structured logs to FastAPI
- Build a WebSocket chat endpoint in FastAPI
- Cache FastAPI responses in Redis
- Call external APIs with retries and timeouts from FastAPI
- Implement role-based access control in FastAPI
- Paginate with cursors in FastAPI