Skip to content

Development Setup

Prerequisites

Tool Minimum Version Check Command
Node.js 20.0.0 node --version
pnpm 9.0.0 pnpm --version
Docker 24.0.0 docker --version
Docker Compose 2.20.0 docker compose version

First-Time Setup

# Clone the repo
git clone <repo-url>
cd meterplex

# Install dependencies
pnpm install

# Copy environment template
cp .env.example .env

# Start infrastructure
pnpm docker:up

# Wait for containers to be healthy (~15 seconds)
docker compose ps

# Run database migrations
pnpm prisma:migrate:dev

# Seed development data
pnpm prisma:seed

# Start the app
pnpm start:dev

Verify Everything Works

# Health check (should return status: ok)
curl http://localhost:3000/health | jq

# Swagger docs (open in browser)
open http://localhost:3000/api/docs

# Check database has seed data
docker compose exec postgres psql -U meterplex -d meterplex -c "SELECT name, slug FROM tenants"

Daily Workflow

# Start your day
pnpm docker:up          # Start containers (if stopped)
pnpm start:dev          # Start app with hot reload

# After changing schema.prisma
pnpm prisma:migrate:dev --name describe-your-change
pnpm prisma:generate    # Regenerate typed client

# Nuclear reset (drops all data, re-runs everything)
pnpm docker:down        # Removes containers AND volumes
pnpm docker:up
pnpm prisma:migrate:dev
pnpm prisma:seed

Common Issues

Port 5432 already in use

You have a local Postgres installation competing with Docker. Stop it:

# Homebrew
brew services stop postgresql@17

# Postgres.app
# Quit from menu bar

Prisma "exports is not defined in ES module scope"

The Prisma client was generated without moduleFormat = "cjs". Fix:

# Verify schema.prisma has moduleFormat = "cjs" in the generator block
pnpm prisma:generate

Prisma "role does not exist"

Docker volume has stale data from a previous run with different credentials:

pnpm docker:down   # -v flag removes volumes
pnpm docker:up
pnpm prisma:migrate:dev
pnpm prisma:seed

Environment Variables

All environment variables are documented in .env.example. The app validates every variable on startup — if any required variable is missing, the app crashes immediately with a clear error listing exactly what's missing.

See src/config/env.validation.ts for the validation schema.

Conventions

File Naming

  • Files: kebab-case.ts (e.g., correlation-id.middleware.ts)
  • Classes: PascalCase (e.g., CorrelationIdMiddleware)
  • Barrel exports: index.ts in each module folder

Import Aliases

Alias Resolves To
@common/* src/common/*
@config/* src/config/*
@modules/* src/modules/*

Database Conventions

  • Table names: snake_case plural (e.g., tenants)
  • Column names: snake_case (e.g., created_at)
  • Primary keys: UUID v4 (Postgres native uuid type)
  • Prisma models: PascalCase singular with @@map() for table name
  • Prisma fields: camelCase with @map() for column name