API Reference
Every public method and option in @portqueue/core, laid out as a hierarchy: each
class and method is a tag on its own line, and the options a method accepts are
nested underneath it. Tags are just labels — anything you can click is rendered as an
ordinary link. Object-shaped options link to their own section, where every field is listed
with its type and default.
PortQueue
The top-level client. A thin facade over a driver: runs migrations on init(), wires the
shared services, and holds this process's queue declarations. Construction wires dependencies
only; no I/O until init().
- new PortQueue<TQueues>— create the client
- PortQueueOptions the only argument
- init()
→
Promise<void>— connect, migrate, start services (idempotent) - shutdown(options?)
→
Promise<void>— drain in-flight handlers and disconnect - get isRunning
→
boolean - migrate()
→
Promise<void>— force-apply pending migrations (the programmatic MANUAL path) - createQueue<TData, TResult>(options)
→
Queue<TData>— declare a queue and, with a
handler, consume it. There is nocreateWorker. - addJob<TData>({ queue, ...options })
→
Promise<AddJobResult>— one job, or a recurring scheduleAddJobOptions<TData> — plus
queue
- addJobs<TData>({ queue, jobs })
→
Promise<AddJobResult[]>— the whole batch in one insert, results in order. One-off jobs only; a recurring entry is rejected. - getQueues(query?)
→
Promise<Page<QueueInfo>> - getQueue({ name })
→
Promise<QueueInfo | null> - getQueueStats({ queue? })
→
Promise<QueueStats>— omitqueuefor client-wide - getQueuesStats()
→
Promise<QueueStats>— all queues combined - getJob<TData>({ jobId })
→
Promise<JobSnapshot | null> - getJobs<TData>(query?)
→
Promise<Page<JobSnapshot>> - getSchedules(query?)
→
Promise<Page<ScheduleInfo>>— one queue's withqueue, every queue's without it - getSchedule({ scheduleId })
→
Promise<ScheduleInfo | null>— by id alone; the result names its queue - retryJob({ jobId, tx? })
→
Promise<AddJobResult | null>— re-arms the job's own row.nullwhen the id is unknown or the job is ACTIVE: re-arming a running job would let a second worker claim it. Add a new job for a fresh run. - cancelJob({ jobId, tx? })
→
Promise<CancelOutcome>— reports what it did:CANCELLEDwhen the row is now cancelled,SIGNALLEDwhen a locally-running handler was only asked to stop,NOT_CANCELLABLEwhen the job has already finished or is running in another process. ThrowsJobNotFoundErrorfor an id that names no row. - removeJob({ jobId, tx? })
→
Promise<boolean>— deletes the row; skips anACTIVEjob and returnsfalse(cancel it first) removeJobs(({ jobIds } | { queue, statuses }) & { tx? })
→
Promise<number>— count deleted; skipsACTIVEids, and throws ifstatusesincludesACTIVE. A batch is one statement, sojobIdsis capped atMAX_BATCH_IDS(1000, the tightestIN-list limit of any supported engine) — pass more and it throwsInvalidOptionsErrorrather than failing in the driver.- removeSchedule({ scheduleId, tx? })
→
Promise<boolean> - on(event, listener, filter?)
→
Unsubscribe— see Events - once(event, listener, filter?)
→
Unsubscribe - off(event, listener)
→
void - useHook(hook)
→
Unsubscribe— see Hooks - pauseQueue({ queue, mode? })
→
Promise<void>—modeis a PauseMode - resumeQueue({ queue })
→
Promise<void> - removeQueue({ name })
→
Promise<boolean>— refused while the queue is declared in this process - getInstances(query?)
→
Promise<Page<InstanceInfo>>— see Scaling - getInstance({ instanceKey })
→
Promise<InstanceInfo | null> - pauseInstance({ instanceKey })
→
Promise<void> - resumeInstance({ instanceKey })
→
Promise<void> - removeInstance({ instanceKey })
→
Promise<boolean>— refused until the instance is unseen for 30 min
Queue
The handle pq.createQueue({ name, ...options }) returns. That call declares the queue in this
process — the handler that processes its jobs, the claim tuning around it, and the default job
options, retention policy and hooks its jobs inherit.
Claiming starts once pq.init() has run and the queue isn't paused, and stops on shutdown. Control
it with pq.pauseQueue / pq.resumeQueue, observe it through pq.on(...), and tune it with the
limiter / dontAutoretryFor fields of CreateQueueOptions. One handler per
queue per process: a second createQueue for the same name throws, so raise limiter.concurrency to
process more at once, or run another instance.
- name
→
string— the declared queue name, and the handle's only member
Job
The read-only view handed to a handler.
- id
string - queue
string - data
TData— the payload you enqueued - retryAttempt
number - retryMaxAttempts
number - priority
number - createdAt
Date - startAfter
Date - scheduleId?
string - idempotencyKey?
string - signal
AbortSignal— aborts on graceful-shutdown timeout / cooperative cancel - extendLock({ bySeconds? })
→
Promise<void>— extend the visibility timeout mid-handler, in seconds
Drivers
One self-contained class per engine, each from its own subpath. Storage layout
(schema / tablePrefix / namespace) lives on the driver, since it can differ per database.
Pass one driver instance to new PortQueue({ driver }).
- new PostgresqlDriver(options?)
@portqueue/core/drivers/postgresql- connectionString?
string - pool?
an existing
pg.Poolto share - config?
any
pg.PoolConfig - schema?
string - tablePrefix?
string· 'pq_' - namespace?
string
- connectionString?
- new MysqlDriver(options?)
@portqueue/core/drivers/mysql- connectionString?
mysql://… - pool?
an existing
mysql2pool - config?
any
mysql2pool config - schema?
string— database name - tablePrefix?
string· 'pq_' - namespace?
string
- connectionString?
- new OracleDriver(options?)
@portqueue/core/drivers/oracle- user?
string - password?
string - connectString?
string - maxIdentifierLen?
number— Oracle's identifier byte limit - schema?
string - tablePrefix?
string· 'pq_' - namespace?
string
- user?
- new SqliteDriver(options?)
@portqueue/core/drivers/sqlite- path?
string· ':memory:' - database?
an existing
better-sqlite3Databaseto share - tablePrefix?
string· 'pq_' - namespace?
string
- path?
- new PgliteDriver(options?)
@portqueue/core/drivers/pglite- dataDir?
string— omit for in-memory - schema?
string - tablePrefix?
string· 'pq_' - namespace?
string
- dataDir?
DB client packages (pg, mysql2, oracledb, better-sqlite3) are optional peer dependencies —
install the one for your engine.
To write your own driver, implement the Driver interface and declare a DriverCapabilitySet
(built with capabilities({ ... })). The full method signatures, the
DriverCapability enum, and the
schema identifier enums (TableName, QueueColumn, JobColumn,
ScheduleColumn, InstanceColumn, MigrationColumn) are documented on the
Advanced page. All of these
types — Driver, DriverProbe, Subscription, MigrationStep, DriverCapability, capabilities,
DriverCapabilitySet, and every schema identifier enum — are exported from @portqueue/core.
Options
Every duration in every options bag below is a number of seconds — there is no duration string to
parse, no other unit anywhere in the API, and exactly one option per setting (no Date-shaped
alternative to a delay). Names carry the unit (expireInSeconds, lockDurationSeconds, …) so a call
site can't be misread.
A duration describing a job — expireInSeconds, retry.delaySeconds, maxDelaySeconds,
ttlSeconds, startAfterSeconds, startEverySeconds, rateDurationSeconds — must be a whole
number of seconds: it is stored in an integer column, so a fraction is rejected rather than
truncated. The process-local loop cadences that are never stored — tickIntervalSeconds,
pollIntervalSeconds, lockDurationSeconds, heartbeatIntervalSeconds, sampleIntervalSeconds,
maxEventLoopDelaySeconds, timeoutSeconds — accept fractions, since sub-second timer intervals
and thresholds are meaningful there.
PortQueueOptions
The single argument to new PortQueue(...).
| Option | Type | Default | Description |
|---|---|---|---|
driver | Driver | — required | A driver instance (see Drivers). |
logger | Logger | LoggerOptions | console logger | A logger you supply, or { level?, pretty? } for the built-in one. |
migration | MigrationMode | AUTO | AUTO (apply pending on init), MANUAL (call migrate() yourself), or VALIDATE_ONLY (fail if pending). |
instance | InstanceOptions | — | This process's identity & heartbeat. |
systemLimiter | SystemLimiterOptions | — | Pressure thresholds that pause claiming. Omitted by default, so nothing throttles. |
tickIntervalSeconds | number | 5 | Scheduler / retention / monitor tick interval, in seconds. |
_headless | boolean | false | Internal. Marks an admin client — set by @portqueue/dashboard and @portqueue/cli, never by an application. See below. |
_headless
@portqueue/dashboard's createDashboard({ driver }) and @portqueue/cli build their own client
with this flag. It is documented here for completeness only: an application never sets it, and it is
not part of the surface those two packages promise.
A headless client observes and administers a deployment without joining it:
createQueuedeclares nothing and writes nothing — it hands back a typed name. Producing onto a queue that has no row throwsQueueNotFoundErrorinstead of inventing the queue.- It registers no instance row and rejects an
instance.instanceKey, so an admin process never appears in the instances list or claims affinity-targeted work. - It runs no workers, scheduler, retention sweep or health monitor — the application's processes own that work, and an admin tool quietly firing the cluster's schedules would be a second producer nobody asked for.
migrationdefaults toMANUAL, so pointing a dashboard at a database never migrates it as a side effect. (The CLI opts back intoAUTO: bringing a schema up to date is squarely its remit.)
Because inUse means "declared in the process that produced this info", it is always false on a
headless client — which is why the dashboard does not surface it.
ShutdownOptions
| Option | Type | Default | Description |
|---|---|---|---|
graceful | boolean | true | Let in-flight handlers finish before disconnecting. |
timeoutSeconds | number | 30 | Max time, in seconds, to wait for a graceful drain, and for background work (maintenance tick, heartbeat, notify) to finish before the driver disconnects. |
shutdown() rejects with ShutdownTimeoutError when that window elapses with work still in flight —
but only after the shutdown has otherwise completed, so the client always ends up stopped and the
driver disconnected. Ignoring the rejection still shuts down cleanly; logging it tells you handlers
were aborted mid-run.
InstanceOptions
| Option | Type | Default | Description |
|---|---|---|---|
instanceKey | string | anonymous | Stable, human-meaningful name for this process (e.g. 'worker-eu-1'). Naming it lets you target, list, and pause it; unnamed processes still run jobs. |
heartbeatIntervalSeconds | number | 15 | How often the instance row's last_seen_at is refreshed, in seconds. |
SystemLimiterOptions
Passing this object is what turns backpressure on. Every worker on the client then stops
claiming while any threshold you set is breached, and resumes once every watched metric is back at
or under its own limit. There is no per-worker opt-in: define a systemLimiter and
they all respect it, omit it (the default) and none do.
Only the thresholds you set are checked, and there are no defaults to merge with — {} samples but
can never throttle. Durations here may be fractional, since they are in-process sampling knobs
rather than stored values.
This process's CPU and heap are measured regardless — the instance heartbeat records them,
so the dashboard can show how each instance is doing, and there is no option to switch that off.
cpuPercent is CPU over the sample window as a share of all the cores available to the process
(0–100), and memoryPercent is used heap over V8's heap limit — neither is a machine-wide
figure, because a host memory reading counts allocated-but-idle memory as used and a container's
real ceiling is well below the host's. Event-loop delay is the exception on measurement: keeping a
perf_hooks histogram enabled costs something, so it is only sampled once
maxEventLoopDelaySeconds gives it a purpose. See
Rate limiting & backpressure.
| Option | Type | Default | Description |
|---|---|---|---|
sampleIntervalSeconds | number | 2 | How often to sample system metrics, in seconds. |
maxEventLoopDelaySeconds | number | unset | Mean event-loop delay above this, in seconds, counts as pressure (0.2 = 200 ms). Not measured at all until you set it. |
maxCpuPercent | number | unset | This process's CPU above this share (0–100) of the cores available to it counts as pressure — core-count independent, so 85 means the same on any box. |
maxMemoryPercent | number | unset | This process's used heap above this share (0–100) of V8's heap limit counts as pressure. |
CreateQueueOptions
The single argument to createQueue — everything about one queue in one call: the handler that
processes its jobs, the limiter / dontAutoretryFor settings that shape how it claims them, and the
defaultJobOptions, retention and hooks its jobs inherit. To enqueue without processing, skip
createQueue and call addJob — it creates the queue's row on first use.
| Option | Type | Default | Description |
|---|---|---|---|
name | TQueues | — required | The queue name every job, schedule and read is filed under. |
defaultJobOptions | JobOptions | — | Defaults every job this process adds to the queue inherits; a per-job option overrides. |
retention | RetentionOptions | keep forever | Queue default retention; resolved per-job and frozen onto each row. |
hooks | HookRegistration[] | [] | Queue-scoped participation hooks. |
handler | JobHandler<TData, TResult> | — required | The processor for this queue's jobs — declaring a queue is how a process takes on consuming it. |
limiter | LimiterOptions | defaults | Throughput & claim tuning for this queue's processing. |
dontAutoretryFor | Array<ErrorClass | string> | [] | Non-retryable errors, matched by instanceof or error.name. |
failureDetail | FailureDetail | STACK | How much of a failure is stored on the row. MESSAGE drops the stack frames — worth it for a queue that fails often, where the capped stack is still ~4 KB per failed row. |
Queue options are process-local: only the pause flags are ever persisted. A queue is declared
once per process — a second createQueue for the same name throws.
JobOptions
Per-job settings (highest precedence). Also the base of AddJobOptions.
| Option | Type | Default | Description |
|---|---|---|---|
priority | number | 0 | Higher runs first. |
retry | RetryOptions | run once | How the job retries on failure. |
expireInSeconds | number | — | Expire the job if it hasn't started within this many seconds. |
retention | RetentionOptions | inherit queue | Per-job retention override. |
idempotencyKey | string | — | A second enqueue with the same key returns the existing job. |
singletonKey | string | — | At most one active job per key at a time. |
tx | TransactionHandle | — | Transactional enqueue: your own transaction, wrapped by an adapter. |
AddJobOptions
JobOptions plus the fields below — the argument to addJob.
| Option | Type | Default | Description |
|---|---|---|---|
data | TData | — required | The job payload (a named key, never positional). |
schedule | ScheduleOptions | run now | One-off delay, fixed interval, or cron. |
preferredInstanceKeyPattern | string | any | Regex source matching instance keys this job may run on. See Scaling. |
RetryOptions
| Option | Type | Default | Description |
|---|---|---|---|
maxAttempts | number | 1 | Total attempts including the first (1 = no retry). |
delaySeconds | number | — | Delay before a retry, in seconds. |
backoff | BackoffOptions | — | Backoff applied to delaySeconds. |
BackoffOptions
| Option | Type | Default | Description |
|---|---|---|---|
strategy | BackoffStrategy |