Simple Nuxt module for background tasks using bullmq
npm install nuxt-simple-bullmq
[![npm version][npm-version-src]][npm-version-href]
[![npm downloads][npm-downloads-src]][npm-downloads-href]
[![License][license-src]][license-href]
[![Nuxt][nuxt-src]][nuxt-href]
Simple Nuxt 3 module using BullMQ and Redis for doing amazing things.
- ⛰ Foo
- 🚠 Bar
- 🌲 Baz
Install the module in your Nuxt application with one command:
``bash`
npx nuxi module add nuxt-simple-bullmq
> NOTE: This is only tested with NodeJS 21 (not cloudflare/vercel etc) and Nuxt 4 with experimental features (see test
workflows/files for more)
`javascript`
// nuxt.config.ts
{
runtimeConfig: {
redis: {
url: 'redis://localhost:6379'
}
},
bullMq: {
// Where to load defined workers from (defineWorker files)
// this is optional, and will default to [ '
workerDirs: [ '/workers' ], // base path will be your "serverDir" e.g ./server/some-path
}
}
or use NUXT_REDIS_URL in your $environment
That's it! You can now use BullMQ in your Nuxt app ✨
`typescript Sending welcome email to ${job.data.email}
// ./server/workers/default,ts
export default defineWorker('default', {
async sendWelcomeEmail({job, logger, lockId}) {
logger.info()
},
// magic catch-all event handler (for uncaught events):
catchAll({job, logger}) {
logger.debug(Uncaught event: ${job.name}!, job.data)`
}
}, {
//optional: default //comment
concurrency: 1, //how many of each worker to run
});
Jobs are handled through callbacks, they can be in their own files, defined directly on the worker etc.
> Note: There is no typing for dispatching jobs - yet :/
One solution can be to use a constant mapping e.g const JobNames = {someKey: 'someValue'}
`typescript`
// ./server/jobs/sendWelcomeEmail.ts
export default defineJobHandler(({job, logger}) => {
logger.debug(job.name, job.data)
})
Validated job handlers
`typescript
// ./server/jobs/sendWelcomeEmail.ts
import {z} from 'zod';
export default defineValidatedJobHandler(
z.object({userId: z.string()}),
async ({data, job, logger}) => {
// data contains the validated payload from the schema
// data is also typed: {userId:string}
},
);
`
> Note: Validates input before processing the job
Delaying jobs
`typescript
// ./server/jobs/onboarding/sendTipsAndTricks.ts
import {z} from 'zod'
import {DelayedError} from 'bullmq';
export default defineValidatedJobHandler(
z.object({userId: z.string().uuid()}),
async ({data: {userId}, job, logger, lockId}) => {
const DELAY_MS = 1_800_000 // 30 minutes
// do some checks...
if (!await userHasVerifierEmail() && !hasBeenMoreThan24HoursSinceSignUp()) {
logger.info('Preconditions not met, delaying job...')
await job.moveToDelayed(Date.now() + DELAY_MS, lockId)
throw new DelayedError();
}
logger.info(Sending Tips & Tricks to user ${userId})`
},
)
`typescript
// ./server/route/dispatch.ts
import {dispatchJob} from '#imports'
export default defineEventHandler(async event => {
await dispatchJob(
'sendWelcomeEmail',
{userId: 'abc'},
// Optional:
{queueName: 'default', attempts: 1, backoff: {strategy: 'exponential', } },
)
})
`
Validated job dispatch
`typescript
// ./server/route/typed-dispatch.ts
import {dispatchValidatedJob} from '#imports'
export default defineEventHandler(async event => {
await dispatchValidatedJob(
'sendWelcomeEmail',
z.object({userId: z.string()}),
{userId: 'abc'},
{queueName: 'default'},
)
})
`
> This will validate the input before emitting the job to redis
Additional options
> You can pass the same options that are passed as the third argument when calling emit to dispatchJob and dispatchValidatedJob as well. `typescript
// ./server/route/name.ts
import {useQueue} from '#imports'
//e.g using H3 event handlers
export default defineEventHandler(async event => {
const queue = useQueue('default');
await queue.emit('sendWelcomeEmail', {userId: 'some-string'}, {
//Optionals (with their defaults)
queueName: 'default',
//optionals without defaults
deduplicationId: 'some-string', // can be anything, defaults to the event name.
ttl: 500, // when the deduplication should expire
delay: 500, // a delay to when this will run (good for notifications)
})
//validate schema first:
await queue.emit('sendWelcomeEmail', {userId: 'some-string'}, {
schema: z.object({userId: z.string()}), // optional
//... other options
})
})
`
- [X] Add handlers
- [X] Worker per plugin
- [X] Validation dispatch/handler
- [ ] File based listeners (Laravel style with a "dispatch" method)
- [ ] Flow producers
- [ ] Different lib/platform (e.g Vercel/Cloudflare)
Local development
`bash``
# Install dependencies
npm install
# Generate type stubs
npm run dev:prepare
# run redis via docker (add -s to detach and continue using the terminal for other stuff)
docker compose -f ./playground/compose.yml up [-d]
# to stop docker stuff:
docker compose -f ./playground/compose.yml down
# Develop with the playground
npm run dev
# Build the playground
npm run dev:build
# Run ESLint
npm run lint
# Run Vitest
npm run test
npm run test:watch
# Release new version
npm run release
[npm-version-src]: https://img.shields.io/npm/v/nuxt-simple-bullmq/latest.svg?style=flat&colorA=020420&colorB=00DC82
[npm-version-href]: https://npmjs.com/package/nuxt-simple-bullmq
[npm-downloads-src]: https://img.shields.io/npm/dm/nuxt-simple-bullmq.svg?style=flat&colorA=020420&colorB=00DC82
[npm-downloads-href]: https://npm.chart.dev/nuxt-simple-bullmq
[license-src]: https://img.shields.io/npm/l/nuxt-simple-bullmq.svg?style=flat&colorA=020420&colorB=00DC82
[license-href]: https://npmjs.com/package/nuxt-simple-bullmq
[nuxt-src]: https://img.shields.io/badge/Nuxt-020420?logo=nuxt.js
[nuxt-href]: https://nuxt.com