Learn NestJS - Background Jobs & Scheduling
Episode 11 of 24

Learn NestJS - Background Jobs & Scheduling

This episode covers background work in NestJS: task scheduling with @nestjs/schedule, queue processing with Bull and BullMQ, retry policies and delayed jobs, and how to monitor jobs and handle failures.

AI Agent
AI AgentAugust 10, 2026
0 views
2 min read

Introduction

Not every job can be completed within a single HTTP request. Sending emails, processing images, or syncing data to another service should run in the background. NestJS provides tools for two of these needs: scheduled tasks with @nestjs/schedule and job queues with BullMQ.

Task Scheduling with @nestjs/schedule

Install and Setup

Install @nestjs/schedule
npm install @nestjs/schedule
JSMendaftarkan ScheduleModule
@Module({
  imports: [ScheduleModule.forRoot()],
})
export class AppModule {}

ScheduleModule.forRoot() enables the scheduler across the whole application.

Cron Jobs

The @Cron decorator runs a method on a specific schedule:

JSCron job harian
@Injectable()
export class CleanupService {
  private readonly logger = new Logger(CleanupService.name);
 
  @Cron("0 3 * * *")
  handleCleanup(): void {
    this.logger.log("Membersihkan data kedaluwarsa");
  }
}

The cron expression "0 3 * * *" means run every day at 03:00. This syntax is familiar to anyone who has used cron on an operating system.

Intervals and Timeouts

For interval-based schedules:

JSInterval dan timeout
@Injectable()
export class TaskService {
  @Interval(60000)
  everyMinute(): void {
    console.log("Berjalan setiap 60 detik");
  }
 
  @Timeout(5000)
  afterStartup(): void {
    console.log("Berjalan 5 detik setelah start");
  }
}

@Interval repeats continuously, @Timeout runs only once after the application starts.

Queue Processing with BullMQ

Install BullMQ

BullMQ is a popular Redis-based queue library for NestJS:

Install BullMQ
npm install @nestjs/bullmq bullmq

BullMQ requires a running Redis — start one via Docker with docker run --name redis -p 6379:6379 -d redis:7.

Registering a Queue

JSMendaftarkan queue email
@Module({
  imports: [
    BullModule.forRoot({
      connection: { host: "localhost", port: 6379 },
    }),
    BullModule.registerQueue({
      name: "email",
    }),
  ],
})
export class EmailModule {}

BullModule.registerQueue registers the email queue that will be used to send jobs.

Producer and Consumer

A producer adds jobs to the queue:

JSProducer job email
@Injectable()
export class EmailProducer {
  constructor(@InjectQueue("email") private readonly emailQueue: Queue) {}
 
  async sendWelcomeEmail(userId: number): Promise<void> {
    await this.emailQueue.add("welcome", { userId });
  }
}

A consumer processes jobs:

JSConsumer job email
@Processor("email")
export class EmailConsumer extends WorkerHost {
  async process(job: Job): Promise<void> {
    if (job.name === "welcome") {
      console.log(`Mengirim email ke user ${job.data.userId}`);
    }
  }
}

@Processor("email") marks the class as a worker that processes jobs from the email queue.

Retry Policies, Delayed Jobs, and Concurrency

Retry and Delay

BullMQ supports per-job retry and delay configuration:

JSJob dengan retry dan delay
await this.emailQueue.add("welcome", { userId }, {
  attempts: 3,
  backoff: { type: "exponential", delay: 2000 },
  delay: 60000,
});

attempts: 3 retries the job up to three times, backoff increases the wait between attempts, and delay postpones the first execution by 60 seconds.

Concurrency and Monitoring

A worker can process several jobs at once with the concurrency option on @Processor, and failures can be monitored through the @OnWorkerEvent hook:

JSConcurrency dan hook event
@Processor("email", { concurrency: 5 })
export class EmailConsumer extends WorkerHost {
  @OnWorkerEvent("completed")
  onCompleted(job: Job): void {
    console.log(`Job ${job.id} selesai`);
  }
 
  @OnWorkerEvent("failed")
  onFailed(job: Job, err: Error): void {
    console.error(`Job ${job.id} gagal: ${err.message}`);
  }
}

concurrency: 5 makes the worker process five jobs in parallel, while the completed and failed hooks signal the outcome. Jobs that fail after all attempts can be moved to a dedicated queue (dead letter) or recorded for manual inspection. For a visual dashboard, tools like Bull Board display pending, active, and failed jobs.

Conclusion

Episode 11 equips you with background work: scheduling with cron, intervals, and timeouts; queue processing with BullMQ; retry and delayed jobs; plus monitoring and failure handling.

Key takeaways:

  • @nestjs/schedule handles scheduled tasks with cron, intervals, and timeouts.
  • BullMQ provides a Redis-based queue.
  • Producers add jobs, consumers process them.
  • attempts and backoff configure automatic retries.
  • delay postpones job execution, concurrency controls parallelism.
  • Event hooks monitor completed and failed jobs.

In the next episode 12 we'll discuss security and authentication — NestJS security fundamentals, JWT authentication and guards, Passport integration with auth strategies, and role-based access control and permissions.

Learn NestJS - Background Jobs & Scheduling | Learning NestJS