Skip to content

Local setup ​

Prerequisites ​

  • Node 22+
  • PostgreSQL 15+, local. The double-booking constraint is PostgreSQL-only, so a local Postgres is required — SQLite or a mock will not do.

Backend ​

sh
cd backend
cp .env.example .env         # then edit — see below
npm install
npm run db:deploy            # apply migrations to DATABASE_URL
npm run db:seed              # admin user, settings, Mon–Fri hours, demo catalogue
npm run dev                  # API on http://localhost:4000

src/env.ts validates the environment at boot and is the authoritative list; .env.example documents every variable. What matters locally:

VariableNotes
DATABASE_URLLocal PostgreSQL. DIRECT_URL is the unpooled URL the Prisma CLI migrates through — set it when DATABASE_URL is pooled (Neon).
TEST_DATABASE_URLLocal database for the test suite. Required; the schema tests truncate tables.
SESSION_SECRET≥ 32 characters. Signs admin JWTs and derives payment tokens.
WEB_ORIGINThe frontend origin; CORS allowlist and emailed links. Defaults to http://localhost:5173.
ADMIN_EMAIL, ADMIN_PASSWORDRead once by the seed to create the single admin.
Payment / email keysOptional locally. With no mail key, emails are written to MAIL_OUTPUT_DIR instead of sent, so you can open the links. Leave payment credentials unset (attempts are recorded and fail at once) or use sandbox credentials.

Never point development or tests at production

backend/.env may hold a production (Neon) DATABASE_URL. For anything that runs the app or the tests, DATABASE_URL, DIRECT_URL and TEST_DATABASE_URL must be a local Postgres. The schema tests truncate tables, and the seed writes rows — neither belongs anywhere near production data.

Never run prisma db push

It reconciles the database to schema.prisma and would silently drop the booking_no_overlap exclusion constraint, which Prisma does not model. Every schema change goes through prisma migrate dev --create-only plus hand-edited SQL. See Data model.

Frontend ​

Run the backend first, then:

sh
cd frontend
npm install
npm run dev                  # http://localhost:5173, proxying /api to :4000

VITE_API_BASE_URL points /api calls at the backend. It is the only VITE_* variable and ships in the bundle — no secret belongs in frontend config.

Docs site ​

sh
cd docs-site
npm install
npm run dev                  # bundles the OpenAPI spec, then serves the docs

Everyday commands ​

InCommandDoes
backendnpm run devAPI with reload.
backendnpm run db:migrateCreate/apply a migration in development.
backendnpm testVitest against TEST_DATABASE_URL.
backendnpm run typecheck / npm run linttsc --noEmit / ESLint.
frontendnpm run dev / build / previewVite.
frontendnpm test / npm run test:e2eVitest / Playwright.
docs-sitenpm run dev / buildVitePress.
docs-sitenpm run api:lintRedocly lint of the spec.

First run ​

After db:seed, the console prints the demo booking's client link. Log in to the admin panel at http://localhost:5173/admin/login with ADMIN_EMAIL / ADMIN_PASSWORD. See Testing for how the suites bootstrap their own database.