Step Queue

QueueSystem

Full API reference for QueueSystem, the fluent builder that owns a BullMQ Queue and Worker.

QueueSystem is the fluent builder that owns a BullMQ Queue and Worker. It enqueues jobs and defines the steps that process them.

class QueueSystem<
  TInput extends JSONLike = JSONLike,
  TOutput extends JSONLike = JSONLike,
  TName extends string = string,
  TConnected extends boolean = boolean,
  TStarted extends boolean = boolean,
>

Constructor

new QueueSystem(nameConfig: { name: string; displayName: string })

nameConfig is typically the output of NameFactory.

Methods

defineInputSchema(schema)

defineInputSchema<TSchema extends JSONLike>(
  schema: StandardSchemaV1<unknown, TSchema>,
): QueueSystem<TSchema, TOutput, TName, TConnected, TStarted>

Sets the Standard Schema that types and validates job input. Infers the initial job.data type for the first step. If the schema’s vendor supports JSON Schema conversion, it also populates jsonSchema. See Typed Input & Schema Validation.

defineConnection(connection)

defineConnection(
  connection: ConnectionOptions,
): QueueSystem<TInput, TOutput, TName, true, TStarted>

Sets the BullMQ ConnectionOptions (Redis). Required before defineProcessor. Narrows queue from Queue | undefined to Queue.

defineConcurrency(n)

defineConcurrency(concurrency: number): this

Sets worker concurrency. Defaults to 1. Must be set before defineProcessor, since that’s when the Worker is constructed.

defineProcessor(buildOrBuilder)

defineProcessor<TNewOutput extends JSONLike>(
  this: QueueSystem<TInput, TOutput, TName, true, TStarted>,
  build: (
    builder: ProcessorBuilder<TInput, TInput, TInput>,
  ) => ProcessorBuilder<TInput, TInput, TNewOutput, ErrorRegistry>,
): QueueSystem<TInput, TNewOutput, TName, true, true>;

defineProcessor<TNewInput extends TInput, TNewOutput extends JSONLike>(
  this: QueueSystem<TInput, TOutput, TName, true, TStarted>,
  builder: ProcessorBuilder<TNewInput, TNewInput, TNewOutput, ErrorRegistry>,
): QueueSystem<TNewInput, TNewOutput, TName, true, true>;

Declares steps and starts the Queue + Worker. Takes either a callback receiving a typed ProcessorBuilder, or a standalone ProcessorBuilder instance (un-built, via ProcessorBuilder.init<T>()). Requires a connected system: calling it before defineConnection is a compile error via the this parameter, with a matching runtime guard (defineConnection(...) must be called before defineProcessor(...)) for callers that bypass the type check. Narrows queue and worker to non-optional. See ProcessorBuilder reference.

addDescription(text) / addToSystem(text)

addDescription(description: string): this
addToSystem(system: string): this

Free-form metadata fields for organizing queues; not used by the engine itself. Stored on description and system.

addJob(data, options?)

addJob(jobData: TInput, options?: JobsOptions): Promise<Job<TInput, TOutput, TName>>

Validates (if defineInputSchema was called) and enqueues one job, merging default job options with any options passed. Throws SchemaValidationError on invalid data, or a plain Error if the queue hasn’t been initialized yet (defineConnection not called).

addBulkJobs(jobs)

addBulkJobs(
  jobs: Array<{ data: TInput; opts?: JobsOptions }>,
): Promise<Job<TInput, TOutput, TName>[]>

Validates and enqueues many jobs in one call.

addChildJob(parentJob, data, options?)

addChildJob(
  parentJob: Job | TypedJob<JSONLike, ErrorRegistry>,
  jobData: TInput,
  options?: JobsOptions,
): Promise<Job<TInput, TOutput, TName>>

Enqueues a job as a BullMQ child of parentJob. Throws if parentJob lacks an id or queueQualifiedName. See Waiting on Child Jobs.

