Switch from PGlite (WebAssembly) to embedded-postgres for zero-config local development — provides a real PostgreSQL server with full compatibility. Add startup banner with config summary on server boot. Improve server bootstrap with auto port detection, database creation, and migration on startup. Update DATABASE.md, DEVELOPING.md, and SPEC-implementation.md to reflect the change. Update CLI database check and prompts. Simplify OnboardingWizard database options. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
3.3 KiB
Database
Paperclip uses PostgreSQL via Drizzle ORM. There are three ways to run the database, from simplest to most production-ready.
1. Embedded PostgreSQL — zero config
If you don't set DATABASE_URL, the server automatically starts an embedded PostgreSQL instance and manages a local data directory.
pnpm dev
That's it. On first start the server:
- Creates a
./server/data/embedded-postgres/directory for storage - Ensures the
paperclipdatabase exists - Runs migrations automatically for empty databases
- Starts serving requests
Data persists across restarts in ./server/data/embedded-postgres/. To reset local dev data, delete that directory.
This mode is ideal for local development and one-command installs.
2. Local PostgreSQL (Docker)
For a full PostgreSQL server locally, use the included Docker Compose setup:
docker compose up -d
This starts PostgreSQL 17 on localhost:5432. Then set the connection string:
cp .env.example .env
# .env already contains:
# DATABASE_URL=postgres://paperclip:paperclip@localhost:5432/paperclip
Run migrations (once the migration generation issue is fixed) or use drizzle-kit push:
DATABASE_URL=postgres://paperclip:paperclip@localhost:5432/paperclip \
npx drizzle-kit push
Start the server:
pnpm dev
3. Hosted PostgreSQL (Supabase)
For production, use a hosted PostgreSQL provider. Supabase is a good option with a free tier.
Setup
- Create a project at database.new
- Go to Project Settings > Database > Connection string
- Copy the URI and replace the password placeholder with your database password
Connection string
Supabase offers two connection modes:
Direct connection (port 5432) — use for migrations and one-off scripts:
postgres://postgres.[PROJECT-REF]:[PASSWORD]@aws-0-[REGION].pooler.supabase.com:5432/postgres
Connection pooling via Supavisor (port 6543) — use for the application:
postgres://postgres.[PROJECT-REF]:[PASSWORD]@aws-0-[REGION].pooler.supabase.com:6543/postgres
Configure
Set DATABASE_URL in your .env:
DATABASE_URL=postgres://postgres.[PROJECT-REF]:[PASSWORD]@aws-0-[REGION].pooler.supabase.com:6543/postgres
If using connection pooling (port 6543), the postgres client must disable prepared statements. Update packages/db/src/client.ts:
export function createDb(url: string) {
const sql = postgres(url, { prepare: false });
return drizzlePg(sql, { schema });
}
Push the schema
# Use the direct connection (port 5432) for schema changes
DATABASE_URL=postgres://postgres.[PROJECT-REF]:[PASSWORD]@...5432/postgres \
npx drizzle-kit push
Free tier limits
- 500 MB database storage
- 200 concurrent connections
- Projects pause after 1 week of inactivity
See Supabase pricing for current details.
Switching between modes
The database mode is controlled by DATABASE_URL:
DATABASE_URL |
Mode |
|---|---|
| Not set | Embedded PostgreSQL (./server/data/embedded-postgres/) |
postgres://...localhost... |
Local Docker PostgreSQL |
postgres://...supabase.com... |
Hosted Supabase |
Your Drizzle schema (packages/db/src/schema/) stays the same regardless of mode.