Stone.jsDocs
Paradigm

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

terminalbash
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.

app/Application.tsdeclarativeimperative
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

app/OrderService.tsts
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

app/SendReceipt.tsts
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):

app/Jobs.tsts
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.

app/Consumer.tsts
export class Consumer {
  constructor (private readonly worker) {}
  async start () { await this.worker.run({ queues: ['default'], sleep: 1000 }) }
}

#Drivers

driverTypeDescription
memorybuilt-inIn-process, with delay, retries and a dead-letter list. Zero-config default; single process.
redisioredisShared and reliable: ready LIST + delayed ZSET + processing LIST (crash-safe reservation).
providercoming nextSQS, Pub/Sub, Azure Storage Queue, wired to the FaaS adapters.

Stone.js

Your app exists in every runtime. Until you run it.

An open-source project by Stone Foundation
Created by Mr. Stone (Evens Pierre)