addBulkChildJobs(parentJob, jobs)

addBulkChildJobs(
  parentJob: Job | TypedJob<JSONLike, ErrorRegistry>,
  jobs: Array<{ data: TInput; opts?: JobsOptions }>,
): Promise<Job<TInput, TOutput, TName>[]>

Bulk version of addChildJob.

addWorkerEventListener(event, cb) / removeWorkerEventListener(event, cb)

addWorkerEventListener<E extends keyof WorkerListener>(
  eventName: E,
  callback: WorkerListener<TInput, TOutput, TName>[E],
): void

removeWorkerEventListener<E extends keyof WorkerListener>(
  eventName: E,
  callback: WorkerListener<TInput, TOutput, TName>[E],
): void

Typed wrappers around the underlying Worker’s event emitter. addWorkerEventListener throws if the worker isn’t initialized yet (before defineProcessor). See Workers, Concurrency & Progress.

close()

close(): Promise<void>

Gracefully closes the worker, queue, and queue events.

Properties

PropertyTypeNotes
nameTNameThe BullMQ queue key (from NameFactory).
displayNamestringHuman-readable name (from NameFactory).
systemstring | undefinedSet via addToSystem.
descriptionstring | undefinedSet via addDescription.
jsonSchemaRecord<string, unknown> | undefinedPopulated by defineInputSchema when the schema vendor supports JSON Schema conversion.
queueQueue | undefined, narrows to Queue after defineConnectionThe underlying BullMQ queue, created on first access once a connection has been defined.
workerWorker | undefined, narrows to Worker after defineProcessorThe underlying BullMQ worker.
QueueEventsQueueEvents (getter)Lazily-created QueueEvents instance, needed by job.waitUntilFinished(queue.QueueEvents, timeout). Throws if accessed before defineConnection.

Lifecycle narrowing

QueueSystem carries two optional lifecycle type parameters that track how far the builder has been driven, so queue and worker stop being optional once the call that creates them has run, no ?. and no ! needed.

const pending = new QueueSystem(NameFactory({ name: "MyCoolQueue" }));
pending.queue; // Queue | undefined
pending.worker; // Worker | undefined

const connected = pending.defineConnection({ host: "localhost", port: 6379 });
connected.queue; // Queue
connected.worker; // Worker | undefined — not started yet

const running = connected.defineProcessor((step) =>
  step.addStep({ name: "greet", handler: async () => ({ ok: true }) }),
);
running.queue; // Queue
running.worker; // Worker

Both parameters default to boolean, meaning “either state”, so an existing annotation like QueueSystem<In, Out, Name> still accepts a system at any stage and behaves exactly as it did before. Write them explicitly only when you want to require a stage:

// Only accepts a system that has been connected.
function enqueueOnly<In extends JSONLike>(q: QueueSystem<In, JSONLike, string, true>) {
  return q.queue; // Queue, no narrowing needed
}

The same mechanism enforces call order. defineProcessor declares a this parameter requiring a connected system, so getting the order wrong fails to compile rather than throwing at startup:

new QueueSystem(NameFactory({ name: "MyCoolQueue" })).defineProcessor((step) =>
  step.addStep({ name: "greet", handler: async () => ({ ok: true }) }),
);
// ^ error: The 'this' context of type 'QueueSystem<..., boolean, boolean>' is not
//   assignable to method's 'this' of type 'QueueSystem<..., true, boolean>'.

QueueInput<T> / QueueOutput<T>

type QueueInput<T> = T extends QueueSystem<infer I, any, any, any, any> ? I : never;
type QueueOutput<T> = T extends QueueSystem<any, infer O, any, any, any> ? O : never;

Extract a queue’s input/output types for use elsewhere: for example, typing a function that only enqueues jobs for an already-built queue.

import type { QueueInput, QueueOutput } from "@michaelrwalker/step-queue";

type MyInput = QueueInput<typeof myQueue>;
type MyOutput = QueueOutput<typeof myQueue>;