@better-bull-board/client
Developer documentation for integrating Better Bull Board with your BullMQ workers.
Installation
pnpm add @better-bull-board/client bullmq
bullmq is a peer dependency (^6). Install the version your app uses; it does not need to match this package's pin.
Usage
To use Better Bull Board with your BullMQ workers, you need to:
- Import the BBB client
- Use a separate processor (sandboxed processor required for job cancellation)
Basic Setup
1. Create a Processor File
Create a separate processor file (e.g., processor.ts or processor.js):
import { patch } from "@better-bull-board/client"
import type { SandboxedJob } from "bullmq"
import { redis } from "./lib/redis" // Your Redis connection
export default patch(async (job: SandboxedJob) => {
console.log(`Processing job ${job.id}`)
// Your job processing logic here
await processYourJob(job.data)
console.log(`Job ${job.id} completed`)
return { status: "done" }
}, redis)
2. Create a Worker
Use the Better Bull Board Worker class in your main worker file:
import path from "node:path"
import { Worker } from "@better-bull-board/client"
import { redis } from "./lib/redis" // Your Redis connection
const processorFile = path.join(__dirname, "processor.cjs") // Note: .cjs extension for built files
new Worker("your-queue-name", processorFile, {
connection: redis,
ioredis: redis,
useWorkerThreads: true,
concurrency: 10,
getJobTags(job) {
// Optional: Return tags for better organization
return ["your-queue", "production"]
},
})
Key Requirements
- Separate Processor: You must use a sandboxed processor (separate file) for job cancellation to work properly
- Redis Connection: Provide both
connectionandioredisoptions pointing to the same Redis instance - Worker Threads: Set
useWorkerThreads: truefor proper isolation - File Extension: Use
.cjsextension for the processor file path when referencing built files
Complete Example
Here's a complete working example based on our demo:
processor.ts
import { patch } from "@better-bull-board/client"
import type { SandboxedJob } from "bullmq"
import { redis } from "../lib/redis"
export default patch(async (job: SandboxedJob) => {
console.log(`Processing job ${job.id}`)
// Simulate work
await new Promise((resolve) => setTimeout(resolve, 10_000))
console.log(`Job ${job.id} processed`)
return { status: "done" }
}, redis)
worker.ts
import path from "node:path"
import { fileURLToPath } from "node:url"
import { Worker } from "@better-bull-board/client"
import { redis } from "./lib/redis"
const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)
const processorFile = path.join(__dirname, "processor.cjs")
new Worker("demo-queue", processorFile, {
connection: redis,
ioredis: redis,
useWorkerThreads: true,
concurrency: 10,
getJobTags() {
return ["demo-queue", "test"]
},
})
Advanced Features
Job Cancellation
The patch function provides built-in job cancellation support. Jobs can be cancelled through the Better Bull Board UI.
Job logs
All console output from your processors is automatically captured and sent to Better Bull Board for monitoring.
Loggers that write to stdout directly, like pino, bypass console. Give pino the job log destination so each line also reaches the current job:
import { createPinoJobLogDestination } from "@better-bull-board/client"
import { pino } from "pino"
export const logger = pino(
{ level: "info" },
pino.multistream([
{ stream: process.stdout, level: "trace" },
{ stream: createPinoJobLogDestination({ ignore: ["trace_id"] }), level: "trace" },
]),
)
The destination keeps msg, appends the other fields as JSON and prints err like a thrown error, followed by its own fields (code, data, …); ignore drops keys you do not want in the job logs (pid and hostname already are). Outside a job it is a no-op, so one logger instance serves both the worker and its processors.
Any other logger can call relayJobLog({ level, message }) from its transport: it forwards the line to the job bound to the current async context and does nothing otherwise.
Job Tags
Use the getJobTags function to organize and filter your jobs in the Better Bull Board interface:
getJobTags(job) {
return [
job.queueName,
job.data.priority || "normal",
process.env.NODE_ENV || "development"
];
}
API Reference
patch(processor, redis)
Wraps your job processor with Better Bull Board functionality.
processor: Your async job processing functionredis: Redis connection instance
createPinoJobLogDestination(options?)
A pino destination that relays each log line to the current job's logs.
options.ignore: extra keys to drop from the relayed message
relayJobLog({ level, message })
Forwards one log line to the job bound to the current async context, if any. level is one of log, debug, info, warn, error.
Worker
Extended BullMQ Worker with Better Bull Board integration.
Required Options:
ioredis: Redis connection instanceuseWorkerThreads: Must betruegetJobTags: Optional function to return job tags
Troubleshooting
- Job cancellation not working: Ensure you're using a sandboxed processor (separate file)
- Jobs not appearing: Check that your Redis connection is properly configured
- Build issues: Make sure to reference
.cjsfiles for built processors