Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
40 prioritized NestJS best practices across architecture, DI, security, performance, testing, and microservices.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
rules/micro-use-queues.md
1---2title: Use Message Queues for Background Jobs3impact: MEDIUM-HIGH4impactDescription: Queues enable reliable background processing5tags: microservices, queues, bullmq, background-jobs6---78## Use Message Queues for Background Jobs910Use `@nestjs/bullmq` for background job processing. Queues decouple long-running tasks from HTTP requests, enable retry logic, and distribute workload across workers. Use them for emails, file processing, notifications, and any task that shouldn't block user requests.1112**Incorrect (long-running tasks in HTTP handlers):**1314```typescript15// Long-running tasks in HTTP handlers16@Controller('reports')17export class ReportsController {18@Post()19async generate(@Body() dto: GenerateReportDto): Promise<Report> {20// This blocks the request for potentially minutes21const data = await this.fetchLargeDataset(dto);22const report = await this.processData(data); // Slow!23await this.sendEmail(dto.email, report); // Can fail!24return report; // Client times out25}26}2728// Fire-and-forget without retry29@Injectable()30export class EmailService {31async sendWelcome(email: string): Promise<void> {32// If this fails, email is never sent33await this.mailer.send({ to: email, template: 'welcome' });34// No retry, no tracking, no visibility35}36}3738// Use setInterval for scheduled tasks39setInterval(async () => {40await cleanupOldRecords();41}, 60000); // No error handling, memory leaks42```4344**Correct (use BullMQ for background processing):**4546```typescript47// Configure BullMQ48import { BullModule } from '@nestjs/bullmq';4950@Module({51imports: [52BullModule.forRoot({53connection: {54host: 'localhost',55port: 6379,56},57defaultJobOptions: {58removeOnComplete: 1000,59removeOnFail: 5000,60attempts: 3,61backoff: {62type: 'exponential',63delay: 1000,64},65},66}),67BullModule.registerQueue(68{ name: 'email' },69{ name: 'reports' },70{ name: 'notifications' },71),72],73})74export class QueueModule {}7576// Producer: Add jobs to queue77@Injectable()78export class ReportsService {79constructor(80@InjectQueue('reports') private reportsQueue: Queue,81) {}8283async requestReport(dto: GenerateReportDto): Promise<{ jobId: string }> {84// Return immediately, process in background85const job = await this.reportsQueue.add('generate', dto, {86priority: dto.urgent ? 1 : 10,87delay: dto.scheduledFor ? Date.parse(dto.scheduledFor) - Date.now() : 0,88});8990return { jobId: job.id };91}9293async getJobStatus(jobId: string): Promise<JobStatus> {94const job = await this.reportsQueue.getJob(jobId);95return {96status: await job.getState(),97progress: job.progress,98result: job.returnvalue,99};100}101}102103// Consumer: Process jobs104@Processor('reports')105export class ReportsProcessor {106private readonly logger = new Logger(ReportsProcessor.name);107108@Process('generate')109async generateReport(job: Job<GenerateReportDto>): Promise<Report> {110this.logger.log(`Processing report job ${job.id}`);111112// Update progress113await job.updateProgress(10);114115const data = await this.fetchData(job.data);116await job.updateProgress(50);117118const report = await this.processData(data);119await job.updateProgress(90);120121await this.saveReport(report);122await job.updateProgress(100);123124return report;125}126127@OnQueueActive()128onActive(job: Job) {129this.logger.log(`Processing job ${job.id}`);130}131132@OnQueueCompleted()133onCompleted(job: Job, result: any) {134this.logger.log(`Job ${job.id} completed`);135}136137@OnQueueFailed()138onFailed(job: Job, error: Error) {139this.logger.error(`Job ${job.id} failed: ${error.message}`);140}141}142143// Email queue with retry144@Processor('email')145export class EmailProcessor {146@Process('send')147async sendEmail(job: Job<SendEmailDto>): Promise<void> {148const { to, template, data } = job.data;149150try {151await this.mailer.send({152to,153template,154context: data,155});156} catch (error) {157// BullMQ will retry based on job options158throw error;159}160}161}162163// Usage164@Injectable()165export class NotificationService {166constructor(@InjectQueue('email') private emailQueue: Queue) {}167168async sendWelcome(user: User): Promise<void> {169await this.emailQueue.add(170'send',171{172to: user.email,173template: 'welcome',174data: { name: user.name },175},176{177attempts: 5,178backoff: { type: 'exponential', delay: 5000 },179},180);181}182}183184// Scheduled jobs185@Injectable()186export class ScheduledJobsService implements OnModuleInit {187constructor(@InjectQueue('maintenance') private queue: Queue) {}188189async onModuleInit(): Promise<void> {190// Clean up old reports daily at midnight191await this.queue.add(192'cleanup',193{},194{195repeat: { cron: '0 0 * * *' },196jobId: 'daily-cleanup', // Prevent duplicates197},198);199200// Send digest every hour201await this.queue.add(202'digest',203{},204{205repeat: { every: 60 * 60 * 1000 },206jobId: 'hourly-digest',207},208);209}210}211212@Processor('maintenance')213export class MaintenanceProcessor {214@Process('cleanup')215async cleanup(): Promise<void> {216await this.cleanupOldReports();217await this.cleanupExpiredSessions();218}219220@Process('digest')221async sendDigest(): Promise<void> {222const users = await this.getUsersForDigest();223for (const user of users) {224await this.emailQueue.add('send', { to: user.email, template: 'digest' });225}226}227}228229// Queue monitoring with Bull Board230import { BullBoardModule } from '@bull-board/nestjs';231import { BullMQAdapter } from '@bull-board/api/bullMQAdapter';232233@Module({234imports: [235BullBoardModule.forRoot({236route: '/admin/queues',237adapter: ExpressAdapter,238}),239BullBoardModule.forFeature({240name: 'email',241adapter: BullMQAdapter,242}),243BullBoardModule.forFeature({244name: 'reports',245adapter: BullMQAdapter,246}),247],248})249export class AdminModule {}250```251252Reference: [NestJS Queues](https://docs.nestjs.com/techniques/queues)253