Background job queue for Nuxt applications, powered by BullMQ and Redis.
- What You Get
- Quick Start
- Core Workflow
- Monitor and Control Jobs
- Configuration
- Production
- API Reference
- Troubleshooting
- File-based jobs (
server/jobs/**) with auto-discovery - Separate worker process for background execution
- Redis-backed queues via BullMQ
- Real-time progress/status updates
- Job lifecycle hooks (
onCompleted,onFailed) - Queue stats + cancel/retry endpoints
- Full TypeScript support
Run Redis:
docker run -d -p 6379:6379 redisnpx nuxi@latest module add nuxt-queuekitOr manually:
npm install nuxt-queuekit// nuxt.config.ts
export default defineNuxtConfig({
modules: ['nuxt-queuekit'],
})# terminal 1
npm run dev
# terminal 2
npx nuxt-queuekit workerThat is enough to run jobs using default Redis (127.0.0.1:6379).
Create a file in server/jobs:
// server/jobs/SendWelcomeEmail.ts
interface WelcomeEmailData {
userId: string
email: string
name: string
}
export default defineJob<WelcomeEmailData>({
async handle(data) {
await sendEmail({
to: data.email,
subject: 'Welcome!',
template: 'welcome',
data: { name: data.name },
})
return { success: true }
},
})defineJob is auto-imported in job files.
Use dispatch() in server runtime code (API routes, server plugins, server utilities):
// server/api/register.post.ts
export default defineEventHandler(async (event) => {
const { email, name } = await readBody(event)
const user = await createUser({ email, name })
const { jobId } = await dispatch('SendWelcomeEmail', {
userId: user.id,
email: user.email,
name: user.name,
})
return { success: true, jobId }
})Use useQueue() on the client:
<script setup lang="ts">
const queue = useQueue('default')
async function start() {
const { jobId, progress, status, result, error } = await queue.add('SendWelcomeEmail', {
userId: '1',
email: 'user@example.com',
name: 'User',
})
}
</script>Nested files map to dot notation:
server/jobs/
emails/NewsletterEmail.ts
media/ProcessImage.ts
await dispatch('emails.NewsletterEmail', { ... })
await dispatch('media.ProcessImage', { ... })export default defineJob({
queue: 'payments',
options: {
attempts: 3,
backoff: { type: 'exponential', delay: 2000 },
},
async handle(data) {
return processPayment(data)
},
})export default defineJob({
async handle(data, job) {
await job.updateProgress(50)
const output = await generateReport(data)
await job.updateProgress(100)
return output
},
async onCompleted(job, result) {
await notifyUser(job.data.userId, result)
},
async onFailed(job, error) {
await alertAdmin(job?.data?.userId, error.message)
},
})const status = await $fetch(`/api/queue/${queueName}/${jobId}`)
// status.state -> waiting | active | completed | failed | delayedconst es = new EventSource(`/api/queue/${queueName}/${jobId}/events`)await $fetch(`/api/queue/${queueName}/${jobId}/cancel`, { method: 'POST' })await $fetch(`/api/queue/${queueName}/${jobId}/retry`, { method: 'POST' })const { stats, loading, error, refresh } = useQueueStats('default')
await refresh()NUXT_REDIS_HOST=127.0.0.1
NUXT_REDIS_PORT=6379
NUXT_REDIS_PASSWORD=
NUXT_REDIS_USERNAME=
NUXT_REDIS_DB=0// nuxt.config.ts
export default defineNuxtConfig({
modules: ['nuxt-queuekit'],
queue: {
redis: {
host: process.env.REDIS_HOST || '127.0.0.1',
port: Number(process.env.REDIS_PORT) || 6379,
password: process.env.REDIS_PASSWORD,
username: process.env.REDIS_USERNAME,
db: Number(process.env.REDIS_DB) || 0,
},
// default: server/jobs
jobsDir: 'server/jobs',
// optional extra jobs
jobs: {
SendNotification: './server/custom-jobs/SendNotification.ts',
},
},
})jobs entries are appended to auto-discovered jobs from jobsDir.
Run web server and workers as separate processes:
npm run build
npm run start
npx nuxt-queuekit worker --concurrency 10npx nuxt-queuekit worker [options]
Options:
--cwd <path> Working directory (default: current directory)
--queue <name> Single queue name (legacy flag)
--queues <names> Comma-separated queues in priority order
--worker <path> Path to custom worker script
--concurrency <number> Concurrent jobs per worker (default: 5)Examples:
npx nuxt-queuekit worker
npx nuxt-queuekit worker --queue emails --concurrency 10
npx nuxt-queuekit worker --queues critical,default,low --concurrency 5
npx nuxt-queuekit worker --queue processing --worker ./workers/custom.tsexport default defineJob<{ userId: string }, { ok: true }>({
queue: 'default',
options: { attempts: 3 },
async handle(data, job) {
await job.updateProgress(100)
return { ok: true }
},
async onCompleted(job, result) {},
async onFailed(job, error) {},
})Server runtime helper that creates a job and returns reactive refs:
const { jobId, queueName, progress, status, result, error, refresh, cancel, retry } =
await dispatch('SendEmail', { to: 'user@example.com' })const queue = useQueue('default')
const job = await queue.add('SendEmail', { to: 'user@example.com' })Returns BullMQ Queue directly (advanced/non-reactive):
const queue = useServerQueue('emails')
await queue.add('send-email', { to: 'user@example.com' })const { stats, loading, error, refresh } = useQueueStats()
await refresh()POST /api/queue/addGET /api/queue/:queueName/:jobIdGET /api/queue/:queueName/:jobId/eventsPOST /api/queue/:queueName/:jobId/cancelPOST /api/queue/:queueName/:jobId/retryGET /api/queue/:queueName/stats
- Ensure file is under configured
jobsDir - Ensure default export is
defineJob({ ... }) - Restart dev server and worker after adding new files
- Confirm Redis is reachable from worker process
- Confirm queue names match (
job.queuevs worker--queue/--queues)
- Ensure worker is running (worker publishes events)
- Check network/proxy supports SSE