├── .eslintignore
├── .eslintrc.js
├── .gitignore
├── .prettierrc
├── LICENSE
├── README.md
├── index.d.ts
├── index.js
├── index.ts
├── package-lock.json
├── package.json
├── src
├── bullmq.constants.ts
├── bullmq.core-module.ts
├── bullmq.decorators.ts
├── bullmq.interfaces.ts
├── bullmq.module.ts
├── bullmq.utils.ts
└── index.ts
├── test
├── index.spec.ts
├── jest.config.json
├── module.spec.ts
└── module.test.ts
├── tsconfig.build.json
└── tsconfig.json
/.eslintignore:
--------------------------------------------------------------------------------
1 | index.js
2 | index.ts
3 | index.d.ts
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | parser: '@typescript-eslint/parser',
3 | parserOptions: {
4 | project: 'tsconfig.json',
5 | sourceType: 'module',
6 | },
7 | plugins: ['@typescript-eslint/eslint-plugin'],
8 | extends: [
9 | 'plugin:@typescript-eslint/eslint-recommended',
10 | 'plugin:@typescript-eslint/recommended',
11 | 'prettier',
12 | 'prettier/@typescript-eslint',
13 | ],
14 | root: true,
15 | env: {
16 | node: true,
17 | jest: true,
18 | },
19 | rules: {
20 | '@typescript-eslint/interface-name-prefix': 'off',
21 | '@typescript-eslint/explicit-function-return-type': 'off',
22 | '@typescript-eslint/no-explicit-any': 'off',
23 | },
24 | };
25 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # compiled output
2 | /dist
3 | /node_modules
4 |
5 | # Logs
6 | logs
7 | *.log
8 | npm-debug.log*
9 | yarn-debug.log*
10 | yarn-error.log*
11 | lerna-debug.log*
12 |
13 | # OS
14 | .DS_Store
15 |
16 | # Tests
17 | /coverage
18 | /.nyc_output
19 |
20 | # IDEs and editors
21 | /.idea
22 | .project
23 | .classpath
24 | .c9/
25 | *.launch
26 | .settings/
27 | *.sublime-workspace
28 |
29 | # IDE - VSCode
30 | .vscode/*
31 | !.vscode/settings.json
32 | !.vscode/tasks.json
33 | !.vscode/launch.json
34 | !.vscode/extensions.json
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": true,
3 | "trailingComma": "all"
4 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Sviatoslav
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # NestJS BullMQ
2 |
3 |
4 |
5 |
6 | ## Table of Contents
7 |
8 | - [Description](#description)
9 | - [Installation](#installation)
10 | - [Examples](#examples)
11 | - [License](#license)
12 |
13 | ## Description
14 | Integrates BullMQ with Nest
15 |
16 | ## Installation
17 |
18 | ```bash
19 | npm install nestjs-bullmq bullmq
20 | ```
21 |
22 | You can also use the interactive CLI
23 |
24 | ```sh
25 | npx nestjs-modules
26 | ```
27 |
28 | ## Examples
29 |
30 | ### BullMQModule.forRoot(options, connection?)
31 |
32 | ```ts
33 | import { Module } from '@nestjs/common';
34 | import { BullMQModule } from 'nestjs-bullmq';
35 | import { AppController } from './app.controller';
36 |
37 | @Module({
38 | imports: [
39 | BullMQModule.forRoot({
40 | name: 'QueueName',
41 | config: {
42 | // url: 'redis://:password@localhost:6379',
43 | connection: { host: 'localhost', port: 6379 },
44 | },
45 | }),
46 | ],
47 | controllers: [AppController],
48 | })
49 | export class AppModule {}
50 | ```
51 |
52 | ### BullMQModule.forRootAsync(options, connection?)
53 |
54 | ```ts
55 | import { Module } from '@nestjs/common';
56 | import { BullMQModule } from 'nestjs-bullmq';
57 | import { AppController } from './app.controller';
58 |
59 | @Module({
60 | imports: [
61 | BullMQModule.forRootAsync({
62 | name: 'QueueName',
63 | useFactory: () => ({
64 | config: {
65 | // url: 'redis://:password@localhost:6379',
66 | connection: { host: 'localhost', port: 6379 },
67 | },
68 | }),
69 | }),
70 | ],
71 | controllers: [AppController],
72 | })
73 | export class AppModule {}
74 | ```
75 |
76 | ### InjectBullMQ(queueName?, connection?)
77 |
78 | ```ts
79 | import { Controller, Get, } from '@nestjs/common';
80 | import { InjectBullMQ, BullMQ } from 'nestjs-bullmq';
81 |
82 | @Controller()
83 | export class AppController {
84 | constructor(
85 | @InjectBullMQ('QueueName') private readonly bullMQ: BullMQ,
86 | ) {}
87 |
88 | @Get()
89 | async getHello() {
90 | this.bullMQ.process(async job => {
91 | console.log('process', job);
92 | })
93 | this.bullMQ.queue.add('myJobName', { foo: 'bar' });
94 | // this.bullMQ.queueEvents.on();
95 | // this.bullMQ.queueScheduler.on();
96 | }
97 | }
98 | ```
99 |
100 | ## License
101 |
102 | MIT
103 |
--------------------------------------------------------------------------------
/index.d.ts:
--------------------------------------------------------------------------------
1 | export * from './dist';
2 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | function __export(m) {
3 | for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
4 | }
5 | exports.__esModule = true;
6 | __export(require("./dist"));
7 |
--------------------------------------------------------------------------------
/index.ts:
--------------------------------------------------------------------------------
1 | export * from './dist';
2 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "nestjs-bullmq",
3 | "version": "0.1.1",
4 | "description": "Nest - modern, fast, powerful node.js web framework (@bullmq)",
5 | "author": "Sviatoslav H",
6 | "license": "MIT",
7 | "repository": {
8 | "type": "git",
9 | "url": "https://github.com/svtslv/nestjs-bullmq"
10 | },
11 | "keywords": [
12 | "nest",
13 | "nestjs",
14 | "bull",
15 | "bullmq",
16 | "redis",
17 | "queue"
18 | ],
19 | "files": [
20 | "dist",
21 | "index.js",
22 | "index.d.ts"
23 | ],
24 | "main": "dist/index.js",
25 | "scripts": {
26 | "fix": "rm -rf node_modules && rm package-lock.json && npm install",
27 | "build": "rm -rf dist && tsc -p tsconfig.build.json",
28 | "format": "prettier --write \"**/*.ts\"",
29 | "lint": "eslint 'lib/**/*.ts' --fix",
30 | "test": "jest --config ./test/jest.config.json --runInBand",
31 | "prepublish:npm": "npm run build",
32 | "publish:npm": "npm publish --access public",
33 | "prepublish:test": "npm run build",
34 | "publish:test": "npm publish --access public --tag test",
35 | "prerelease": "npm run build",
36 | "release": "release-it"
37 | },
38 | "peerDependencies": {
39 | "@nestjs/common": ">=6.7.0",
40 | "@nestjs/core": ">=6.7.0",
41 | "bullmq": ">=1.8.0"
42 | },
43 | "devDependencies": {
44 | "@nestjs/common": "^9.2.0",
45 | "@nestjs/core": "^9.0.5",
46 | "@nestjs/platform-express": "^9.2.1",
47 | "@nestjs/testing": "^7.0.3",
48 | "@types/express": "^4.17.6",
49 | "@types/jest": "^26.0.5",
50 | "@types/node": "^13.9.2",
51 | "@types/supertest": "^2.0.8",
52 | "@typescript-eslint/eslint-plugin": "^2.24.0",
53 | "@typescript-eslint/parser": "^2.24.0",
54 | "bullmq": "^1.8.7",
55 | "eslint": "^6.8.0",
56 | "eslint-config-prettier": "^6.10.0",
57 | "eslint-plugin-import": "^2.20.1",
58 | "jest": "^26.1.0",
59 | "prettier": "^1.19.1",
60 | "reflect-metadata": "^0.1.13",
61 | "supertest": "^4.0.2",
62 | "ts-jest": "^26.1.3",
63 | "ts-loader": "^9.4.2",
64 | "ts-node": "^8.7.0",
65 | "tsconfig-paths": "^3.9.0",
66 | "typescript": "^3.8.3"
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/src/bullmq.constants.ts:
--------------------------------------------------------------------------------
1 | export const BULLMQ_MODULE_CONNECTION = 'default';
2 | export const BULLMQ_MODULE_CONNECTION_TOKEN = 'BullMQModuleConnectionToken';
3 | export const BULLMQ_MODULE_OPTIONS_TOKEN = 'BullMQModuleOptionsToken';
4 |
--------------------------------------------------------------------------------
/src/bullmq.core-module.ts:
--------------------------------------------------------------------------------
1 | import { Global, Module, DynamicModule, Provider } from '@nestjs/common';
2 | import { BullMQModuleAsyncOptions, BullMQModuleOptions, BullMQModuleOptionsFactory } from './bullmq.interfaces';
3 | import { createBullMQConnection, getBullMQOptionsToken, getBullMQConnectionToken } from './bullmq.utils'
4 |
5 | @Global()
6 | @Module({})
7 | export class BullMQCoreModule {
8 |
9 | /* forRoot */
10 | static forRoot(options: BullMQModuleOptions, connection?: string): DynamicModule {
11 |
12 | const bullMQOptionsProvider: Provider = {
13 | provide: getBullMQOptionsToken(options, connection),
14 | useValue: options,
15 | };
16 |
17 | const bullMQConnectionProvider: Provider = {
18 | provide: getBullMQConnectionToken(options, connection),
19 | useValue: createBullMQConnection(options, connection),
20 | };
21 |
22 | return {
23 | module: BullMQCoreModule,
24 | providers: [
25 | bullMQOptionsProvider,
26 | bullMQConnectionProvider,
27 | ],
28 | exports: [
29 | bullMQOptionsProvider,
30 | bullMQConnectionProvider,
31 | ],
32 | };
33 | }
34 |
35 | /* forRootAsync */
36 | public static forRootAsync(options: BullMQModuleAsyncOptions, connection: string): DynamicModule {
37 |
38 | const bullMQConnectionProvider: Provider = {
39 | provide: getBullMQConnectionToken(options, connection),
40 | useFactory(syncOptions: BullMQModuleOptions) {
41 | syncOptions.name = options.name;
42 | return createBullMQConnection(syncOptions, connection)
43 | },
44 | inject: [getBullMQOptionsToken(options, connection)],
45 | };
46 |
47 | return {
48 | module: BullMQCoreModule,
49 | imports: options.imports,
50 | providers: [...this.createAsyncProviders(options, connection), bullMQConnectionProvider],
51 | exports: [bullMQConnectionProvider],
52 | };
53 | }
54 |
55 | /* createAsyncProviders */
56 | public static createAsyncProviders(options: BullMQModuleAsyncOptions, connection?: string): Provider[] {
57 |
58 | if(!(options.useExisting || options.useFactory || options.useClass)) {
59 | throw new Error('Invalid configuration. Must provide useFactory, useClass or useExisting');
60 | }
61 |
62 | if (options.useExisting || options.useFactory) {
63 | return [
64 | this.createAsyncOptionsProvider(options, connection)
65 | ];
66 | }
67 |
68 | return [
69 | this.createAsyncOptionsProvider(options, connection),
70 | { provide: options.useClass, useClass: options.useClass },
71 | ];
72 | }
73 |
74 | /* createAsyncOptionsProvider */
75 | public static createAsyncOptionsProvider(options: BullMQModuleAsyncOptions, connection?: string): Provider {
76 |
77 | if(!(options.useExisting || options.useFactory || options.useClass)) {
78 | throw new Error('Invalid configuration. Must provide useFactory, useClass or useExisting');
79 | }
80 |
81 | if (options.useFactory) {
82 | return {
83 | provide: getBullMQOptionsToken(options, connection),
84 | useFactory: options.useFactory,
85 | inject: options.inject || [],
86 | };
87 | }
88 |
89 | return {
90 | provide: getBullMQOptionsToken(options, connection),
91 | async useFactory(optionsFactory: BullMQModuleOptionsFactory): Promise {
92 | return await optionsFactory.createBullMQModuleOptions();
93 | },
94 | inject: [options.useClass || options.useExisting],
95 | };
96 | }
97 | }
--------------------------------------------------------------------------------
/src/bullmq.decorators.ts:
--------------------------------------------------------------------------------
1 | import { Inject } from '@nestjs/common';
2 | import { getBullMQConnectionToken } from './bullmq.utils';
3 |
4 | export const InjectBullMQ = (queueName?: string, connection?: string) => {
5 | return Inject(getBullMQConnectionToken({ name: queueName }, connection));
6 | };
7 |
--------------------------------------------------------------------------------
/src/bullmq.interfaces.ts:
--------------------------------------------------------------------------------
1 | import { ModuleMetadata, Type } from "@nestjs/common/interfaces";
2 | import type { QueueBaseOptions, Queue, Worker, QueueEvents, QueueScheduler, Job } from 'bullmq';
3 |
4 | export type BullMQ = {
5 | queue: Queue,
6 | process: (callback: (job: Job) => any) => Worker,
7 | queueEvents: QueueEvents,
8 | queueScheduler: QueueScheduler
9 | }
10 |
11 | export interface BullMQModuleOptions {
12 | name?: string,
13 | config: QueueBaseOptions & { url?: string };
14 | }
15 |
16 | export interface BullMQModuleOptionsFactory {
17 | createBullMQModuleOptions(): Promise | BullMQModuleOptions;
18 | }
19 |
20 | export interface BullMQModuleAsyncOptions extends Pick {
21 | name?: string,
22 | inject?: any[];
23 | useClass?: Type;
24 | useExisting?: Type;
25 | useFactory?: (...args: any[]) => Promise | BullMQModuleOptions;
26 | }
27 |
--------------------------------------------------------------------------------
/src/bullmq.module.ts:
--------------------------------------------------------------------------------
1 | import { DynamicModule, Module } from '@nestjs/common';
2 | import { BullMQCoreModule } from './bullmq.core-module';
3 | import { BullMQModuleAsyncOptions, BullMQModuleOptions } from './bullmq.interfaces';
4 |
5 | @Module({})
6 | export class BullMQModule {
7 | public static forRoot(options: BullMQModuleOptions, connection?: string): DynamicModule {
8 | return {
9 | module: BullMQModule,
10 | imports: [BullMQCoreModule.forRoot(options, connection)],
11 | exports: [BullMQCoreModule],
12 | };
13 | }
14 |
15 | public static forRootAsync(options: BullMQModuleAsyncOptions, connection?: string): DynamicModule {
16 | return {
17 | module: BullMQModule,
18 | imports: [BullMQCoreModule.forRootAsync(options, connection)],
19 | exports: [BullMQCoreModule],
20 | };
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/bullmq.utils.ts:
--------------------------------------------------------------------------------
1 | import { BullMQModuleOptions, BullMQ } from "./bullmq.interfaces";
2 | import { Queue, Worker, QueueEvents, QueueScheduler, Job } from 'bullmq';
3 | import {
4 | BULLMQ_MODULE_CONNECTION,
5 | BULLMQ_MODULE_CONNECTION_TOKEN,
6 | BULLMQ_MODULE_OPTIONS_TOKEN
7 | } from './bullmq.constants';
8 |
9 | export { Queue, Worker, QueueEvents, QueueScheduler }
10 | export * as bullmq from 'bullmq';
11 |
12 | export function getBullMQOptionsToken(options: Partial, connection: string): string {
13 | const queueName = options?.name || connection || BULLMQ_MODULE_CONNECTION;
14 | return `${ queueName }_${ connection || queueName }_${ BULLMQ_MODULE_OPTIONS_TOKEN }`;
15 | }
16 |
17 | export function getBullMQConnectionToken(options: Partial, connection: string): string {
18 | const queueName = options?.name || connection || BULLMQ_MODULE_CONNECTION;
19 | return `${ queueName }_${ connection || queueName }_${ BULLMQ_MODULE_CONNECTION_TOKEN }`;
20 | }
21 |
22 | export function createBullMQConnection(options: BullMQModuleOptions, connection: string): BullMQ {
23 | const { config, name } = options;
24 | const queueName = name || connection || BULLMQ_MODULE_CONNECTION;
25 |
26 | if(config?.url) {
27 | const url = new URL(config.url);
28 | config.connection = {
29 | host: url.hostname,
30 | port: +url.port,
31 | password: url.password,
32 | }
33 | }
34 |
35 | return {
36 | process: (callback: (job: Job) => any): Worker => {
37 | return new Worker(queueName, callback, config);
38 | },
39 | queue: new Queue(queueName, config),
40 | queueEvents: new QueueEvents(queueName, config),
41 | queueScheduler: new QueueScheduler(queueName, config),
42 | };
43 | }
44 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './bullmq.module';
2 | export * from './bullmq.decorators';
3 | export * from './bullmq.interfaces';
4 | export * from './bullmq.utils';
--------------------------------------------------------------------------------
/test/index.spec.ts:
--------------------------------------------------------------------------------
1 | import * as index from '../src/index';
2 |
3 | describe('Index', () => {
4 | test('should return 10 exports', () => {
5 | expect(Object.keys(index)).toHaveLength(10);
6 | });
7 | });
--------------------------------------------------------------------------------
/test/jest.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "moduleFileExtensions": ["js", "json", "ts"],
3 | "rootDir": ".",
4 | "testEnvironment": "node",
5 | "testRegex": ".spec.ts$",
6 | "transform": {
7 | "^.+\\.(t|j)s$": "ts-jest"
8 | }
9 | }
--------------------------------------------------------------------------------
/test/module.spec.ts:
--------------------------------------------------------------------------------
1 | describe('Module', () => {
2 | it(`Instance BullMQ`, async () => {
3 | expect(1).toBe(1);
4 | });
5 | });
6 |
7 | // import { Injectable, INestApplication } from '@nestjs/common';
8 | // import * as Redis from 'ioredis';
9 | // import { Test, TestingModule } from '@nestjs/testing';
10 | // import { BullMQModule, InjectBullMQ, BullMQ, } from '../src';
11 | // import { Worker, Processor } from 'bullmq';
12 |
13 | // describe('Module', () => {
14 | // it(`Instance BullMQ`, async () => {
15 | // let app: INestApplication = null;
16 | // let moduleRef: TestingModule = null;
17 |
18 | // @Injectable()
19 | // class BullMQService {
20 | // constructor(@InjectBullMQ() private readonly bullMq: BullMQ) {}
21 |
22 | // async getTest() {
23 | // this.bullMq.process(async job => {
24 | // console.log('process', job);
25 | // })
26 | // await this.bullMq.queue.add('test', { hello: 'world' });
27 | // return null;
28 | // }
29 | // }
30 |
31 | // moduleRef = await Test.createTestingModule({
32 | // imports: [
33 | // BullMQModule.forRootAsync({
34 | // useFactory: () => ({
35 | // name: 'QueueName',
36 | // config: { connection: { host: 'localhost', port: 6378 } },
37 | // }),
38 | // }),
39 | // ],
40 | // providers: [BullMQService],
41 | // }).compile();
42 |
43 | // app = moduleRef.createNestApplication();
44 |
45 | // await app.init();
46 |
47 | // const bullMQModule = moduleRef.get(BullMQModule);
48 | // const bullMQService = moduleRef.get(BullMQService);
49 |
50 | // await bullMQService.getTest();
51 | // expect(bullMQModule).toBeInstanceOf(BullMQModule);
52 |
53 | // await new Promise((resolve) => {
54 | // setTimeout(() => {
55 | // resolve();
56 | // }, 4000);
57 | // })
58 | // await app.close();
59 | // });
60 | // });
--------------------------------------------------------------------------------
/test/module.test.ts:
--------------------------------------------------------------------------------
1 | import { Injectable, INestApplication } from '@nestjs/common';
2 | import { Test, TestingModule } from '@nestjs/testing';
3 | import { BullMQModule, InjectBullMQ, BullMQ, } from '../src';
4 |
5 | const run = async() => {
6 | let app: INestApplication = null;
7 | let moduleRef: TestingModule = null;
8 |
9 | @Injectable()
10 | class BullMQService {
11 | constructor(@InjectBullMQ() private readonly bullMq: BullMQ) {}
12 |
13 | async getTest() {
14 | this.bullMq.process(async job => {
15 | console.log('process', job);
16 | })
17 | await this.bullMq.queue.add('test', { hello: 'world' });
18 | return null;
19 | }
20 | }
21 |
22 | moduleRef = await Test.createTestingModule({
23 | imports: [
24 | BullMQModule.forRootAsync({
25 | useFactory: () => ({
26 | name: 'QueueName',
27 | config: { connection: { host: 'localhost', port: 6378 } },
28 | }),
29 | }),
30 | ],
31 | providers: [BullMQService],
32 | }).compile();
33 |
34 | app = moduleRef.createNestApplication();
35 |
36 | await app.init();
37 |
38 | const bullMQModule = moduleRef.get(BullMQModule);
39 | const bullMQService = moduleRef.get(BullMQService);
40 |
41 | await bullMQService.getTest();
42 | console.log(bullMQModule.constructor.name);
43 | console.log(bullMQService.constructor.name);
44 |
45 | await app.close();
46 | };
47 |
48 | run().then();
49 |
50 |
--------------------------------------------------------------------------------
/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "exclude": ["node_modules", "test", "dist", "**/*spec.ts"]
4 | }
5 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "commonjs",
4 | "declaration": true,
5 | "removeComments": true,
6 | "emitDecoratorMetadata": true,
7 | "experimentalDecorators": true,
8 | "target": "es2017",
9 | "sourceMap": false,
10 | "outDir": "./dist",
11 | "baseUrl": "./",
12 | "skipLibCheck": true
13 | },
14 | "include": ["src/**/*", "test/**/*"],
15 | "exclude": ["node_modules", "dist"]
16 | }
17 |
--------------------------------------------------------------------------------