Skip to main content

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
  • 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 no createWorker.

  • addJob<TData>({ queue, ...options })

    → Promise<AddJobResult> — one job, or a recurring schedule

  • 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> — omit queue for 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 with queue, 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. null when 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: CANCELLED when the row is now cancelled, SIGNALLED when a locally-running handler was only asked to stop, NOT_CANCELLABLE when the job has already finished or is running in another process. Throws JobNotFoundError for an id that names no row.

  • removeJob({ jobId, tx? })

    → Promise<boolean> — deletes the row; skips an ACTIVE job and returns false (cancel it first)

  • removeJobs(({ jobIds } | { queue, statuses }) & { tx? })

    → Promise<number> — count deleted; skips ACTIVE ids, and throws if statuses includes ACTIVE. A batch is one statement, so jobIds is capped at MAX_BATCH_IDS (1000, the tightest IN-list limit of any supported engine) — pass more and it throws InvalidOptionsError rather 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> — mode is 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.Pool to share

    • config?

      any pg.PoolConfig

    • schema? string
    • tablePrefix?

      string · 'pq_'

    • namespace? string
  • new MysqlDriver(options?) @portqueue/core/drivers/mysql
    • connectionString? mysql://…
    • pool?

      an existing mysql2 pool

    • config?

      any mysql2 pool config

    • schema?

      string — database name

    • tablePrefix?

      string · 'pq_'

    • namespace? string
  • 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
  • new SqliteDriver(options?) @portqueue/core/drivers/sqlite
    • path?

      string · ':memory:'

    • database?

      an existing better-sqlite3 Database to share

    • tablePrefix?

      string · 'pq_'

    • namespace? string
  • new PgliteDriver(options?) @portqueue/core/drivers/pglite
    • dataDir?

      string — omit for in-memory

    • schema? string
    • tablePrefix?

      string · 'pq_'

    • namespace? string

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(...).

