@worker-manager/nestjs
v2.5.0
Published
A NestJS module for the Worker Manager dashboard.
Maintainers
Readme
@worker-manager/nestjs
NestJS module for Worker Manager.

Installation
Install both @worker-manager/api and this module.
$ npm install --save @worker-manager/nestjs @worker-manager/apiInstall the Express or Fastify adapter depending on what you use in NestJS (default is Express)
$ npm install --save @worker-manager/express
//or
$ npm install --save @worker-manager/fastifyRegister the root module
Once the installation is completed, we can import the WorkerManagerModule into your rootmodule e.g. AppModule.
import { Module } from '@nestjs/common';
import { WorkerManagerModule } from "@worker-manager/nestjs";
@Module({
imports: [
BullModule.forRoot({
// your bull module config here.
}),
// Served at /queues, with the adapter matching your Nest platform (Express or Fastify).
WorkerManagerModule.forRoot(),
],
})
export class AppModule {
}The forRoot() method registers the Worker Manager instance and allows you to pass several options to both the instance and module.
The following options are available, all optional.
| Option | Default | |
|---|---|---|
| route | '/queues' | Base route of the board, relative to the Nest global prefix. |
| adapter | auto-detected | ExpressAdapter (@worker-manager/express) or FastifyAdapter (@worker-manager/fastify). When omitted, the module reads the platform from HttpAdapterHost and loads the matching package. |
| auth | none | Built-in authentication, see Authentication. |
| enabled | true | false registers nothing: no routes, no middleware, forFeature becomes a no-op and the injected instance is null. |
| readOnly | false | Read-only mode for every queue registered through queues or forFeature, unless the queue sets options.readOnlyMode itself. |
| queues | [] | Queues to register at the root, same shape as forFeature entries. |
| uiConfig | | Merged into boardOptions.uiConfig. |
| title / logo / theme | | Shortcuts for uiConfig.boardTitle, uiConfig.boardLogo, uiConfig.theme. |
| boardOptions | | Options as provided by the Worker Manager package, such as uiBasePath and uiConfig. |
| middleware | | Nest middleware applied to the board route, after auth on Express. |
WorkerManagerModule.forRoot({
route: '/ops/queues',
title: 'Ops queues',
readOnly: process.env.NODE_ENV === 'production',
enabled: process.env.QUEUE_BOARD !== 'off',
queues: [{ name: 'emails', adapter: BullMQAdapter }],
}),Async configuration
forRootAsync() accepts useFactory + inject, useClass or useExisting, with imports:
WorkerManagerModule.forRootAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: (config: ConfigService) => ({
route: '/queues',
enabled: config.get('QUEUE_BOARD_ENABLED') !== 'false',
auth: {
strategy: 'keycloak',
url: config.getOrThrow('KEYCLOAK_URL'),
realm: config.getOrThrow('KEYCLOAK_REALM'),
clientId: config.getOrThrow('KEYCLOAK_CLIENT_ID'),
clientSecret: config.get('KEYCLOAK_CLIENT_SECRET'),
requiredRoles: ['wm-admin'],
cookie: { secret: config.getOrThrow('SESSION_SECRET') },
},
}),
}),@Injectable()
class BoardConfig implements WorkerManagerOptionsFactory {
constructor(private readonly config: ConfigService) {}
createWorkerManagerOptions(): WorkerManagerModuleOptions {
return { auth: { strategy: 'basic', users: [{ username: 'admin', password: this.config.getOrThrow('BOARD_PASSWORD') }] } };
}
}
WorkerManagerModule.forRootAsync({ imports: [ConfigModule], useClass: BoardConfig }),Authentication
The auth option protects every board route (page, API, assets) with
@worker-manager/auth, on Express and
Fastify alike, and honours the Nest global prefix.
Basic
WorkerManagerModule.forRoot({
auth: {
strategy: 'basic',
users: [{ username: 'admin', password: process.env.BOARD_PASSWORD, roles: ['admin'] }],
},
}),Keycloak
WorkerManagerModule.forRoot({
auth: {
strategy: 'keycloak',
url: 'https://sso.example.com',
realm: 'ops',
clientId: 'worker-manager',
clientSecret: process.env.KEYCLOAK_CLIENT_SECRET,
publicUrl: 'https://api.example.com/queues', // the board's external URL, base path included
requiredRoles: ['wm-admin'],
cookie: { secret: process.env.SESSION_SECRET },
},
}),Browsers are sent through the OIDC authorization code flow (PKCE), API clients may send an
Authorization: Bearer access token. Register https://api.example.com/queues/auth/callback as a
redirect URI on the Keycloak client. The board serves GET /queues/auth/me (the signed-in user)
and GET /queues/auth/logout.
Token
WorkerManagerModule.forRoot({
route: '/admin/queues',
auth: {
strategy: 'token',
tokens: [process.env.BOARD_TOKEN],
header: 'X-Board-Token', // Authorization: Bearer <token> works too
cookie: { secret: process.env.BOARD_SESSION_SECRET }, // enables the browser login form
},
}),API calls without the token get 401 JSON. A browser is sent to /admin/queues/auth/login, types
the token once and gets an encrypted SameSite=Strict session cookie.
Custom
WorkerManagerModule.forRoot({
auth: {
strategy: 'custom',
authenticate: async (req) => verifyMyApiKey(req.headers['x-api-key']), // AuthUser or null
},
}),Testing
Test.createTestingModule({ imports: [AppModule] }).compile() works without an explicit
adapter: the platform is detected when createNestApplication() provides it, during
app.init().
Custom middleware
middleware still takes any Nest middleware, e.g. express-basic-auth. On Express it runs after
auth. On Fastify it is applied as Nest middleware to the exact route, before the board's own
hooks.
import basicAuth from "express-basic-auth";
WorkerManagerModule.forRoot({
route: "/queues",
middleware: basicAuth({
challenge: true,
users: { admin: "passwordhere" },
}),
}),Register your queues
To register a new queue, you need to register WorkerManagerModule.forFeature in the same module as where your queues are registered.
import { Module } from '@nestjs/common';
import { WorkerManagerModule } from "@worker-manager/nestjs";
import { BullMQAdapter } from "@worker-manager/api/bullMQAdapter";
import { BullModule } from "@nestjs/bullmq";
@Module({
imports: [
BullModule.registerQueue(
{
name: 'my_awesome_queue'
}
),
WorkerManagerModule.forFeature({
name: 'my_awesome_queue',
adapter: BullMQAdapter, //or use BullAdapter if you're using bull instead of bullMQ
}),
],
})
export class FeatureModule {}The forFeature method registers the given queues to the Worker Manager instance.
The following options are available.
namethe queue name to resolve from the Nest DI container.queuea queue instance to register directly, instead of resolving it byname.adaptereitherBullAdapterorBullMQAdapterdepending on which package you use.optionsqueue adapter options as found in the Worker Manager package, such asreadOnlyMode,descriptionetc.
Provide either name or queue.
PostgreSQL-backed queues (BullMQ v6)
A BullMQ v6 queue stored in PostgreSQL has no Redis connection and is usually not in the Nest container, so hand the instance over directly:
import { Queue, createPostgresBackend } from 'bullmq'; // bullmq@6, plus `pg`
const invoices = new Queue(
'invoices',
// BullMQ 6.3+: `migrate: true` creates its schema on first connect.
{ connection: { connectionString: process.env.POSTGRES_URL, migrate: true } },
createPostgresBackend
);
WorkerManagerModule.forRoot({
queues: [{ queue: invoices, adapter: BullMQAdapter }],
}),Redis and PostgreSQL queues can share one board.
Registering queue instances directly
@nestjs/bullmq generates the DI token for a queue from its name only, ignoring the
prefix. Two queues that share a name but use different prefixes therefore collapse onto a
single DI token, and a name-based lookup cannot tell them apart. Pass the queue instances
directly via queue to register them as distinct board entries:
@Module({
imports: [
WorkerManagerModule.forFeature(
{ queue: emailsTenantA, adapter: BullMQAdapter, options: { prefix: 'tenant-a:' } },
{ queue: emailsTenantB, adapter: BullMQAdapter, options: { prefix: 'tenant-b:' } },
),
],
})
export class FeatureModule {}Using the Worker Manager instance in your controllers and/or services.
The created Worker Manager instance is available via the @InjectWorkerManager() decorator.
For example in a controller:
import { Controller, Get } from "@nestjs/common";
import { WorkerManagerBoard, InjectWorkerManager } from "@worker-manager/nestjs";
@Controller('my-feature')
export class FeatureController {
constructor(
@InjectWorkerManager() private readonly boardInstance: WorkerManagerBoard
) {
}
//controller methods
}Usage examples
- Redis with
@nestjs/bullmqand basic auth - PostgreSQL-backed BullMQ v6 queues
- Keycloak auth from
ConfigService - Fastify platform with a custom auth hook
- The pg-boss board over the app's own pg-boss instance
Set it up with an AI agent
The Worker Manager agent skill
teaches a coding agent this module (forRoot/forRootAsync/forFeature, named boards,
engine: 'pg-boss', auth). In Claude Code: /plugin marketplace add naldomadeira/worker-manager,
then /plugin install worker-manager@worker-manager; other agents can unzip
worker-manager-skill.zip
into their skills folder.
For more info visit the main README
