Skip to main content
The @talosjs/queue component is a background job layer built on BullMQ. You extend the abstract Queue base class, declare the data your jobs carry, and implement a handler that processes each job. Your subclass provides the BullMQ Queue and Worker; the base class wires the common producer helpers (add, addBulk, removeJob, close) and forwards worker lifecycle events to optional hooks on your class.

Producer and consumer in one class

Extending Queue<T, R> gives you typed add, addBulk, removeJob, and close methods, plus a worker that runs your handler once your subclass constructs it. The job data type T (a record of scalars) and the return type R flow through add and handler, keeping producers and consumers in sync. You can implement optional onCompleted, onFailed, onError, and other hooks, and the base class binds them to the worker when your subclass calls registerEvents(). Jobs persist in Redis through BullMQ, so they survive restarts and scale across worker instances. Register a queue class with a decorator and resolve it from the container.

How it works

A queue class extends Queue<T, R>, where T extends Record<string, ScalarType> is the job data and R is the value the handler returns. Inside the constructor you create a BullMQ Queue (the producer) and a Worker (the consumer) over a shared Redis connection, then call registerEvents() to bind your hooks. The producer side enqueues work: The consumer side processes work. You implement the abstract handler:
QueueHandlerReturnType<R> is Promise<R> | R, so the handler may be sync or async. The worker invokes it for each job and the returned value becomes the job result (surfaced to onCompleted). Worker lifecycle events are optional hooks on IQueue. Declare any of them and registerEvents() attaches them:

Decorator and usage

@decorator.queue()

Registers a queue class with the container. It accepts an optional scope (defaults to singleton). Apply it to a class that extends Queue.
Resolve it from the container, then enqueue jobs on the producer side. The worker (consumer) runs handler for each:
Inject the queue where you produce jobs:

Exceptions

The package exports QueueException as the typed error for queue-related failures in your own subclasses. The abstract Queue base class does not throw one on its own; you choose the keys and messages in the subclass that creates the BullMQ connection and worker.

Running queues well

Keep job data small and serializable. It’s a record of scalars (string, number, bigint, boolean), so pass ids and look up the rest in the handler rather than embedding large payloads. Make handlers idempotent too: jobs can be retried after a failure or a stall, so processing the same job twice has to be safe. Always attach onError. Without it, worker-level errors go unhandled, so implement it (and usually onFailed) on every queue. Configure attempts with exponential backoff so flaky downstream calls recover instead of dropping the job, and use removeOnComplete and removeOnFail so finished jobs don’t pile up on a shared instance. Match concurrency and limiter to what the handler’s downstream resources can sustain. Finally, call close() on shutdown to drain the worker and release Redis connections cleanly.

CLI command

Scaffold a queue class and its test file with the generator. It writes the class under modules/<module>/src/queues/<Name>Queue.ts and installs @talosjs/queue if it is missing.
The generated class builds the BullMQ Queue and Worker from QUEUE_REDIS_URL, registers its event hooks, and leaves a handler for you to implement.
See queue:create for the full command reference.

Use with Claude and Codex

The generator ships a matching queue:create skill. It runs the scaffold, then guides your AI agent through completing the queue: implementing handler, adding typed add/addBulk wrappers for the jobs it carries, and ensuring QUEUE_REDIS_URL is set. Initialize the skills once for your agent.
Then ask Claude in natural language. It maps the request to the generator, runs it, and fills in the implementation:
Prompt
For example, the prompt above maps to queue:create --name=Email, then implements the handler to send each queued email.