Skip to content

Job Management

Use these methods on an initialized Monque instance. To expose them over HTTP, see the Management API. For a browser interface, see Dashboard setup.

getJob() accepts a MongoDB ObjectId or its hexadecimal string, including IDs returned by the Management API:

const job = await monque.getJob("507f1f77bcf86cd799439011");
if (job) {
  console.log(job.name, job.status, job.data);
}

Missing jobs and invalid ID strings return null.

Use getJobsWithCursor() to read jobs in pages:

const filter = { name: "send-email", status: "failed" } as const;
const sort = { by: "createdAt", direction: "desc" } as const;
const firstPage = await monque.getJobsWithCursor({ filter, sort, limit: 20 });

if (firstPage.hasNextPage && firstPage.cursor) {
  const nextPage = await monque.getJobsWithCursor({
    filter,
    sort,
    limit: 20,
    cursor: firstPage.cursor,
  });
  console.log(nextPage.jobs);
}

Keep the same filters and sort when following a cursor. To change either, start without a cursor. Job names match exactly. Filters also accept multiple statuses and inclusive date ranges for createdAt, updatedAt, and nextRunAt.

Sort by identifier, createdAt, updatedAt, or nextRunAt, in ascending or descending order. A job ID breaks ties between equal timestamps. See CursorOptions for all options.

Cursor queries have a 30-second server execution limit. A cursor does not freeze the collection: jobs may change or be deleted between requests.

All core listing methods accept a limit from 1 to 1,000, inclusive. The default is 100 for getJobs() and 50 for cursor listings. Zero, negative, fractional, non-finite, and larger limits throw InvalidJobQueryError; zero does not mean unlimited. Use successive pages for larger reads. Offset pagination’s skip must be a non-negative safe integer.

Use getJobSummariesWithCursor() when you do not need payloads:

const page = await monque.getJobSummariesWithCursor({
  filter: { name: "send-email" },
  limit: 50,
});

for (const job of page.jobs) {
  console.log(job._id, job.status, job.nextRunAt);
}

It accepts the same options and cursors as full listings, but excludes data from the query and returned jobs. Load a job’s payload with getJob() when needed.

Pass a hexadecimal job ID to change or delete a job:

const id = "507f1f77bcf86cd799439011";
const cancelled = await monque.cancelJob(id);
MethodEligible jobsResult
cancelJob(id)Pending jobsSets status to cancelled.
retryJob(id)Failed or cancelled jobsSets status to pending, clears failure state, and makes the job due immediately.
rescheduleJob(id, date)Pending jobsChanges nextRunAt.
setJobPriority(id, priority)Pending jobsChanges priority without changing the scheduled time.
deleteJob(id)Any statusPermanently removes the document.

Cancel, retry, reschedule, and priority changes return the updated job or null if it is missing. An incompatible state raises JobStateError. Repeated cancellation does not emit a second job:cancelled event. Deletion returns true when a document was removed, otherwise false. Deleting a processing job does not interrupt a handler that is already running.

const nextHour = new Date(Date.now() + 60 * 60 * 1000);
await monque.rescheduleJob(id, nextHour);
await monque.setJobPriority(id, 10); // Promote pending work
await monque.setJobPriority(id, -3); // Demote pending work

The priority must be a signed JavaScript safe integer. setJobPriority() rejects invalid or missing values with InvalidJobPriorityError before any write. With a valid priority, missing or malformed Job IDs return null. Only pending Jobs can change priority, including those scheduled for the future. If another Scheduler Instance claims the Job first, the change rejects with JobStateError. Running handlers are not interrupted.

The operation changes priority and updatedAt. It preserves the payload, schedule, identity, retry metadata, and ownership. The new priority stays in effect through retries, rescheduling, recovery, and later runs of a recurring Job.

Bulk methods act on all jobs matching the selector, including jobs outside the current page:

const cancelled = await monque.cancelJobs({ name: "send-email" });
const retried = await monque.retryJobs({ name: "send-email", status: "failed" });
const deleted = await monque.deleteJobs({
  status: ["completed", "cancelled"],
  olderThan: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000),
});

console.log(cancelled.count, retried.count, deleted.count);

cancelJobs() only changes pending jobs. retryJobs() only changes failed or cancelled jobs. deleteJobs() deletes every match. The selector’s olderThan and newerThan bounds apply to createdAt. An empty selector matches all eligible jobs.

Provided names must be non-empty strings. An empty name is rejected, rather than treated as an unfiltered selector. Status filters accept only the documented status values, and date bounds must be valid Date objects. Invalid filters throw InvalidJobQueryError before database execution. MongoDB operator objects and regular expressions are not accepted as filter values. These rules also apply to job listings and name-scoped statistics.

Each method returns { count, errors } and emits its corresponding bulk event. See JobSelector for selector fields.

getQueueStats() returns counts for each status:

const stats = await monque.getQueueStats();
console.log(stats.pending, stats.processing, stats.completed, stats.failed, stats.cancelled);

const emailStats = await monque.getQueueStats({ name: "send-email" });
console.log(emailStats.total);

statsCacheTtlMs controls cached counts for both getQueueStats() and getQueueViewSummaries(). The default is 5 seconds:

const monque = new Monque(db, { statsCacheTtlMs: 10_000 });

Repeated reads can return cached counts until the TTL expires. Concurrent reads with the same scope share a query, reducing database work when several clients request statistics. Job management actions on this instance invalidate cached counts. Worker registration and activity are read fresh, while changes from job processing or other instances may take up to the TTL to appear in counts.

Set statsCacheTtlMs: 0 to disable the cache. Concurrent reads still share a query. Stopping Monque clears cached statistics.

getQueueViewSummaries() groups persisted jobs and registered workers by job name:

const views = await monque.getQueueViewSummaries();

for (const view of views) {
  console.log(view.name, view.stats.total, view.worker?.activeCount ?? 0);
}

Each summary includes counts, hasPersistedJobs, hasRegisteredWorker, and the local worker’s concurrency and activeCount when registered. Names are sorted alphabetically. Local worker summaries also report effective paused state, hasSchema, maxRetries, baseRetryInterval, and maxBackoffDelay. Retry delays are milliseconds and include inherited scheduler defaults. Worker state is refreshed on every call, even when counts are cached. Pauses apply only to this scheduler instance; active executions retain their original retry policy after a worker is replaced. A name can appear with historical jobs but no worker, or with a registered worker and no persisted jobs.

Pass { name: 'send-email' } to scope the database aggregation and result to one exact Job Name. Unknown names return an empty list; registered-worker-only names remain visible. Filtered statistics use separate cache entries from the unfiltered overview.

Counts cover the MongoDB collection. Worker activity describes only the instance on which you called the method, not all schedulers connected to that collection.