bull-board

The framework-agnostic core API and queue adapters that power the Bull Board dashboard for Bull and BullMQ.

Library
npm
v9.10.4
3,504 stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum. How we score it →
92 /100 Excellent
Development Activity 96
Maintenance 100
Community 72
Maturity 60
Momentum 40

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation. How we score it →
90 /100 Excellent
Architecture 88
Code Quality 92
Innovation 90
Learning Curve 90

@bull-board/api is the engine behind Bull Board: it defines the queue adapter contract (BaseAdapter), the full set of dashboard API routes, and the createBullBoard() composition function that wires a list of queues into any Node HTTP server. It normalizes Bull and BullMQ (including BullMQ v6 on PostgreSQL) behind one interface, so the dashboard can list jobs, inspect flows, manage job schedulers, tune rate limits and concurrency, and drive retries/promotions/cleanup without knowing which queue library or datastore is underneath.

It ships with no HTTP framework baked in. Nine separate adapter packages (@bull-board/express, fastify, koa, hapi, nestjs, hono, h3, elysia, bun) each implement a small IServerAdapter contract and hand the routes to their framework’s router, so this package is the one place the actual dashboard logic, hooks, and adapters live.

What You Get

  • createBullBoard({ queues, serverAdapter, options }) that returns addQueue/removeQueue/setQueues/replaceQueues for runtime reconfiguration of the board’s queue set
  • BullAdapter, BullMQAdapter and BullMQProAdapter implementations of the abstract BaseAdapter contract, covering Bull v4, BullMQ v5/v6 (Redis and Postgres backends), and BullMQ Pro
  • A full set of dashboard API routes (queues, jobs, job flows, job schedulers, rate limits, global concurrency, redis/datastore stats) implemented as independent handler functions
  • Optional handlerHooks (before/after) for per-request authorization or observability, and an optional historyProvider hook that adds metrics-history routes only when a provider supplies them
  • Per-queue formatters, a visibility guard, read-only mode, and configurable job-data JSON Schema validation

Common Use Cases

  • Embedding a queue-inspection dashboard into an existing Express/Fastify/Koa/NestJS app by pairing this package with one framework adapter
  • Building a custom internal ops tool on top of the same adapter/route API without using the bundled UI
  • Adding role-based access control to queue operations via handlerHooks.before, rejecting pause/retry/obliterate actions for read-only users
  • Monitoring a BullMQ v6 deployment backed by PostgreSQL instead of Redis, using the same adapter and dashboard code

Under The Hood

Architecture

createBullBoard() is the composition root: it takes a list of queue adapters and a server adapter, builds the API route table (appRoutes.api plus any routes a historyProvider unlocks), optionally wraps every handler with wrapHandlerWithHooks for before/after hooks, and hands the whole thing to the server adapter via a small fluent interface (setQueues/setViewsPath/setStaticPath/setUIConfig/setEntryRoute/setErrorHandler/setApiRoutes). Each dashboard capability lives in its own file under handlers/ (addJob, retryJob, pauseQueue, jobFlow, metricsHistory, setGlobalConcurrency, and two dozen more), kept independent of any specific HTTP framework. The real seam is the abstract BaseAdapter class in queueAdapters/base.ts, which declares roughly thirty abstract lifecycle methods (getJob, getJobs, pause, clean, obliterate, job-scheduler CRUD, rate limiting); BullAdapter and BullMQAdapter implement it for Bull and BullMQ respectively, so supporting a new queue engine or datastore (BullMQAdapter already branches on Redis vs. Postgres backends) means writing one adapter, not touching routes or handlers.

Tech Stack

Written in TypeScript inside a Yarn 4 workspaces monorepo (packages/api plus one package per HTTP framework adapter). Peer dependencies on bull ^4.16.5 and bullmq ^5.56.0 || ^6.0.0 are both optional, letting a consumer bring either or both; the only hard runtime dependency is the small redis-info package for parsing Redis INFO output. Builds go through plain tsc, tests run under Jest with several parallel jest.config variants (bullmq-floor/v5/v6) to cover the supported BullMQ version range, and linting/formatting use the Rust-based oxlint/oxfmt pair enforced through simple-git-hooks and lint-staged. CI (GitHub Actions) runs CodeQL, Docker image builds, and the Node test suite; releases are cut with release-it and auto-changelog.

Code Quality

The package carries 41 spec files: unit tests per handler under tests/api, adapter-focused suites under tests/queueAdapters, and a bullmq-matrix suite that re-runs adapter, handler, flow and Postgres-backend behavior across the BullMQ version matrix, plus a type-only spec that checks the adapter’s generic type inference. Errors are centralized through a single errorResponse() helper that returns translation keys rather than hardcoded English, keeping the API i18n-friendly by construction. Non-obvious invariants are documented inline with JSDoc-style comments explaining why (e.g. the WeakMap-cached FlowProducer, or how an armed job-scheduler id is derived from job ids rather than trusted metadata), which is unusually thorough for a library of this size.

API Design createBullBoard() returns a small, focused surface (addQueue/removeQueue/setQueues/replaceQueues) that lets a running server change its monitored queue set without a restart. Nine separate HTTP-framework adapters (Express, Fastify, Koa, Hapi, NestJS, Hono, H3, Elysia, Bun) each implement one small IServerAdapter contract, so this single package’s routes and handlers are reused everywhere rather than reimplemented per framework. The optional historyProvider hook is capability-detected at construction time (hasHistoryUsage, canPurgeHistory, hasLatencyHistory), so metrics-history routes only appear when a provider actually supports them, keeping the default install stateless. handlerHooks.before/after gives consumers a clean seam for authorization or observability without needing framework-specific middleware.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers