EEPNext.js Template
Dev Environment

Database

Local Postgres via Docker, migrations, seeding, and the smoke check.

Local setup

Local dev runs Postgres 17 in Docker, matching the schema and driver used in production. The connection string is pre-filled in .env.local_template:

DATABASE_URL=postgresql://postgres:postgres@localhost:5432/app
docker compose up -d    # start Postgres on :5432
docker compose down     # stop (keeps the data volume)
docker compose down -v  # stop and wipe all data

If Postgres is already running on :5432 (another project, a standalone container), docker compose up -d will fail silently. Check what's running with docker ps and stop the conflicting container first. This repo's container is named next-template-postgres.


Migrations

Migrations are generated from src/db/schema.ts using Drizzle Kit and live in drizzle/.

pnpm db:generate   # generate a new SQL migration from schema changes
pnpm db:migrate    # apply all pending migrations (idempotent, safe to re-run)

Always run pnpm db:migrate after a fresh clone or after pulling changes that include new migrations.


Seeding

pnpm db:seed

Clears example_items and inserts 3 fixed demo rows. Useful for resetting to a known state when testing the example CRUD feature. Skip if you have data you want to keep.


Smoke check

pnpm db:check

A fast, read-only probe of the full DATABASE_URL → pg.Pool → Drizzle → example_items path. Useful after a fresh clone or when the DB is misbehaving - it confirms the connection, driver, and schema are all wired correctly without mutating any data.


Studio

pnpm db:studio

Opens Drizzle Studio - a browser-based table viewer and editor. Useful for inspecting data during development.


Production

For production, replace DATABASE_URL with your hosted provider's connection string. Neon is the recommended option - see Neon Setup for the setup steps. No code changes are needed; only the env var changes.


Extending with caching

This template uses Postgres for all persistence. As your app grows you may reach a point where certain workloads benefit from a cache layer - common triggers are:

  • Rate limiting API routes (the most common early need)
  • Caching expensive queries to avoid redundant DB round-trips across serverless invocations
  • Background job queues via QStash

Upstash Redis is the natural fit for this stack. It is serverless-billed (pay per request, not per hour), connects over HTTP so it works correctly in Vercel's serverless environment, and has a native Vercel integration that injects env vars automatically. The setup mirrors Neon: create a database, add two env vars (UPSTASH_REDIS_REST_URL and UPSTASH_REDIS_REST_TOKEN), and you're connected.

When you add it, follow the same service pattern as everything else in classes/ - a classes/services/cache/ singleton wrapping @upstash/redis. Rate limiting drops into proxy.ts via @upstash/ratelimit without touching any other layer.

Don't add it until you have a concrete reason to. Postgres handles far more load than most early-stage products ever generate.

On this page