A Bull provider for the Adonis framework.
Adonis Bull provides an easy way to start using Bull. The fastest, most reliable, Redis-based queue for Node.
adonis install @rocketseat/adonis-bull
Register the Bull provider at start/app.js
const providers = [
//...
"@rocketseat/adonis-bull/providers/Bull"
];
Create a file with the jobs
that will be processed at start/jobs.js
:
module.exports = ["App/Jobs/UserRegisterEmail"];
Add the config file at config/bull.js
:
"use strict";
const Env = use("Env");
module.exports = {
// redis connection
connection: Env.get("BULL_CONNECTION", "bull"),
bull: {
redis: {
host: "127.0.0.1",
port: 6379,
password: null,
db: 0,
keyPrefix: ""
}
},
remote: "redis://redis.example.com?password=correcthorsebatterystaple"
};
In the above file you can define redis connections, there you can pass all Bull
queue configurations described here.
Create a file to initiate Bull
at preloads/bull.js
:
const Bull = use("Rocketseat/Bull");
Bull.process()
// Optionally you can start BullBoard:
.ui(9999); // http://localhost:9999
// You don't need to specify the port, the default number is 9999
Add .preLoad in server.js to initialize the bull preload
new Ignitor(require('@adonisjs/fold'))
.appRoot(__dirname)
.preLoad('preloads/bull') // Add This Line
.fireHttpServer()
.catch(console.error)
Create a class that mandatorily has the methods key
and handle
.
The key
method is the unique identification of each job. It has to be a static get
method.
The handle
is the method that contains the functionality of your job
.
const Mail = use("Mail");
class UserRegisterEmail {
static get key() {
return "UserRegisterEmail-key";
}
async handle(job) {
const { data } = job; // the 'data' variable has user data
await Mail.send("emails.welcome", data, message => {
message
.to(data.email)
.from("<from-email>")
.subject("Welcome to yardstick");
});
return data;
}
}
module.exports = UserRegisterEmail;
You can use the connection
static get method to specify which connection your job
will work.
class UserRegisterEmail {
// ...
static get connection() {
return "remote";
}
}
You can config the events related to the job
to have more control over it
const Ws = use('Ws')
class UserRegisterEmail {
...
onCompleted(job, result) {
Ws
.getChannel('admin:notifications')
.topic('admin:notifications')
.broadcast('new:user', result)
}
}
module.exports = UserRegisterEmail;
You can share the job
of any controller
, hook
or any other place you might like:
const User = use('App/Models/User')
const Bull = use('Rocketseat/Bull')
const Job = use('App/Jobs/UserRegisterEmail')
class UserController {
store ({ request, response }) {
const data = request.only(['email', 'name', 'password'])
const user = await User.create(data)
Bull.add(Job.key, user)
}
}
module.exports = UserController
Sometimes it is necessary to schedule a job instead of shooting it imediately. You should use schedule
for that:
const User = use('App/Models/User')
const Bull = use('Rocketseat/Bull')
const Job = use('App/Jobs/HolidayOnSaleEmail')
class HolidayOnSaleController {
store ({ request, response }) {
const data = request.only(['date', 'product_list']) // 2019-11-15 12:00:00
const products = await ProductOnSale.create(data)
Bull.schedule(Job.key, products, data.date)
}
}
module.exports = HolidayOnSaleController
This job
will be sent only on the specific date, wich for example here is on November 15th at noon.
When finishing a date, never use past dates because it will cause an error.
other ways of using schedule
:
Bull.schedule(key, data, new Date("2019-11-15 12:00:00"));
Bull.schedule(key, data, "2 hours"); // 2 hours from now
Bull.schedule(key, data, 60 * 1000); // 1 minute from now.
You can use the own Bull
configs to improve your job:
Bull.add(key, data, {
repeat: {
cron: "0 30 12 * * WED,FRI"
}
});
This job
will be run at 12:30 PM, only on wednesdays and fridays.
To have a bigger control over errors that might occur on the line, the events that fail can be manipulated at the file App/Exceptions/QueueHandler.js
:
const Sentry = use("Sentry");
class QueueHandler {
async report(error, job) {
Sentry.configureScope(scope => {
scope.setExtra(job);
});
Sentry.captureException(error);
}
}
module.exports = QueueHandler;