5.6 KiB
Boat — Local Development Runbook
This runbook explains how to run the Boat MVP locally, seed demo data, and troubleshoot common issues.
Project layout highlights
- app.page() — Graph landing page that fetches /api/graph and renders the Cytoscape graph
- components/Graph.tsx — Cytoscape wrapper with zoom/pan, fit, and selection details
- components/PersonForm.tsx — Create person form
- components/PeopleSelect.tsx — Typeahead selector against /api/people
- components/ConnectionForm.tsx — Create connection form with ordered introducer chain
- app.people.new.page() — Page for adding a person
- app.connections.new.page() — Page for adding a connection
- api.people.route(), api.people.id.route() — People API
- api.connections.route(), api.connections.id.route() — Connections API
- api.graph.route() — Graph snapshot API
- lib.db() — Prisma client singleton
- lib.validators() — Zod schemas
- prisma.schema() — Data model
- prisma.migration.sql — Undirected uniqueness + self-edge constraints
- prisma.seed() — Seed script for 20 demo people and ~30 connections
Prerequisites
- Docker (to run local Postgres quickly)
- Node 20+ and npm
- Start Postgres (Docker) If you haven’t yet started the Docker Postgres container, run:
- docker volume create boat-pgdata
- docker run -d --name boat-postgres -p 5432:5432 -e POSTGRES_DB=boat -e POSTGRES_USER=boat -e POSTGRES_PASSWORD=boat -v boat-pgdata:/var/lib/postgresql/data postgres:16
Verify it’s running:
- docker ps | grep boat-postgres
-
Environment variables This repo already includes .env pointing to the Docker Postgres: DATABASE_URL="postgresql://boat:boat@localhost:5432/boat?schema=public"
-
Install deps and generate Prisma Client From the app directory:
- npm install
- npx prisma generate
- Migrate the database
- npx prisma migrate dev --name init
- npx prisma migrate dev (if new migrations were added)
Note: Constraints for undirected uniqueness and self-edges are included in prisma.migration.sql.
- Seed demo data (20 people)
- npm run db:seed
This executes prisma.seed() and inserts people and connections.
- Run the Next.js dev server Important: run from the app directory, not workspace root.
- npm run dev
Access:
- http://localhost:3000 (or the alternate port if 3000 is busy)
- Using the app
- Landing page shows the global graph
- Toolbar buttons:
- Add Person → app.people.new.page()
- Add Connection → app.connections.new.page()
- Reload — refetches data for the graph
- Click nodes or edges to show details in the side panel
- API quick tests
- List people:
- Create person:
- curl -X POST "http://localhost:3000/api/people" -H "Content-Type: application/json" -d '{"name":"Jane Demo","sectors":["agriculture"]}'
- Create/update connection (undirected):
- curl -X POST "http://localhost:3000/api/connections" -H "Content-Type: application/json" -d '{"personAId":"","personBId":"","introducedByChain":[],"eventLabels":["event:demo"]}'
Troubleshooting
A) “npm enoent Could not read package.json at /home/maxime/boat/package.json”
- You ran npm in the workspace root. Use the app directory:
- cd boat-web
- npm run dev
B) “Unable to acquire lock … .next/dev/lock”
- Another Next dev instance is running or a stale lock exists.
- Kill dev: pkill -f "next dev" (Unix)
- Remove lock: rm -f .next/dev/lock
- Then: npm run dev
C) “Failed to load external module @prisma/client … cannot find module '.prisma/client/default'”
- Prisma client must be generated after schema changes or misconfigured generator.
- Ensure generator in prisma.schema() is: generator client { provider = "prisma-client-js" }
- Regenerate: npx prisma generate
- If still failing, remove stale output and regenerate:
- rm -rf node_modules/.prisma
- npx prisma generate
D) Port 3000 already in use
- Run on a different port:
- npm run dev -- -p 3001
Tech notes
- The undirected edge uniqueness is enforced via functional unique index on LEAST/GREATEST and a no-self-edge CHECK in prisma.migration.sql.
- Deleting a person cascades to connections (MVP behavior).
- Sectors, interests, and eventLabels are free-text arrays (TEXT[]).
- Introduced-by chain is an ordered list of person IDs (existing people only).
- UI intentionally minimal and open as per MVP brief.
Acceptance checklist mapping
- Create person: api.people.route() + PersonForm.tsx ✔
- Create connection: api.connections.route() + ConnectionForm.tsx ✔
- Global graph view: api.graph.route() + Graph.tsx ✔
- Persistence: Postgres via Prisma ✔