Urgent.News

One page, thousands of outlets. See who else covered it.

Editions

Tech

Building Resilient Background Jobs in NestJS with BullMQ

Background jobs look simple right up until one of them dies silently in production and nobody notices for three days. A job that sends confirmation emails stops running. A job that syncs inventory data quietly falls behind. Nobody gets an error, because from the queue's perspective, nothing "crashed" — the job just failed and nobody was watching. Most BullMQ tutorials stop at "job added, job…

In the realm of background jobs, complications arise when one of them fails silently in a live environment for an extended period. A job responsible for sending confirmation emails may cease to function, while another job that synchronizes inventory data could lag behind. No error is triggered, as the queue perceives the job as having failed rather than the job itself crashing.

While BullMQ tutorials often cover the basics of job addition and processing, they fall short in addressing real-world scenarios where external APIs time out, workers restart mid-job, and retries without proper safeguards can exacerbate problems.

This article skips the foundational setup tutorial and delves into essential patterns for building resilient background jobs in NestJS using BullMQ. These patterns encompass retries that prevent a thundering herd, idempotency to avoid duplicate side effects from retries, dead-letter queues for jobs that keep failing, concurrency limits to protect databases, and mechanisms to catch jobs that are completed but still stuck in processing.

To begin, ensure BullMQ is integrated into your NestJS application. Install the necessary packages using the command `npm install @nestjs/bullmq bullmq ioredis`. Within your `app.module.ts`, import `BullModule` from `@nestjs/bullmq` and configure the queue with the appropriate connection settings. For instance, use `BullModule.forRoot({ connection: { host: process.env.REDIS_HOST, port: Number(process.env.REDIS_PORT) }, })` to establish the connection. Additionally, register the queue using `BullModule.registerQueue({ name: 'notifications', })`.

When dealing with retries, the default approach of adding retries without a backoff strategy can lead to a thundering herd effect. Instead, implement exponential backoff to space out retries. Configure this by adding `{ backoff: { type: 'exponential', delay: 1000, } }` to the job options when adding it to the queue. This causes retries to occur at increasing intervals (1s, 2s, 4s, and so on) to allow the downstream system to recover.

For high-volume queues, incorporate jitter to further spread out retries and prevent a synchronized cluster of retries that could otherwise worsen the situation.

Idempotency is another crucial aspect often overlooked in tutorials. Retries should be safe, meaning the job can be retried multiple times without causing additional side effects. To achieve this, employ an idempotency key—a unique identifier for each job. Before executing the job's side effects, verify that this key has not been processed before.

If it has, skip the operation to prevent duplication. Implement this logic in a service, such as `IdempotencyService`, which checks for existing keys in your database (e.g., using Prisma or another ORM) before proceeding with the job's execution.

Written by urgent.news from Dev.to's reporting — not their text. Machine-written — may contain errors; check the original before relying on it.

Read the original at dev.to →

More in Tech

Why Your Docker Build Takes 11 Minutes in CI When It Takes 20 Seconds Locally

If your Docker build is fast locally and slow in CI, the base image is almost never the problem. CI runners are ephemeral, so they start with an empty layer cache unless you explicitly wire one up…

  • CI runners start with empty cache, invalidating previous layers
  • Move dependency installation steps to bottom of Dockerfile
  • Use --progress=plain to identify cached vs uncached steps

Designing HTML Page Caching Changed How I Think About Caching

Long Story Short Designing HTML page caching in practice connected several concepts I had previously understood separately, such as TTL, browser caching, and where cache policies should live.

  • HTML page caching at work revealed broader system impact
  • CloudFront served as CDN, cached HTML pages including SSR data
  • TTL wasn't simple, required context-specific reasoning

We scanned our own production site and found 8 vulnerabilities. Here’s the list.

Building software in 2026 feels surreal. With LLMs handling boilerplate, we ship features in hours that used to take weeks. But fast shipping has a nasty side effect: it breeds overconfidence.

  • Vergate discovered 8 vulnerabilities in their own marketing site
  • Missing security headers exposed site to cross-site attacks
  • Incident highlights need for continuous security checks

More from Sunday 16 August →