Extensions
Queue
Push slow work off the request path. @stone-js/queue lets you dispatch a job now or later, process it with a worker, and retry it with backoff, over a memory queue (zero-config) or Redis. Dispatch is injected as queue; handlers are plain, dependency-injected classes.
#Install
npm i @stone-js/queue
npm i ioredis # only for the Redis connection#Enable it
The principle
A request should return as soon as the user's intent is captured. The work it triggers, emails, thumbnails, webhooks, belongs on a queue, not in the response.
In Stone.js
One QueueConnection contract backs every driver. The provider binds the manager as queueManager, the default connection as queue, the handler registry, and a worker.
import { StoneApp } from '@stone-js/core'
import { Queue } from '@stone-js/queue'
@Queue({ driver: 'redis', url: 'redis://localhost:6379' })
@StoneApp({ name: 'app' })
export class Application {}import { defineConfig } from '@stone-js/core'
import { defineQueue } from '@stone-js/queue'
export const AppConfig = defineConfig(defineQueue({
default: 'redis',
connections: [
{ name: 'redis', driver: 'redis', url: 'redis://localhost:6379', prefix: 'jobs' },
{ name: 'sync', driver: 'memory' }
]
}))#Dispatch
export class OrderService {
constructor (private readonly queue) {}
async checkout (order) {
// returns immediately; the receipt is sent by a worker
await this.queue.dispatch('send-receipt', { orderId: order.id }, { delay: 5, maxAttempts: 3, backoff: 10 })
}
}#Handle
import { JobHandler } from '@stone-js/queue'
@JobHandler('send-receipt')
export class SendReceipt {
constructor (private readonly mailer) {} // dependency-injected
async handle (payload: { orderId: string }) {
await this.mailer.receipt(payload.orderId)
}
}One class can also handle several jobs, one method each, with @OnJob (a name-less @JobHandler() marks the class for scanning):
import { JobHandler, OnJob } from '@stone-js/queue'
@JobHandler()
export class Jobs {
@OnJob('resize') async resize (payload) { /* … */ }
@OnJob('purge') async purge (payload) { /* … */ }
}#Process
Run the worker in a long-running process. It reserves each job, runs its handler and acks it; on failure it retries with linear backoff up to maxAttempts, then dead-letters.
export class Consumer {
constructor (private readonly worker) {}
async start () { await this.worker.run({ queues: ['default'], sleep: 1000 }) }
}#Drivers
| driver | Type | Description |
|---|---|---|
| memory | built-in | In-process, with delay, retries and a dead-letter list. Zero-config default; single process. |
| redis | ioredis | Shared and reliable: ready LIST + delayed ZSET + processing LIST (crash-safe reservation). |
| provider | coming next | SQS, Pub/Sub, Azure Storage Queue, wired to the FaaS adapters. |