ViteHub

Queue usage

Practical patterns for typed jobs, enqueue envelopes, deferred dispatch, provider clients, and stable queue names.

After the quickstart works, most Queue code falls into four patterns: define a typed job, enqueue a payload, choose whether dispatch is awaited or deferred, and let the provider deliver the handler later.

Define payload types

Export payload types from the queue definition when producer code needs them.

src/welcome-email.queue.ts
import { defineQueue } from '@vitehub/queue'

export type WelcomeEmailPayload = {
  email: string
  template: 'default' | 'vip'
}

export default defineQueue<WelcomeEmailPayload>(async (job) => {
  console.log(job.id, job.attempts, job.payload.email)
})

The handler receives a QueueJob<TPayload> with id, payload, attempts, and optional provider metadata.

Keep producers provider-neutral

The route should not know whether Cloudflare or Vercel is delivering the job.

const result = await runQueue('welcome-email', payload)

Provider details belong in config:

queue: {
  provider: 'cloudflare',
}

Send a bare payload

Pass the payload directly when you do not need delivery options:

await runQueue('welcome-email', {
  email: 'ava@example.com',
  template: 'vip',
})

runQueue() resolves after the provider accepts the send:

{
  "status": "queued",
  "messageId": "queue_7f1b6f8e-7b5c-4c5e-b3a1-8d6a4b3d4c2a"
}

Send an enqueue envelope

Pass an object with payload when you need delivery options:

await runQueue('welcome-email', {
  id: 'welcome-email-1',
  payload: {
    email: 'ava@example.com',
    template: 'vip',
  },
  delaySeconds: 30,
  idempotencyKey: 'welcome-email-1',
})

The shared envelope supports these fields:

FieldProvider supportDescription
payloadCloudflare, VercelThe job payload delivered to the queue handler.
idCloudflare, VercelMessage id used in the ViteHub enqueue result. Vercel also uses it as the default idempotency key.
delaySecondsCloudflare, VercelDelay delivery when the provider supports it.
contentTypeCloudflarePayload encoding passed to Cloudflare Queues.
idempotencyKeyVercelDeduplicate sends through Vercel Queue.
regionVercelOverride the Vercel region for this send.
retentionSecondsVercelKeep a queued message longer when supported by Vercel Queue.
Unsupported provider options throw QueueError. Cloudflare rejects idempotencyKey and retentionSeconds; Vercel rejects contentType.

Defer dispatch until after the response

Use deferQueue() when the route should return immediately and enqueue dispatch can run through the current runtime context:

import { deferQueue } from '@vitehub/queue'

export default defineEventHandler(() => {
  deferQueue('welcome-email', {
    email: 'ava@example.com',
    template: 'default',
  })

  return { ok: true }
})

deferQueue() uses waitUntil() when the runtime provides it. If enqueue fails, Queue logs the failure and calls onDispatchError from the queue definition when configured.

export default defineQueue<WelcomeEmailPayload>(async (job) => {
  console.log(job.payload.email)
}, {
  onDispatchError(error, context) {
    console.error(`Deferred dispatch failed for ${context.name}`, error)
  },
})

Resolve the active provider client

Use getQueue() when you need the concrete provider client instead of a plain send call:

import { getQueue } from '@vitehub/queue'

const queue = await getQueue('welcome-email')

if (queue.provider === 'cloudflare') {
  console.log(queue.binding)
}

Provider-specific client methods:

ProviderExtra methods
CloudflaresendBatch(), createBatchHandler()
Vercelcallback()

Queue naming rules

Queue names always come from discovered files:

  • src/welcome-email.queue.ts becomes welcome-email
  • src/email/welcome.queue.ts becomes email/welcome

Next steps

Copyright © 2026