Database
This project uses PostgreSQL with Drizzle ORM for database management.
Overview
- Database: PostgreSQL 15
- ORM: Drizzle ORM
- Schema Management: better-auth generates auth tables automatically
- Migrations: Drizzle Kit
Quick Start
1. Set Up Database
Local Development (Docker):
# (Optional) copy Docker Compose env vars
cp .env.example .env
# Start PostgreSQL (and Redis if you're using the provided Compose stack)
docker-compose up postgres -d
# or:
docker-compose up postgres redis -d
# Check database is running
docker psManual Setup:
Install PostgreSQL 15 and create a database:
CREATE DATABASE mydatabase;2. Configure Environment
Create apps/api/.env (copy from .env.example):
DATABASE_URL="postgresql://postgres:postgres@localhost:5432/mydatabase"For Docker, the host is postgres. For local PostgreSQL, use localhost.
Drizzle Kit Commands
The API package includes convenient scripts for database management. Run these from the apps/api directory or use the --filter=api flag from the root.
Generate Migrations
From apps/api:
bun run db:generateFrom root:
bun --filter=api run db:generateDirect command:
bunx drizzle-kit generateGenerates SQL migration files based on schema changes in apps/api/drizzle/.
Apply Migrations
From apps/api:
bun run db:migrateFrom root:
bun --filter=api run db:migrateDirect command:
bunx drizzle-kit migrateApplies pending migrations to the database.
Push Schema
From apps/api:
bun run db:pushFrom root:
bun --filter=api run db:pushDirect command:
bunx drizzle-kit pushPushes schema changes directly to the database without migrations (good for development).
View Database
From apps/api:
bun run db:studioFrom root:
bun --filter=api run db:studioDirect command:
bunx drizzle-kit studioOpens Drizzle Studio - a database GUI at http://localhost:4983.
Migration Workflow
When you make changes to your database schema in apps/api/src/db/schema.ts, follow this workflow:
1. Update Schema
Edit your schema file:
// apps/api/src/db/schema.ts
export const user = pgTable("user", {
id: text("id").primaryKey(),
name: text("name").notNull(),
role: userRoleEnum("role").notNull().default("USER"), // New field
// ... other fields
});2. Generate Migration
cd apps/api
bun run db:generateThis creates a new SQL migration file in apps/api/drizzle/ with a timestamp and descriptive name.
3. Review Migration
Check the generated SQL file to ensure it matches your intended changes:
cat drizzle/0001_*.sql4. Apply Migration
Development:
bun run db:migrateProduction:
bun run db:migrateOr use db:push for quick prototyping (skips migration files):
bun run db:push5. Commit Migration
git add drizzle/
git commit -m "feat: add user role to schema"Configuration
Drizzle Config
The Drizzle configuration is at apps/api/drizzle.config.ts:
import type { Config } from "drizzle-kit";
export default {
schema: "./src/db/schema.ts",
out: "./drizzle",
dialect: "postgresql",
dbCredentials: {
url: process.env.DATABASE_URL || "",
},
} satisfies Config;Database Client
The database client is at apps/api/src/db/client.ts:
import { drizzle } from "drizzle-orm/postgres-js";
import postgres from "postgres";
const connectionString = process.env.DATABASE_URL || "";
const queryClient = connectionString ? postgres(connectionString) : ({} as any);
export const db = drizzle(queryClient);Local Development
Using Docker
The recommended approach for local development:
# Start database
docker-compose up postgres -d
# Check it's running
docker logs myapp-postgres
# Connect to database
docker exec -it myapp-postgres psql -U postgres -d mydatabaseConnection String
DATABASE_URL="postgresql://postgres:postgres@localhost:5432/mydatabase"- User:
postgres - Password:
postgres - Host:
localhost(orpostgreswhen running in Docker network) - Port:
5432 - Database:
mydatabase
Production
Database Setup
Provision PostgreSQL: Use a managed service like:
Get Connection String:
txtpostgresql://username:password@host:port/database?sslmode=requireSet Environment Variable:
bashexport DATABASE_URL="your-production-connection-string"Push Schema:
bashcd apps/api bunx drizzle-kit push
Security Considerations
- ✅ Use SSL/TLS connections (
?sslmode=require) - ✅ Use strong, random passwords
- ✅ Restrict database access to application servers only
- ✅ Enable connection pooling
- ✅ Set up regular backups
- ✅ Monitor database performance and logs
- ✅ Never commit
DATABASE_URLto version control
Troubleshooting
Can't Connect to Database
Check PostgreSQL is running:
bashdocker ps | grep postgresCheck connection string:
- Verify username, password, host, port, database name
- For Docker: use
postgresas host - For local: use
localhostas host
Check firewall: Ensure port 5432 is open
Schema Not Creating
Check DATABASE_URL is set:
bashecho $DATABASE_URLRun push command:
bashbunx drizzle-kit push:pgCheck for errors in the output
Data Not Persisting
Check Docker volumes:
bashdocker volume lsRestart containers:
bashdocker-compose restart postgres
Migrations vs Push
Use push for:
- ✅ Local development
- ✅ Rapid prototyping
- ✅ Quick schema changes
Use generate + migrations for:
- ✅ Production deployments
- ✅ Team collaboration
- ✅ Tracking schema history
- ✅ Rollback capability