A Nest.js wrapper for Graphile Worker.
What is Graphile worker ?
Job queue for PostgreSQL running on Node.js - allows you to run jobs (e.g. sending emails, performing calculations, generating PDFs, etc) "in the background" so that your HTTP response/application code is not held up. Can be used with any PostgreSQL-backed application. Pairs beautifully with PostGraphile or PostgREST.
Why you should prefer Graphile Worker instead of Bull ?
- You already have a PostgreSQL in your stack (and you don't want to add a Redis server)
- provide a module
GraphileWorkerModule
to setup the runner usingasRoot
orasRootAsync
- provide a
WorkerService
to add jobs or start runner - provide a
@OnWorkerEvent
decorator to add custom behavior onjob:success
for example - provide a
@Task(name)
decorator to define your injectable tasks
npm install nestjs-graphile-worker
yarn add nestjs-graphile-worker
pnpm add nestjs-graphile-worker
In order, to setup the library, you need to import and initialize GraphileWorkerModule
.
You can do it using forRoot
method:
// src/app.module.ts
import { GraphileWorkerModule } from "nest-graphile-worker";
import { Module } from "@nestjs/common";
import { AppController } from "./app.controller";
@Module({
imports: [
GraphileWorkerModule.forRoot({
connectionString: "postgres://example:password@postgres/example",
}),
],
controllers: [AppController],
providers: [],
})
export class AppModule {}
Or using forRootAsync
:
// src/app.module.ts
import { GraphileWorkerModule } from "nestjs-graphile-worker";
import { Module } from "@nestjs/common";
import { ConfigModule, ConfigService } from "@nestjs/config";
import { AppController } from "./app.controller";
import { helloTask } from "./hello.task";
@Module({
imports: [
ConfigModule.forRoot(),
GraphileWorkerModule.forRootAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: (config: ConfigService) => ({
connectionString: config.get("PG_CONNECTION"),
taskList: {
hello: helloTask,
},
}),
}),
],
controllers: [AppController],
providers: [],
})
export class AppModule {}
The module configuration is GraphileWorkerConfiguration
, which is a wrapper around Graphile's RunnerOptions
type GraphileWorkerConfiguration = Omit<RunnerOptions, "events" | "taskList">;
This means you can pass any configuration to the runner, like Recurring tasks (crontab):
// src/app.module.ts
@Module({
imports: [
GraphileWorkerModule.forRoot({
connectionString: "postgres://example:password@postgres/example",
crontab: [' * * * * * taskIdentifier ?priority=1 {"foo": "bar"}'].join(
"\n",
),
}),
],
controllers: [AppController],
providers: [],
})
export class AppModule {}
To create task you need to define an @Injectable
class with @Task(name)
decorator containing a decorated method with @TaskHandler
:
// src/hello.task.ts
import { Injectable, Logger } from "@nestjs/common";
import type { Helpers } from "graphile-worker";
import { Task, TaskHandler } from "../../src/index";
@Injectable()
@Task("hello")
export class HelloTask {
private logger = new Logger(HelloTask.name);
@TaskHandler()
handler(payload: any, _helpers: Helpers) {
this.logger.log(`handle ${JSON.stringify(payload)}`);
}
}
Then do not forget to register this class as provider in your module:
// src/app.module.ts
import { Module } from "@nestjs/common";
import { HelloTask } from "./hello.task";
// ...
@Module({
imports: [
/* ... */
],
controllers: [
/* ... */
],
providers: [HelloTask],
})
export class AppModule {}
You can use WorkerService
which is a wrapper of graphile-worker
's Runner
instance. WorkerService
let you add job easily.
import { WorkerService } from "nestjs-graphile-worker";
import { Controller, HttpCode, Post } from "@nestjs/common";
@Controller()
export class AppController {
constructor(private readonly graphileWorker: WorkerService) {}
@Post()
@HttpCode(201)
async addJob() {
await this.graphileWorker.addJob("hello", { hello: "world" });
}
@Post("bulk")
@HttpCode(201)
async addJobs() {
const jobs = new Array(100)
.fill(undefined)
.map((_, i) => ({ identifier: "hello", payload: { hello: i } }));
return this.graphileWorker.addJobs(jobs);
}
}
Add WorkerService.run
in main.ts
file:
import { WorkerService } from "nestjs-graphile-worker";
import { NestFactory } from "@nestjs/core";
import { AppModule } from "./app.module";
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.get(WorkerService).run();
await app.listen(3000);
}
bootstrap();
You can use @OnWorkerEvent
decorator to listen any Graphile Worker event. You simply have to:
@GraphileWorkerListener
decorator on your class- set
@OnWorkerEvent(eventName)
on your method
import { Injectable, Logger } from "@nestjs/common";
import { WorkerEventMap } from "graphile-worker";
import { GraphileWorkerListener, OnWorkerEvent } from "../../src/index";
@Injectable()
@GraphileWorkerListener()
export class AppService {
private readonly logger = new Logger(AppService.name);
@OnWorkerEvent("job:success")
onJobSuccess({ job }: WorkerEventMap["job:success"]) {
this.logger.debug(`job #${job.id} finished`);
}
@OnWorkerEvent("job:error")
onJobError({ job, error }: WorkerEventMap["job:error"]) {
this.logger.error(`job #${job.id} fail ${JSON.stringify(error)}`);
}
}
You can find a sample using this library. To run it, simply npm install
and then:
docker-compose up