Appearance
Queue
Holo-JS ships queue support as a first-class server subsystem. New projects already scaffold config/queue.ts, create server/jobs, and default to the sync driver so dispatch works on day one.
Existing projects
Install queue support into an existing Holo-JS app with:
bash
npx holo install queuePick a starting driver during install when needed:
bash
npx holo install queue --driver redis
npx holo install queue --driver databaseWhat queue owns
The queue subsystem gives you:
- a typed
config/queue.ts - auto-discovered jobs under
server/jobs - dispatch helpers
- long-lived worker commands for async drivers
- failed-job management
- first-party media integration for queued conversions
Queue does not own event contracts. Event contracts and listener orchestration are documented under Events. Queued listeners use queue internally.
Queue config
New apps start with sync:
ts
import { defineQueueConfig } from '@holo-js/queue'
export default defineQueueConfig({
default: 'sync',
failed: {
driver: 'database',
connection: 'default',
table: 'failed_jobs',
},
connections: {
sync: {
driver: 'sync',
queue: 'default',
},
},
})sync runs the job inline. It is the simplest default and needs no worker process. Async drivers need a separate worker runtime in production. See Deployment for host selection and Workers for worker commands.
End-to-end driver examples
Sync
Use sync when you want immediate execution in local development, tests, or low-volume app flows.
ts
import SendDigest from '../jobs/reports/send-digest'
await SendDigest.dispatch({
reportId: 'daily-summary',
})No worker process is required.
Redis
Use redis for normal asynchronous work:
ts
// config/redis.ts
import { env } from '@holo-js/config'
import { defineRedisConfig } from '@holo-js/kernel'
export default defineRedisConfig({
default: 'cache',
connections: {
cache: {
url: env('REDIS_URL') || undefined,
host: env('REDIS_HOST', '127.0.0.1'),
port: env('REDIS_PORT', 6379),
username: env('REDIS_USERNAME'),
password: env('REDIS_PASSWORD'),
db: env('REDIS_DB', 0),
},
},
})ts
// config/queue.ts
import { defineQueueConfig } from '@holo-js/queue'
export default defineQueueConfig({
default: 'redis',
failed: {
driver: 'database',
connection: 'default',
table: 'failed_jobs',
},
connections: {
redis: {
driver: 'redis',
connection: 'cache',
queue: 'default',
retryAfter: 90,
blockFor: 5,
},
},
})Queue only needs the shared Redis connection name. The actual Redis target lives in config/redis.ts.
Shared Redis connections resolve in this order:
urlclustershost
See Configuration for examples of all three connection styles.
Run a worker:
bash
npx holo queue:work --connection redisDatabase
Use database when you want queue portability and are comfortable polling from your app database:
ts
import { defineQueueConfig } from '@holo-js/queue'
export default defineQueueConfig({
default: 'database',
failed: {
driver: 'database',
connection: 'default',
table: 'failed_jobs',
},
connections: {
database: {
driver: 'database',
connection: 'default',
table: 'jobs',
queue: 'default',
retryAfter: 90,
sleep: 1,
},
},
})Generate the required tables and migrate:
bash
npx holo queue:table
npx holo queue:failed-table
npx holo migrateThen run a worker:
bash
npx holo queue:work --connection databaseQueue names and connections
- A connection selects the backend driver configuration such as
sync,redis, ordatabase. - A queue name lets you partition work inside that connection, such as
default,emails, ormedia. - Jobs can set defaults, and dispatch can override them per call.
Dispatch overview
Use the job definition for typed queued execution:
ts
import SendDigest from '../jobs/reports/send-digest'
await SendDigest.dispatch({
reportId: 'daily-summary',
})
.onConnection('redis')
.onQueue('emails')
.delay(60)
.onComplete((result) => {
console.log(result.jobId)
})
.onFailed((error) => {
console.error(error)
})Use the string-based dispatch helper when the job name is dynamic or when you prefer dispatching by name:
ts
import { dispatch } from '@holo-js/queue'
await dispatch('reports.send-digest', {
reportId: 'daily-summary',
})
.onConnection('redis')
.onQueue('emails')If dispatch must wait for a successful database commit, compose that explicitly with DB.afterCommit() from @holo-js/db.
Use dispatchSync() when the current code path must execute the job immediately:
ts
import SendDigest from '../jobs/reports/send-digest'
import { dispatchSync } from '@holo-js/queue'
await SendDigest.dispatchSync({
reportId: 'daily-summary',
})
await dispatchSync('reports.send-digest', {
reportId: 'daily-summary',
})Server-only rules
Queue jobs are backend code. Keep them in server-owned files and pass only JSON-serializable payloads:
- IDs
- strings
- numbers
- booleans
- arrays
- plain objects
Do not queue model instances, functions, streams, or browser-only objects.