Files
mbatchelder 73335484f8 feat: migrate from PocketBase to PostgreSQL with Drizzle ORM
- Add @project-e/db package with Drizzle schema and migrations
- Replace PocketBase client with PostgreSQL-based database client
- Migrate auth from custom to NextAuth.js
- Add Docker Compose with PostgreSQL container
- Update worker to use new database client
- Remove PocketBase-specific files and migrations
- Add drizzle config and initial migration
2026-07-24 07:08:29 -04:00

5.0 KiB

Development Guide

Use this guide to run Project E locally, change the database schema, and prepare a pull request.

Prerequisites

  • Node.js 22.13.0 or later
  • npm 10.0.0 or later
  • Docker and Docker Compose, for PostgreSQL 16
  • Git

Set up your local environment

  1. Clone the repository and install dependencies.

    git clone <repository-url>
    cd ProjectE
    npm install
    
  2. Copy the environment template.

    cp .env.example .env
    
  3. Set the database and authentication values in .env.

    DATABASE_URL=postgresql://project_e:your_postgres_password@localhost:5432/project_e
    POSTGRES_PASSWORD=your_postgres_password
    NEXTAUTH_SECRET=your_long_random_secret
    INITIAL_ADMIN_EMAIL=admin@example.com
    INITIAL_ADMIN_PASSWORD=your_initial_admin_password
    

    DATABASE_URL connects local processes to PostgreSQL. POSTGRES_PASSWORD must match the password in that URL. Generate NEXTAUTH_SECRET with openssl rand -base64 32.

  4. Start PostgreSQL 16.

    docker compose up db -d
    

    Docker initializes the project_e database and applies drizzle/0000_first_mauler.sql when it creates an empty database volume.

  5. Start the app.

    npm run dev
    
  6. Open http://localhost:3000 and sign in with INITIAL_ADMIN_EMAIL and INITIAL_ADMIN_PASSWORD.

    The credentials create the first admin account only when the users table has no accounts.

Project structure

project-e/
├── apps/web/                 # Next.js application
│   ├── app/                  # App Router pages and API routes
│   ├── components/           # React components
│   ├── hooks/                # Custom React hooks
│   └── lib/                  # Services, database adapter, and NextAuth config
├── packages/
│   ├── db/                   # Drizzle schema and PostgreSQL client
│   └── shared/               # Shared schemas, types, and constants
├── drizzle/                  # Generated PostgreSQL migrations
├── worker/                   # Background job worker
├── e2e/                      # Playwright tests
├── tests/                    # Unit and component tests
├── drizzle.config.ts         # Drizzle Kit configuration
└── docker-compose.yml        # Web, PostgreSQL, and worker services

Data access and authentication

The app uses Drizzle ORM with PostgreSQL. packages/db/src/schema.ts defines the schema, and packages/db/src/index.ts creates the database client from DATABASE_URL.

Application records live in the records table as JSONB data grouped by collection. The users table stores email addresses and bcrypt password hashes. API routes use the database adapter in apps/web/lib/database.ts for collection operations.

NextAuth uses the credentials provider. It creates JWT sessions after a user signs in with an email address and password. Keep NEXTAUTH_SECRET stable for an environment; changing it invalidates existing sessions.

Add a feature

  1. Define or update the data shape in packages/db/src/schema.ts.

  2. Generate a Drizzle migration.

    npm run db:generate
    
  3. Review and commit the generated SQL in drizzle/.

  4. Apply the migration to your local PostgreSQL database before testing. The initial Docker setup applies drizzle/0000_first_mauler.sql; apply later migrations through your deployment migration process.

  5. Update shared Zod schemas in packages/shared/ when validation changes.

  6. Update the relevant API route, service, state, and UI.

  7. Add tests for the new behavior.

Database schema changes

  • Do not edit a migration after another environment has applied it.
  • Keep the Drizzle schema and generated SQL in the same pull request.
  • Test a migration against a database with representative data.
  • Add indexes for fields used in common filters or sorts.
  • Back up production data before applying a migration.

drizzle.config.ts reads DATABASE_URL and writes generated migrations to drizzle/.

Testing

Run the checks that match your change:

npm run typecheck
npm run lint
npm run test
npm run test:e2e

Unit tests cover validation, utilities, services, and store actions. Component tests cover rendering and user interactions. Playwright tests cover browser flows such as signing in, creating a task, and completing it.

Code conventions

  • Use TypeScript strict mode.
  • Prefer unknown over any for external input.
  • Keep React components focused and extract shared logic into hooks.
  • Use Tailwind utility classes, cn() for class merging, and cva for variants.
  • Validate API input with shared Zod schemas.
  • Catch errors at API boundaries and log request context.

Git workflow

Use conventional commits and keep each commit focused. Before pushing, run the relevant checks and review git diff --stat.

Use these branch prefixes:

feature/description
fix/description
refactor/description
chore/description

Pull requests should explain the change, its reason, and the tests you ran. Include screenshots for UI changes.