Skip to main content

Development

Local setup, common commands, testing strategy, debugging tips.


1. Prerequisites

ToolVersionPurpose
Node.js≥ 20.x LTSRuns the API
pnpm≥ 9Package management
Docker + Docker ComposecurrentRuns Mongo + Mongo Express
A REST clientanyTalking to the API (Bruno, Insomnia, httpie, curl)

No global Nest CLI is required — scripts in api/package.json invoke it via the local dependency.


2. First-time setup

From the repo root:

# 1. Copy env templates
cp .env.example .env
cp api/.env.example api/.env

# 2. Edit secrets (at minimum: MONGO_ROOT_PASSWORD, ME_PASSWORD,
# JWT_ACCESS_SECRET, JWT_REFRESH_SECRET once auth lands)

# 3. Start Mongo
docker compose up -d

# 4. Install API deps
cd api && pnpm install

# 5. Seed the database (once seed script exists)
pnpm seed

# 6. Run the API in watch mode
pnpm dev

The API listens on the port from PORT (default 4100). Mongo Express is at http://localhost:8083 using the credentials in the root .env.


3. Environment files

Two files, two purposes. Don't mix them.

FileConsumed byContains
/.envdocker-compose.ymlMongo root creds, Mongo Express creds, DB_NAME, APP_SLUG (for container naming)
/api/.envThe Nest process via ConfigModuleEverything the running API reads: PORT, MONGO_URI, APP_* branding, JWT_*, CORS_*

Full variable list: CONFIG.md.


4. Common commands

All run from api/:

CommandWhat it does
pnpm devStart the API with file watch (nest start --watch)
pnpm buildCompile TypeScript to dist/
pnpm startRun the compiled output (node dist/main.js)
pnpm seedSeed the database with roles, the default surface, the platform list, and a default group
pnpm testRun unit tests
pnpm test:e2eRun integration tests against a real Mongo
pnpm lintLint + typecheck (future)

From the repo root:

CommandWhat it does
docker compose up -dStart Mongo + Mongo Express
docker compose downStop containers (preserve volumes)
docker compose down -vStop and wipe the Mongo volume — destroys data
./scripts/verify.shEnd-to-end smoke test (curl-based)
./scripts/seed.shSeed demo roles + users
./scripts/reset.shNuke the volume and restart
./scripts/gen-docs.shRegenerate docs-site/docs/generated/ + docs-site/docs/api-reference/ from source

5. Mongo Express

http://localhost:8083, basic auth with ME_USER / ME_PASSWORD from the root .env. Useful for spot-checking data during development. Never expose it in production.


6. Seeding for development

api/scripts/seed.ts (future) is idempotent. Safe to re-run. It upserts:

  • Roles: admin, moderator, member.
  • A single surface: { slug: 'content', ... }.
  • A default group: { slug: 'general', surfaceSlug: 'content' }.
  • The starter platform list: youtube, twitter, bluesky, reddit, article, generic.
  • A development admin user if DEV_SEED_ADMIN_EMAIL + DEV_SEED_ADMIN_PASSWORD are set (never do this in prod).

Re-running after you've modified documents does NOT overwrite them — findOneAndUpdate({ upsert: true, setOnInsert: { ... } }) is used for records that should not be reset.

For the current (pre-content) sandbox, ./scripts/seed.sh seeds demo roles + users via the HTTP API.


7. Testing strategy

Three layers, each with a different purpose and speed budget.

Unit tests

  • Target: pure functions (slug generation, URL→platform inference, scoring math), service methods with mocked repositories.
  • Framework: Jest.
  • Run: pnpm test.
  • Fast — should stay under 10 seconds total for the whole suite.

Integration tests

  • Target: controller + service + real Mongoose against a real (ephemeral) Mongo.
  • Mechanism: mongodb-memory-server (in-process) OR a short-lived testcontainers Mongo instance.
  • Run: pnpm test:int.
  • Each test suite creates an isolated DB, seeds its fixtures, asserts, drops. No shared state.
  • Don't mock Mongo — integration tests that mock the database are contract-lying.

End-to-end tests

  • Target: HTTP surface. Boot the Nest app, issue real requests, assert envelope shape + status codes.
  • Mechanism: supertest against NestFactory.create(AppModule); Mongo via the same ephemeral strategy.
  • Run: pnpm test:e2e.
  • Use for: auth flows, approval flows, catalog invalidation.

What NOT to test

  • Mongoose schema defaults (Mongoose already tests its own library).
  • ValidationPipe behavior (Nest already tests its own library).
  • Purely presentational DTOs with no logic.

8. Debugging

  • NODE_OPTIONS='--inspect' before pnpm dev exposes a debugger on 9229; connect from VS Code (launch.json snippet in the repo once it exists).
  • Mongoose query log: mongoose.set('debug', true) in main.ts (dev only) prints every query.
  • X-Request-Id: every request gets one; it's in logs and in meta.requestId. Match a failing response to server logs by this ID.
  • Slow query? db.collection.explain('executionStats').find(...) in Mongo Express.

9. Common pitfalls

  • DTO validation doesn't fire. Make sure the property is on a class with @Expose()/@Type() where relevant, and that the controller argument is declared with the class type (not any).
  • ObjectId vs string. Nest serializes ObjectIds to strings via .toJSON() automatically on .lean() results; in tests you may need toString() to compare.
  • Indexes not applied. Mongoose creates indexes lazily unless autoIndex: true. Call model.syncIndexes() on boot in dev; production should use explicit scripts.
  • Transactions silently fail. Mongo transactions require a replica set. The dev docker-compose uses a standalone — transactions will error. When implementing tag-rename, switch dev Mongo to a single-node replica set or mock the session in unit tests and test the real path in integration against a replica-set testcontainer.