- Node.js
- Redis running locally on port
6379 SHIPPING_PROVIDER_URLenv var set (defaults tohttp://localhost:8000/api/update-order-status), used by thesend-to-shipping-providerjobSHIPPING_PROVIDER_SIMULATE_FAILURE=trueto deterministically force everysend-to-shipping-providerattempt to fail (no network call made) — handy for demoing the DLQ without needing to actually take a service down. Defaults tofalse/unset.DLQ_ALERT_THRESHOLD(optional, default1) — DLQ depth (waiting count) at which a log-level alert is emitted
docker run -p 6379:6379 redis
npm install
npm run start:devBase URL: http://localhost:3000
Health check / welcome message.
curl http://localhost:3000/Registers a user. Emits UserRegisteredEvent → enqueues a send-registration-email job.
| Query param | Type | Required | Description |
|---|---|---|---|
email |
string | yes | User's email address |
curl "http://localhost:3000/user/register?email=someone@example.com"Response
{ "message": "Registration event emitted for someone@example.com" }Requests a password reset. Emits PasswordResetRequestedEvent → enqueues a send-password-reset-email job.
| Query param | Type | Required | Description |
|---|---|---|---|
email |
string | yes | User's email address |
curl "http://localhost:3000/user/forgot-password?email=someone@example.com"Response
{ "message": "Password reset event emitted for someone@example.com" }Places an order. Emits OrderPlacedEvent → fans out to two jobs: send-to-shipping-provider and send-confirmation-email.
| Query param | Type | Required | Description |
|---|---|---|---|
orderId |
string | yes | Order ID |
email |
string | yes | Customer email |
curl "http://localhost:3000/order/place?orderId=123&email=someone@example.com"Response
{ "message": "Order placed event emitted for order 123" }Lists current entries in the order-dlq dead-letter queue (jobs that exhausted all send-to-shipping-provider retry attempts). Each entry includes the original job name/queue, payload, failedReason, stack trace, attempts made, first-attempt/final-failure timestamps, the originating request ID, and how many times it's already been reprocessed.
curl http://localhost:3000/order/dlqReconstructs the original job payload from a DLQ entry and re-adds it to the order queue, then removes the DLQ entry. :id is the DLQ job's own ID (as returned by GET /order/dlq), not the original order job's ID.
curl -X POST http://localhost:3000/order/dlq/12/reprocessPoison-pill guard: each reprocess attempt increments a reprocessCount carried in the job's options metadata, so the count survives a fail → reprocess → fail cycle. After 3 reprocess attempts, further reprocessing is refused (400 Bad Request) until someone investigates manually — reprocessing is manual-only, there is no auto-retry out of the DLQ.
When a send-to-shipping-provider job exhausts all configured attempts (checked via job.attemptsMade >= job.opts.attempts in OrderProcessor's @OnWorkerEvent('failed') handler, so only truly exhausted jobs are captured — not every individual retry), it's moved into the order-dlq queue instead of being left in BullMQ's default failed state. The original job is removed from order once it's copied over, so the DLQ is the single place to look for exhausted jobs.
- Demo trigger: set
SHIPPING_PROVIDER_SIMULATE_FAILURE=trueto force failures deterministically. - Inspection:
order-dlqis registered in Bull Board alongsideorder/user, andGET /order/dlqgives a readable JSON view. - Reprocessing:
POST /order/dlq/:id/reprocess, capped at 3 attempts per entry (see above).
Inspect queues and job status at:
http://localhost:3000/admin/queues
This includes the order-dlq queue.