Drizzle ORM — Schema Design & Best Practices
Drizzle ORM is a TypeScript-first ORM with SQL-like syntax, zero runtime overhead, and full type safety. It supports SQLite, PostgreSQL, MySQL, and more — each with driver-specific column types.badmin
SQLite admin panel — users, roles, RBAC permissions.
Cruise Ship
PostgreSQL business system — bookings, finance, cabin allocation.
cyop
PostgreSQL AI platform — requirements, tasks, captions.
SQLite Schema (badmin)
SQLite is great for admin panels and prototypes — zero infra cost, local file DB:SQLite stores timestamps as integers. Use
{ mode: "timestamp" } so Drizzle auto-converts Date ↔ unixepoch(). The sql(unixepoch())` default gives you a server-side timestamp without app logic.PostgreSQL Schema (Cruise Ship)
PostgreSQL gives you rich column types —numeric for money, jsonb for snapshots, text[] for arrays:
Key PostgreSQL Patterns
- CHECK constraints for enums — PostgreSQL doesn’t have native enums in Drizzle, so use
check()withANY (ARRAY[...]):
- Composite indexes — For multi-column queries, define indexes inline:
-
Array columns —
text("image_urls").array()for storing multiple URLs without a separate table: -
JSONB for snapshots —
jsonb("item_snapshot").default([]).notNull()for revision history:
Many-to-Many Relationships (RBAC)
RBAC needs user ↔ role ↔ permission many-to-many tables:Repository Pattern
Separate data access from business logic using repository interfaces:When loading a user for session checks, never include the password hash. Drizzle’s
columns filter lets you exclude sensitive fields at the query level, not just in the service layer.Migrations
Drizzle Kit generates SQL migrations from your schema:Seeding
Seed the database with initial roles, permissions, and an admin user:Best Practices
- SQLite for admin panels — Zero infra, local dev, good for small-to-medium row counts
- PostgreSQL for business data — Rich types (jsonb, arrays, CHECK constraints, numeric precision)
- CHECK constraints over app-level validation — Enforce enum values at the DB level
- Cascade deletes on M2M —
onDelete: "cascade"prevents orphaned association rows - Exclude sensitive columns —
columns: { passwordHash: false }in session queries - Repository pattern — Separate Drizzle queries from service/business logic
- Session versioning — Increment
sessionVersionto force logout - Seed with system roles — Always seed an admin role and initial permissions
References
- Drizzle ORM Docs
- Drizzle Kit CLI
- badmin schema — SQLite RBAC
- cruise-ship schema — PostgreSQL business