OptionTypeDefaultDescription
driverDriver— requiredA driver instance (see Drivers).
loggerLogger | LoggerOptionsconsole loggerA logger you supply, or { level?, pretty? } for the built-in one.
migrationMigrationModeAUTOAUTO (apply pending on init), MANUAL (call migrate() yourself), or VALIDATE_ONLY (fail if pending).
instanceInstanceOptions—This process's identity & heartbeat.
systemLimiterSystemLimiterOptions—Pressure thresholds that pause claiming. Omitted by default, so nothing throttles.
tickIntervalSecondsnumber5Scheduler / retention / monitor tick interval, in seconds.
_headlessbooleanfalseInternal. 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:

  • createQueue declares nothing and writes nothing — it hands back a typed name. Producing onto a queue that has no row throws QueueNotFoundError instead 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.
  • migration defaults to MANUAL, so pointing a dashboard at a database never migrates it as a side effect. (The CLI opts back into AUTO: 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​

OptionTypeDefaultDescription
gracefulbooleantrueLet in-flight handlers finish before disconnecting.
timeoutSecondsnumber30Max 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​

OptionTypeDefaultDescription
instanceKeystringanonymousStable, 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.
heartbeatIntervalSecondsnumber15How 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.

OptionTypeDefaultDescription
sampleIntervalSecondsnumber2How often to sample system metrics, in seconds.
maxEventLoopDelaySecondsnumberunsetMean event-loop delay above this, in seconds, counts as pressure (0.2 = 200 ms). Not measured at all until you set it.
maxCpuPercentnumberunsetThis 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.
maxMemoryPercentnumberunsetThis 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.

OptionTypeDefaultDescription
nameTQueues— requiredThe queue name every job, schedule and read is filed under.
defaultJobOptionsJobOptions—Defaults every job this process adds to the queue inherits; a per-job option overrides.
retentionRetentionOptionskeep foreverQueue default retention; resolved per-job and frozen onto each row.
hooksHookRegistration[][]Queue-scoped participation hooks.
handlerJobHandler<TData, TResult>— requiredThe processor for this queue's jobs — declaring a queue is how a process takes on consuming it.
limiterLimiterOptionsdefaultsThroughput & claim tuning for this queue's processing.
dontAutoretryForArray<ErrorClass | string>[]Non-retryable errors, matched by instanceof or error.name.
failureDetailFailureDetailSTACKHow 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.

OptionTypeDefaultDescription
prioritynumber0Higher runs first.
retryRetryOptionsrun onceHow the job retries on failure.
expireInSecondsnumber—Expire the job if it hasn't started within this many seconds.
retentionRetentionOptionsinherit queuePer-job retention override.
idempotencyKeystring—A second enqueue with the same key returns the existing job.
singletonKeystring—At most one active job per key at a time.
txTransactionHandle—Transactional enqueue: your own transaction, wrapped by an adapter.

AddJobOptions​

JobOptions plus the fields below — the argument to addJob.

OptionTypeDefaultDescription
dataTData— requiredThe job payload (a named key, never positional).
scheduleScheduleOptionsrun nowOne-off delay, fixed interval, or cron.
preferredInstanceKeyPatternstringanyRegex source matching instance keys this job may run on. See Scaling.

RetryOptions​

OptionTypeDefaultDescription
maxAttemptsnumber1Total attempts including the first (1 = no retry).
delaySecondsnumber—Delay before a retry, in seconds.
backoffBackoffOptions—Backoff applied to delaySeconds.

BackoffOptions​

OptionTypeDefaultDescription
strategyBackoffStrategy— requiredFIXED or EXPONENTIAL.
factornumber2Growth factor for EXPONENTIAL; ignored for FIXED.
maxDelaySecondsnumber—Cap on the computed delay, in seconds.
jitterbooleanfalseRandomize the delay to spread retries across instances.

RetentionOptions​

Set retention and finished jobs are deleted once their TTL elapses; omit it and they are kept forever. There is no mode to pick.

OptionTypeDefaultDescription
ttlSecondsnumber— requiredHow many seconds after finishing before the job is deleted.
statusesJobStatus[]every finished statusWhich finished statuses the policy applies to (COMPLETED, FAILED, CANCELLED, EXPIRED unless narrowed).

ScheduleOptions​

Exactly one timing mode may be set.

OptionTypeDefaultDescription
startAfterSecondsnumber— (runs now)One-off: delay this many seconds from now.
startEverySecondsnumber— (not recurring)Recurring: seconds between runs, e.g. 30.
cronCronSpec— (not recurring)Recurring: a cron string, or { pattern, tz }.

LimiterOptions​

The consumer tuning group on CreateQueueOptions. All process-local — no cluster-wide coordination.

OptionTypeDefaultDescription
concurrencynumber1Max handlers running at once for this queue in this process.
rateMaxnumber—Max jobs started per rateDurationSeconds window (set with rateDurationSeconds).
rateDurationSecondsnumber—The window for rateMax, in seconds — e.g. 60.
batchSizenumberconcurrencyMax jobs claimed per fetch.
pollIntervalSecondsnumber5Poll cadence when LISTEN/NOTIFY is unavailable, in seconds.
lockDurationSecondsnumber30Visibility timeout / heartbeat window for a claimed job, in seconds.

PageQuery​

The paging window every list read accepts. getJobs, getSchedules and getInstances all extend it, so limit/offset mean the same thing wherever they appear, and each returns a Page<T> of { items, total }.

OptionTypeDefaultDescription
limitnumber50Page size, capped at 500.
offsetnumber0Row offset, for numbered pagination.

GetJobsQuery​

PageQuery plus the job filters. Jobs always come back newest first; there is no sort option.

OptionTypeDefaultDescription
queuestringall queuesFilter to one queue.
statusesJobStatus[]all statusesFilter by status.
scheduleIdstring—Jobs minted by one schedule.
limitnumber50Page size, capped at 500.
offsetnumber0Row offset, for numbered pagination.

GetSchedulesQuery​

PageQuery plus two filters, for pq.getSchedules. Returns a Page<ScheduleInfo>.

OptionTypeDefaultDescription
queuestringall queuesNarrow to one queue's schedules.
enabledbooleanbothtrue → only armed schedules, false → only disarmed, omitted → both.
limitnumber50Page size, capped at 500.
offsetnumber0Row offset, for numbered pagination.

GetInstancesQuery​

PageQuery plus the narrowings, for pq.getInstances(). Returns a Page<InstanceInfo>. Every filter runs in SQL, so finding the paused or stale instances is a query rather than a page-and-filter.

OptionTypeDefaultDescription
searchstring—Case-insensitive substring of the instance key.
processingPausedbooleanbothtrue → only paused instances, false → only running, omitted → both.
alivebooleanbothtrue → only instances inside the liveness window, false → only stale.
limitnumber50Page size, capped at 500.
offsetnumber0Row offset, for numbered pagination.

GetQueuesQuery​

PageQuery plus the two narrowings, for pq.getQueues(). Returns a Page<QueueInfo> — each row with its per-status counts, and total counted over the same filter as the page.

OptionTypeDefaultDescription
searchstring—Case-insensitive substring of the queue name.
processingPausedbooleanbothtrue → only paused queues, false → only running, omitted → both.
limitnumber50Page size, capped at 500.
offsetnumber0Row offset, for numbered pagination.

Result types​

Reads return objects, never bare ids. The main shapes:

  • AddJobResult — jobId?, scheduleId?, nextRunAt?, queue, status?, deduplicated, deduplicationReason?, startAfter?.
  • JobSnapshot — the full read-side view of a job (all timestamps as Date, plus a derived stalled flag).
  • ScheduleInfo — scheduleId, queue, cron?, everySeconds?, timezone?, nextRunAt?, lastRunAt?, enabled, idempotencyKey?.
  • QueueInfo — name, processingPaused, acceptingPaused, counts, inUse (inUse is process-local: true when this client declared the queue).
  • InstanceInfo — instanceKey, processingPaused, alive, createdAt, updatedAt, lastSeenAt, counts, plus cpuPercent/memoryPercent — that process's CPU (share of the cores available to it) and heap (share of V8's limit), both 0–100, at its last heartbeat, null before the first beat.
  • QueueStats — queue?, counts (a JobStatus → count map).
  • Page<T> — items and total (rows matching the query, ignoring paging). There is no hasMore flag: offset + items.length < total tells you whether another page follows.

