This guide will help you create your first queue and worker with BullMQ. By the end, you’ll have a working example that adds jobs to a queue and processes them.
Let’s build a simple job queue system. We’ll create a queue that processes “paint” jobs.
1
Create a Queue
2
First, create a queue that will hold your jobs:
3
import { Queue } from 'bullmq';// Create a new queue named 'paint'const paintQueue = new Queue('paint');// Add jobs to the queueasync function addJobs() { await paintQueue.add('cars', { color: 'blue' }); await paintQueue.add('cars', { color: 'red' }); await paintQueue.add('bikes', { color: 'green' }); console.log('Jobs added to the queue');}addJobs();
4
Create a Worker
5
Now create a worker that will process the jobs:
6
import { Worker, Job } from 'bullmq';// Create a worker that processes jobs from the 'paint' queueconst worker = new Worker('paint', async (job: Job) => { console.log(`Processing job ${job.id} of type ${job.name}`); // Simulate painting if (job.name === 'cars') { await paintCar(job.data.color); } else if (job.name === 'bikes') { await paintBike(job.data.color); } // Return a value that will be stored in the job return { painted: true, color: job.data.color };});// Mock painting functionsasync function paintCar(color: string) { console.log(`Painting car ${color}...`); await new Promise(resolve => setTimeout(resolve, 1000)); console.log(`Car painted ${color}!`);}async function paintBike(color: string) { console.log(`Painting bike ${color}...`); await new Promise(resolve => setTimeout(resolve, 500)); console.log(`Bike painted ${color}!`);}// Listen to worker eventsworker.on('completed', (job: Job) => { console.log(`Job ${job.id} completed!`);});worker.on('failed', (job: Job | undefined, err: Error) => { console.log(`Job ${job?.id} failed with error ${err.message}`);});console.log('Worker started, waiting for jobs...');
7
Run the Example
8
Open two terminal windows:
9
Terminal 1 - Start the worker:
10
node worker.ts# or with TypeScriptnpx tsx worker.ts
11
Terminal 2 - Add jobs:
12
node producer.ts# or with TypeScriptnpx tsx producer.ts
The difference is that Worker events only fire for jobs processed by that specific worker, while QueueEvents fire for all jobs in the queue regardless of which worker processed them.
import { Worker } from 'bullmq';const worker = new Worker('paint', async (job) => { // If this throws, job will be marked as failed if (!job.data.color) { throw new Error('Color is required!'); } // Process job});// Critical: Handle worker errorsworker.on('error', (err) => { console.error('Worker error:', err);});
If you don’t add an error handler, your worker may stop processing jobs when an error occurs. Always add a worker.on('error') listener!