Related: README | API Overview | Webhook Flow | Deployment Notes
The recommended local workflow is Docker-first. That keeps the API, worker, PostgreSQL, Redis, RabbitMQ, migrations, and seed data aligned with the same runtime assumptions used in CI.
| Requirement | Why it matters |
|---|---|
| Docker with Compose support | Starts the full local stack in one command |
| Node.js 20+ | Needed only for non-container execution or local script runs |
jq |
Optional, but useful for reading JSON responses in the walkthrough below |
docker compose up --build -ddocker compose logs -f migrator api workerThe migrator service runs npm run db:migrate && npm run db:seed before the API and worker come online.
curl -s http://localhost:3000/api/v1/health | jqMGMT_KEY=local-internal-management-keycurl -s http://localhost:3000/api/v1/integrations \
-H "x-internal-api-key: $MGMT_KEY" | jqdocker compose downThe seed data makes the worker path usable immediately because both local integrations point their callback URLs at the non-production internal delivery sink.
PAYLOAD='{"eventId":"evt-demo-1001","eventType":"order.created","occurredAt":"2026-03-07T10:00:00.000Z","subject":"order-1001","data":{"id":"order-1001","total":149.5,"currency":"USD"}}'
SIG=$(node -e "const crypto=require('crypto');const payload=process.argv[1];process.stdout.write(crypto.createHmac('sha256','acme-demo-secret').update(payload).digest('hex'))" "$PAYLOAD")
curl -s -X POST http://localhost:3000/api/v1/webhooks/acme \
-H "content-type: application/json" \
-H "x-acme-signature: $SIG" \
--data "$PAYLOAD" | jqcurl -s "http://localhost:3000/api/v1/events?page=1&pageSize=5&sortBy=receivedAt&sortOrder=desc" \
-H "x-internal-api-key: $MGMT_KEY" | jqcurl -s -X POST "http://localhost:3000/api/v1/events/<event-id>/replay" \
-H "content-type: application/json" \
-H "x-internal-api-key: $MGMT_KEY" \
--data '{"requestedBy":"local-demo","reason":"Manual replay validation"}' | jqcurl -s "http://localhost:3000/api/v1/events/<event-id>/status" \
-H "x-internal-api-key: $MGMT_KEY" | jq
curl -s "http://localhost:3000/api/v1/deliveries?eventId=<event-id>" \
-H "x-internal-api-key: $MGMT_KEY" | jq
curl -s "http://localhost:3000/api/v1/audit-entries?entityId=<event-id>" \
-H "x-internal-api-key: $MGMT_KEY" | jqTo exercise retries, include simulateFailure: true in the payload or in payload.data. The local internal delivery sink will return 500, which causes the worker to schedule retries.
| Service | Address |
|---|---|
| API | http://localhost:3000 |
| Nginx proxy | http://localhost:8082 |
| PostgreSQL | localhost:5434 |
| Redis | localhost:6381 |
| RabbitMQ AMQP | localhost:5672 |
| RabbitMQ UI | http://localhost:15672 |
RabbitMQ UI credentials: guest / guest
Use this path only when PostgreSQL, Redis, and RabbitMQ already exist outside Docker.
cp .env.example .env
npm install
npm run db:migrate
npm run db:seed
npm run dev
npm run worker:dev| Command | Purpose |
|---|---|
npm run lint |
ESLint |
npm run format |
Prettier check |
npm run typecheck |
TypeScript validation |
npm test |
Unit and integration coverage |
npm run build |
Compile API and worker output |
docker compose config > /dev/null |
Validate Compose wiring |