Enums​

JobStatus (PENDING, ACTIVE, COMPLETED, FAILED, RETRY, CANCELLED, EXPIRED) · PauseMode (PROCESSING, ALL) · BackoffStrategy (FIXED, EXPONENTIAL) · MigrationMode (AUTO, MANUAL, VALIDATE_ONLY) · DeduplicationReason (IDEMPOTENCY, SINGLETON) · CancelOutcome (CANCELLED, SIGNALLED, NOT_CANCELLABLE) · FailureDetail (STACK, MESSAGE) · LogLevel (TRACE, DEBUG, INFO, WARN, ERROR, FATAL) · DatabaseEngine · PortQueueEvent · HookPhase · PortQueueErrorCode.


Errors​

Every error thrown by @portqueue/core is an instance of PortQueueError (or a subclass) and carries a .code (PortQueueErrorCode) and a .context object. All are importable from @portqueue/core, so you can branch with instanceof:

PortQueueError (base) · NotStartedError · InvalidOptionsError · UnsupportedCapabilityError · SchemaVersionMismatchError · MigrationFailedError · DriverConnectionError · DriverQueryError · JobNotFoundError · QueueNotFoundError · QueuePausedError · QueueInUseError · InstanceActiveError · ShutdownTimeoutError · LockLostError · MissingPeerDependencyError.

The full error table — codes, causes, and remedies — lives in Troubleshooting.