Database Standards¶
Operational guides for working with PostgreSQL in our stack — migrations, connection management, monitoring, and implementation patterns.
For theory and deep knowledge (SQL fundamentals, query performance, indexing, transactions, backup), see Database Internals.
Overview¶
PostgreSQL is our primary database. We use it for:
- Relational data — Users, sessions, bookings, media metadata
- JSONB storage — Flexible schemas for configuration, preferences
- Full-text search — Native search without external services
- Geospatial queries — PostGIS for location-based features
Architecture¶
┌─────────────────┐ ┌──────────────────┐ ┌─────────────┐
│ FastAPI App │────▶│ SQLAlchemy │────▶│ PostgreSQL │
│ (async) │ │ (asyncpg) │ │ 15+ │
└─────────────────┘ └──────────────────┘ └─────────────┘
│
▼
┌──────────────────┐
│ Alembic │
│ (migrations) │
└──────────────────┘
When to Use What¶
| Need | Solution |
|---|---|
| Simple queries | SQLAlchemy ORM |
| Complex aggregations | Raw SQL via text() |
| Bulk inserts | COPY or executemany |
| Search | PostgreSQL full-text search |
| JSON data | JSONB columns |
| Geospatial | PostGIS extension |
Section Contents¶
Operations¶
- Migrations — Alembic deep dive, data migrations, rollbacks
- Connection Management — Pooling, asyncpg tuning
- Monitoring — pg_stat_*, query logging, alerting
Patterns¶
- Soft Deletes — Soft delete pattern with proper indexes
- Audit Trails — Change tracking patterns
- Full-Text Search — tsvector, GIN indexes, ranking
Reference¶
- Cheatsheet — Quick reference for common operations
Quick Start¶
Connect to Local Database¶
# Using psql
psql -h localhost -U postgres -d sartiq
# Using pgcli (better autocomplete)
pgcli -h localhost -U postgres -d sartiq
Run Migrations¶
# Apply all pending migrations
uv run alembic upgrade head
# Create new migration
uv run alembic revision --autogenerate -m "Add user preferences table"
# Rollback one migration
uv run alembic downgrade -1
Common Query Patterns¶
from sqlalchemy import select, func
from app.models import User, Booking
# Async query with SQLAlchemy
async def get_user_with_bookings(db: AsyncSession, user_id: int):
stmt = (
select(User)
.options(selectinload(User.bookings))
.where(User.id == user_id)
)
result = await db.execute(stmt)
return result.scalar_one_or_none()
# Aggregation
async def get_booking_stats(db: AsyncSession):
stmt = (
select(
func.count(Booking.id).label("total"),
func.sum(Booking.amount).label("revenue")
)
.where(Booking.status == "completed")
)
result = await db.execute(stmt)
return result.one()
Best Practices¶
- Always use parameterized queries — Never interpolate user input
- Index foreign keys — PostgreSQL doesn't auto-index them
- Use EXPLAIN before optimizing — Measure, don't guess
- Keep transactions short — Long transactions block others
- Prefer bulk operations — Batch inserts/updates when possible
Related Documentation¶
- Backend Standards — SQLAlchemy patterns and async practices
- Testing — Database testing strategies
- Database Internals — SQL fundamentals, query performance, indexing, transactions