ReportHub is the system of record for long-running report/data export requests. It prevents duplicate work, tracks lifecycle states, and provides auditability with idempotent writes and optimistic locking.
- Express + TypeScript for HTTP APIs and structured request handling.
- PostgreSQL + Prisma as the data layer, with a repository pattern isolating all Prisma calls.
- Zod for validation.
- Pino for structured logging with request IDs.
- BullMQ + Redis for async report processing (optional but enabled here).
- Swagger UI at
/docsfor interactive API docs.
- CRUD with soft deletes for
ReportRequest. - Strict lifecycle transitions with conflict detection.
- Idempotency keys for safe creates.
- Optimistic locking for updates via
If-Matchversion. - Async processing worker for queued reports.
- OpenAPI docs and integration tests.
- CI workflow with lint, tests, and build.
- Start infrastructure:
docker-compose up -d- Configure environment:
cp .env.example .env- Install deps and run migrations:
npm install
npm run prisma:generate
npm run prisma:migrate- Start API:
npm run dev- (Optional) Start worker:
npm run build
npm run workerSwagger UI: http://localhost:3000/docs
npm run testCreate a request with idempotency:
curl -X POST http://localhost:3000/report-requests \
-H "Content-Type: application/json" \
-H "Idempotency-Key: req-001" \
-d '{
"title": "Monthly Sales",
"type": "SALES_SUMMARY",
"parameters": { "month": "2025-12" },
"createdBy": "analyst@example.com"
}'Queue a report:
curl -X POST http://localhost:3000/report-requests/{id}/queueUpdate with optimistic locking:
curl -X PATCH http://localhost:3000/report-requests/{id} \
-H "Content-Type: application/json" \
-H "If-Match: 1" \
-d '{ "title": "Updated Title" }'List with filters:
curl "http://localhost:3000/report-requests?status=QUEUED&page=1&pageSize=20"Cancel a request:
curl -X POST http://localhost:3000/report-requests/{id}/cancel- Swagger UI:
/docs - OpenAPI JSON:
/docs.json
src/
app.ts
server.ts
config/
lib/
middlewares/
modules/reportRequests/