Jobs API
Fetching jobs, awaiting results, streaming partials, cancellation, listing, and errors.
Fetching jobs, awaiting results, streaming partials, cancellation, listing, and errors.
| Method | Description |
|---|---|
getJob(id) → Job | null | Fetch a job's current state. |
getResult(id) | Deserialized result of a completed job, or undefined if not yet ready. |
result(id, { timeoutMs?, pollMs? }) | Await a job's terminal result; rejects on failure/cancellation/timeout. |
stream(id, { timeoutMs?, pollMs? }) | Async-iterate a job's published partials. |
requestCancel(id) | Request cooperative cancellation of a running job. |
isCancelRequested(id) | Whether cancellation has been requested for a job. |
cancelJob(id) | Cancel a pending job. |
Scan-heavy calls (marked Promise below) run off the JS event loop on a
background thread pool.
| Method | Description |
|---|---|
listJobs(filter) → Promise<Job[]> | List jobs by status, queue or task, newest first. |
listJobsFiltered(filter) → Promise<Job[]> | The wider filter: everything listJobs matches on, plus metadata/error substrings and a created-at range. |
listJobsAfter(filter?, after?) → Promise<Page<Job>> | Keyset-paginated listJobs, ordered by created time. The filter's offset is ignored — the cursor replaces it. |
listJobsFilteredAfter(filter?, after?) → Promise<Page<Job>> | Keyset-paginated listJobsFiltered, same cursor contract. |
getJobErrors(id) → Promise<JobError[]> | Every failed attempt, oldest first. Retries append rather than replace, so this is the record of why a job reached the dead-letter queue, not just what the last attempt said. |
A Page carries items and nextCursor; pass the cursor back as after for
the following page, and null means that was the last one. Treat it as opaque —
do not build or parse one.
Use the keyset forms to walk a listing rather than page it. An offset re-scans every row it skips, so a deep page costs more than a shallow one, and a job enqueued mid-walk shifts every later offset by one — rows get seen twice or missed entirely. A keyset page is O(page) at any depth and stable under concurrent inserts.
On SQLite and Postgres the cursor becomes an index seek. Redis status indexes are not seekable, so the keyset is applied in memory: still correct and still stable, but the cost tracks the number of matching rows rather than the page size. Filter narrowly there.
| Method | Description |
|---|---|
replay(id) → Promise<string> | Re-enqueue a copy of a job with its original payload, and record the replay. A new job, not a resumed one: fresh id, fresh retry budget, and the original row untouched. Resolves with the new id. Declared as replayJob. |
replayHistory(id) → Promise<ReplayEntry[]> | Every replay of a job, newest first: id, originalJobId, replayJobId, replayedAt (Unix ms), originalError, replayError. The audit trail for "we retried this by hand", which is otherwise invisible — a replay looks like an unrelated job. Declared as getReplayHistory. |
jobDag(id) → Promise<JobDag> | The dependency graph reachable from a job: { nodes, edges }, where each edge points dependency → dependent. Transitive in both directions, so it answers "what is this waiting on, and what is waiting on it" in one call. |
Dependency edges never cross namespaces — an enqueue that would create one is rejected — so the graph is scoped by its anchor job. A job in another namespace comes back with no edges, exactly like an unknown id.
Terminal jobs are moved out of the hot jobs table into the archive, so
listings and dispatch scans stay fast without losing the history. The queue does
not archive on demand: the sweep belongs to the worker, and the archive has its
own retention window. These two
read it back.
| Method | Description |
|---|---|
listArchived(limit?, offset?) → Promise<Job[]> | The archive, newest first, offset-paginated. Fine for one page. |
listArchivedAfter(limit?, after?) → Promise<Page<Job>> | The keyset twin, ordered by completed time. Pass a page's nextCursor back as after; null means that was the last page. |
Prefer the keyset form for a full walk: an offset re-scans everything it skips, and a job archived mid-walk shifts every later offset by one, so rows get seen twice or not at all. Cursors are opaque — do not build or parse one.