├── .commitlintrc.json ├── .github └── workflows │ ├── codeql-analysis.yml │ └── nodejs-environment.yml ├── .gitignore ├── .husky ├── .gitignore ├── commit-msg └── pre-commit ├── .npmignore ├── .prettierrc ├── .release-it.json ├── LICENSE ├── README.md ├── eslint.config.mjs ├── index.d.ts ├── index.js ├── index.ts ├── lib ├── common │ ├── index.ts │ ├── mysql.decorators.ts │ └── mysql.utils.ts ├── exceptions │ └── circular-dependency.exception.ts ├── index.ts ├── interfaces │ ├── index.ts │ ├── mysql-module-async-options.interface.ts │ ├── mysql-options-factory.interface.ts │ └── mysql-options.interface.ts ├── mysql-core.module.ts ├── mysql.constants.ts └── mysql.module.ts ├── package-lock.json ├── package.json ├── renovate.json ├── tests ├── data.sql ├── docker-compose.yml ├── e2e │ ├── mysql-app-user.spec.ts │ ├── mysql-async-class.spec.ts │ ├── mysql-async-existing.spec.ts │ └── mysql.spec.ts ├── jest-e2e.json └── src │ ├── app-async.module.ts │ ├── app.module.ts │ ├── apps │ ├── app-multi-database │ │ ├── .env.example │ │ ├── app │ │ │ ├── app.module.ts │ │ │ ├── main.ts │ │ │ ├── post │ │ │ │ ├── dto │ │ │ │ │ ├── create-post.dto.ts │ │ │ │ │ └── update-post.dto.ts │ │ │ │ ├── interfaces │ │ │ │ │ └── post.interface.ts │ │ │ │ ├── post.controller.ts │ │ │ │ ├── post.module.ts │ │ │ │ └── post.service.ts │ │ │ └── users │ │ │ │ ├── dto │ │ │ │ ├── create-user.dto.ts │ │ │ │ └── update-user.dto.ts │ │ │ │ ├── interfaces │ │ │ │ └── user.interface.ts │ │ │ │ ├── users.controller.ts │ │ │ │ ├── users.module.ts │ │ │ │ └── users.service.ts │ │ ├── data.sql │ │ └── docker-compose.yml │ └── app-mysql │ │ ├── .env.example │ │ ├── app │ │ ├── app.module.ts │ │ ├── main.ts │ │ └── users │ │ │ ├── dto │ │ │ ├── create-user.dto.ts │ │ │ └── update-user.dto.ts │ │ │ ├── interfaces │ │ │ └── user.interface.ts │ │ │ ├── users.controller.ts │ │ │ ├── users.module.ts │ │ │ └── users.service.ts │ │ ├── data.sql │ │ └── docker-compose.yml │ ├── async-class-options.module.ts │ ├── async-existing-options.module.ts │ └── database.module.ts ├── tsconfig.build.json └── tsconfig.json /.commitlintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["@commitlint/config-angular"], 3 | "rules": { 4 | "subject-case": [ 5 | 2, 6 | "always", 7 | ["sentence-case", "start-case", "pascal-case", "upper-case", "lower-case"] 8 | ], 9 | "type-enum": [ 10 | 2, 11 | "always", 12 | [ 13 | "build", 14 | "chore", 15 | "ci", 16 | "docs", 17 | "feat", 18 | "fix", 19 | "perf", 20 | "refactor", 21 | "revert", 22 | "style", 23 | "test", 24 | "sample" 25 | ] 26 | ] 27 | } 28 | } -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | name: "CodeQL" 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | schedule: 9 | - cron: '0 0 * * *' 10 | 11 | jobs: 12 | analyze: 13 | name: Analyze 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - name: Checkout repository 18 | uses: actions/checkout@v4 19 | with: 20 | fetch-depth: 0 21 | 22 | - name: Initialize CodeQL 23 | uses: github/codeql-action/init@v3 24 | with: 25 | languages: javascript 26 | 27 | - name: Autobuild 28 | uses: github/codeql-action/autobuild@v3 29 | 30 | - name: Perform CodeQL Analysis 31 | uses: github/codeql-action/analyze@v3 32 | -------------------------------------------------------------------------------- /.github/workflows/nodejs-environment.yml: -------------------------------------------------------------------------------- 1 | name: Node.js CI 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | 14 | strategy: 15 | matrix: 16 | node-version: [20.x, 22.x, 24.x] 17 | 18 | steps: 19 | - uses: actions/checkout@v4 20 | - name: Use Node.js ${{ matrix.node-version }} 21 | uses: actions/setup-node@v4 22 | with: 23 | node-version: ${{ matrix.node-version }} 24 | - run: npm ci 25 | - run: npm run build --if-present 26 | 27 | integration-test: 28 | 29 | runs-on: ubuntu-latest 30 | 31 | strategy: 32 | matrix: 33 | node-version: [20.x, 22.x, 24.x] 34 | needs: [build] 35 | 36 | steps: 37 | - uses: actions/checkout@v4 38 | - name: Use Node.js ${{ matrix.node-version }} 39 | uses: actions/setup-node@v4 40 | with: 41 | node-version: ${{ matrix.node-version }} 42 | - name: Install Docker 43 | run: | 44 | sudo apt-get update 45 | curl -fsSL https://get.docker.com -o get-docker.sh 46 | sudo sh get-docker.sh 47 | sudo usermod -aG docker $USER 48 | sudo apt-get install -y docker-compose 49 | - name: Start Docker-Compose 50 | run: cd tests && docker-compose up -d 51 | - name: Install npm 52 | uses: actions/setup-node@v4 53 | with: 54 | node-version: ${{ matrix.node-version }} 55 | - run: npm install 56 | - name: Run tests e2e 57 | run: npm run test:e2e 58 | - name: Stop Docker-Compose 59 | run: cd tests && docker-compose down -v 60 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # dependencies 2 | /node_modules 3 | 4 | # IDE 5 | /.idea 6 | /.awcache 7 | /.vscode 8 | tags 9 | 10 | # misc 11 | npm-debug.log 12 | .DS_Store 13 | 14 | # tests 15 | /test 16 | /coverage 17 | /.nyc_output 18 | 19 | # dist 20 | dist 21 | 22 | #enviroments 23 | .env 24 | -------------------------------------------------------------------------------- /.husky/.gitignore: -------------------------------------------------------------------------------- 1 | _ -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | npx --no-install commitlint --edit $1 -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | npx lint-staged -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # source 2 | lib 3 | index.ts 4 | package-lock.json 5 | tslint.json 6 | tsconfig.json 7 | .prettierrc 8 | 9 | # misc 10 | .commitlintrc.json 11 | .release-it.json 12 | .eslintignore 13 | .eslintrc.js 14 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "all" 4 | } 5 | -------------------------------------------------------------------------------- /.release-it.json: -------------------------------------------------------------------------------- 1 | { 2 | "git": { 3 | "commitMessage": "chore(): release v${version}" 4 | }, 5 | "github": { 6 | "release": true 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022-2025 Tony133 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 |

2 | Nest Logo 3 |

4 | 5 | [circleci-image]: https://img.shields.io/circleci/build/github/nestjs/nest/master?token=abc123def456 6 | [circleci-url]: https://circleci.com/gh/nestjs/nest 7 | 8 |

A progressive Node.js framework for building efficient and scalable server-side applications.

9 |

10 | NPM Version 11 | Package License 12 | NPM Downloads 13 | CircleCI 14 | Coverage 15 | Discord 16 | Backers on Open Collective 17 | Sponsors on Open Collective 18 | 19 | Support us 20 | 21 |

22 | 24 | 25 | ## Description 26 | 27 | MySQL module for Nest framework (node.js) 😻 28 | 29 | ## Installation 30 | 31 | First install the module via `yarn` or `npm` or `pnpm` and do not forget to install the driver package as well: 32 | 33 | 34 | ```bash 35 | $ npm i --save nest-mysql mysql2 36 | ``` 37 | or 38 | 39 | ```bash 40 | $ yarn add nest-mysql mysql2 41 | ``` 42 | 43 | or 44 | 45 | ```bash 46 | $ pnpm add nest-mysql mysql2 47 | ``` 48 | 49 | ## Table of Contents 50 | 51 | - [Description](#description) 52 | - [Table of Contents](#table-of-contents) 53 | - [Usage](#usage) 54 | - [MySqlModule](#mysqlmodule) 55 | - [MultiConnectionsDatabase](#multi-connections-database) 56 | - [ExampleOfUse](#example-of-use) 57 | 58 | ## Usage 59 | 60 | ### MySqlModule 61 | 62 | MySqlModule is the primary entry point for this package and can be used synchronously 63 | 64 | ```typescript 65 | @Module({ 66 | imports: [ 67 | MysqlModule.forRoot({ 68 | host: 'localhost', 69 | database: 'test2', 70 | password: 'root', 71 | user: 'root', 72 | port: 3306, 73 | }), 74 | ], 75 | }) 76 | ``` 77 | 78 | or asynchronously 79 | 80 | ```typescript 81 | @Module({ 82 | imports: [ 83 | MysqlModule.forRootAsync({ 84 | useFactory: () => ({ 85 | host: 'localhost', 86 | database: 'test', 87 | password: 'root', 88 | user: 'root', 89 | port: 3306, 90 | }), 91 | }), 92 | ], 93 | }) 94 | ``` 95 | 96 | ## Example of use 97 | 98 | UsersService: 99 | 100 | ```typescript 101 | import { Injectable } from '@nestjs/common'; 102 | import { InjectClient } from 'nest-mysql'; 103 | import { Connection } from 'mysql2'; 104 | import { User } from '../interfaces/user.interface'; 105 | 106 | @Injectable() 107 | export class UsersService { 108 | constructor(@InjectClient() private readonly connection: Connection) {} 109 | 110 | async findAll(): Promise { 111 | const users = await this.connection.query('SELECT * FROM users'); 112 | const results = Object.assign([{}], users[0]); 113 | 114 | return results; 115 | } 116 | } 117 | ``` 118 | 119 | UsersController: 120 | 121 | ```typescript 122 | import { Controller, Get } from '@nestjs/common'; 123 | import { UsersService } from './users.service'; 124 | import { User } from '../interfaces/user.interface'; 125 | 126 | @Controller() 127 | export class UsersController { 128 | constructor(private readonly usersService: UsersService) {} 129 | 130 | @Get() 131 | async getAllUsers(): Promise { 132 | return await this.usersService.findAll(); 133 | } 134 | } 135 | ``` 136 | 137 | ## Multi Connections Database 138 | 139 | ```typescript 140 | @Module({ 141 | imports: [ 142 | MysqlModule.forRootAsync( 143 | { 144 | useFactory: () => ({ 145 | host: 'localhost', 146 | database: 'test1', 147 | password: 'root', 148 | user: 'root', 149 | port: 3306, 150 | }), 151 | }, 152 | 'db1Connection', 153 | ), 154 | MysqlModule.forRootAsync( 155 | { 156 | useFactory: () => ({ 157 | host: 'localhost', 158 | database: 'test2', 159 | password: 'root', 160 | user: 'root', 161 | port: 3307, 162 | }), 163 | }, 164 | 'db2Connection', 165 | ), 166 | ], 167 | controllers: [], 168 | providers: [], 169 | }) 170 | export class AppModule {} 171 | ``` 172 | 173 | Usage example with Multi Connection 174 | 175 | PostService: 176 | 177 | ```typescript 178 | import { Pool } from 'mysql2'; 179 | import { InjectConnection } from 'nest-mysql'; 180 | import { CreatePostDto } from './dto/create-post.dto'; 181 | import { Post } from '../interfaces/post.interface'; 182 | 183 | @Injectable() 184 | export class PostService { 185 | constructor( 186 | @InjectConnection('db2Connection') 187 | private dbConnection: Pool, 188 | ) {} 189 | 190 | public async findAll(): Promise { 191 | const posts = await this.dbConnection.query('SELECT * FROM posts'); 192 | const results = Object.assign([{}], posts[0]); 193 | 194 | return results; 195 | } 196 | 197 | public async create(createPostDto: CreatePostDto): Promise { 198 | try { 199 | const post = await this.dbConnection.query( 200 | 'INSERT INTO posts (title, description) VALUES (?, ?)', 201 | [createPostDto.title, createPostDto.description], 202 | ); 203 | return post; 204 | } catch (err) { 205 | throw new HttpException(err, HttpStatus.BAD_REQUEST); 206 | } 207 | } 208 | } 209 | ``` 210 | 211 | UsersService: 212 | 213 | ```typescript 214 | import { Pool } from 'mysql2'; 215 | import { InjectConnection } from 'nest-mysql'; 216 | import { CreateUserDto } from './dto/create-user.dto'; 217 | import { User } from '../interfaces/user.interface'; 218 | 219 | @Injectable() 220 | export class UsersService { 221 | constructor( 222 | @InjectConnection('db1Connection') 223 | private dbConnection: Pool, 224 | ) {} 225 | 226 | public async findAll(): Promise { 227 | const users = await this.dbConnection.query('SELECT * FROM users'); 228 | const results = Object.assign([{}], users[0]); 229 | 230 | return results; 231 | } 232 | 233 | public async create(createUserDto: CreateUserDto): Promise { 234 | try { 235 | const user = await this.dbConnection.query( 236 | 'INSERT INTO users (firstName, lastName) VALUES (?, ?)', 237 | [createUserDto.firstName, createUserDto.lastName], 238 | ); 239 | return user; 240 | } catch (err) { 241 | throw new HttpException(err, HttpStatus.BAD_REQUEST); 242 | } 243 | } 244 | } 245 | ``` 246 | 247 | For more information on `node-mysql` see [here](https://github.com/sidorares/node-mysql2) 248 | 249 | ## Contribute 250 | Feel free to help this library, I'm quite busy with also another Nestjs packages, but the community will appreciate the effort of improving this library. Make sure you follow the guidelines 251 | 252 | ## Stay in touch 253 | 254 | - Author - [Tony133](https://github.com/Tony133) 255 | - Framework - [https://nestjs.com](https://nestjs.com/) 256 | 257 | ## License 258 | 259 | [MIT licensed](LICENSE) 260 | -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | //@ts-check 2 | import eslint from '@eslint/js'; 3 | import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'; 4 | import globals from 'globals'; 5 | import tseslint from 'typescript-eslint'; 6 | 7 | export default tseslint.config( 8 | { 9 | ignores: ['tests/**'], 10 | }, 11 | eslint.configs.recommended, 12 | ...tseslint.configs.recommendedTypeChecked, 13 | eslintPluginPrettierRecommended, 14 | { 15 | languageOptions: { 16 | globals: { 17 | ...globals.node, 18 | ...globals.jest, 19 | }, 20 | ecmaVersion: 5, 21 | sourceType: 'module', 22 | parserOptions: { 23 | projectService: true, 24 | tsconfigRootDir: import.meta.dirname, 25 | }, 26 | }, 27 | }, 28 | { 29 | rules: { 30 | '@typescript-eslint/no-explicit-any': 'off', 31 | '@typescript-eslint/no-unsafe-assignment': 'off', 32 | '@typescript-eslint/no-unsafe-call': 'off', 33 | '@typescript-eslint/no-unsafe-member-access': 'off', 34 | '@typescript-eslint/no-unsafe-function-type': 'off', 35 | '@typescript-eslint/no-unsafe-argument': 'off', 36 | '@typescript-eslint/no-unsafe-return': 'off', 37 | '@typescript-eslint/no-unused-expressions': 'off', 38 | '@typescript-eslint/await-thenable': 'off', 39 | '@typescript-eslint/unbound-method': 'off', 40 | '@typescript-eslint/no-unnecessary-type-assertion': 'off', 41 | }, 42 | }, 43 | ); -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /lib/common/index.ts: -------------------------------------------------------------------------------- 1 | export * from './mysql.decorators'; 2 | export * from './mysql.utils'; 3 | -------------------------------------------------------------------------------- /lib/common/mysql.decorators.ts: -------------------------------------------------------------------------------- 1 | import { Inject } from '@nestjs/common'; 2 | import { MysqlModuleOptions } from '../interfaces/mysql-options.interface'; 3 | import { getConnectionToken } from './mysql.utils'; 4 | 5 | export const InjectClient = (connection?: string) => { 6 | return Inject(getConnectionToken(connection)); 7 | }; 8 | 9 | export const InjectPool = (connection?: string) => { 10 | return Inject(getConnectionToken(connection)); 11 | }; 12 | 13 | export const InjectConnection: ( 14 | connection?: MysqlModuleOptions | string, 15 | ) => ParameterDecorator = (connection?: MysqlModuleOptions | string) => 16 | Inject(getConnectionToken(connection)); 17 | -------------------------------------------------------------------------------- /lib/common/mysql.utils.ts: -------------------------------------------------------------------------------- 1 | import { MysqlModuleOptions } from '../interfaces'; 2 | import { DEFAULT_CONNECTION_NAME } from '../mysql.constants'; 3 | import { Observable } from 'rxjs'; 4 | import { delay, retryWhen, scan } from 'rxjs/operators'; 5 | import { randomUUID } from 'node:crypto'; 6 | import { Logger } from '@nestjs/common'; 7 | import { CircularDependencyException } from '../exceptions/circular-dependency.exception'; 8 | 9 | const logger = new Logger('MysqlModule'); 10 | 11 | /** 12 | * This function generates an injection token for an Database 13 | * @param {Function} This parameter can either be a Databases 14 | * @param {string} [connection='default'] Connection name 15 | * @returns {string} The Entity injection token 16 | */ 17 | export function getDbToken( 18 | database: Function, 19 | connection: MysqlModuleOptions | string = DEFAULT_CONNECTION_NAME, 20 | ) { 21 | if (database === null || database === undefined) { 22 | throw new CircularDependencyException('@InjectClient()'); 23 | } 24 | const connectionPrefix = getConnectionPrefix(connection); 25 | return `${connectionPrefix}${database.name}`; 26 | } 27 | 28 | export function getConnectionToken( 29 | connection: MysqlModuleOptions | string = DEFAULT_CONNECTION_NAME, 30 | ): string | Function { 31 | if (typeof connection === 'string') { 32 | return connection; 33 | } 34 | return `${connection.name || DEFAULT_CONNECTION_NAME}`; 35 | } 36 | 37 | /** 38 | * This function returns a Connection prefix based on the connection name 39 | * @param {MysqlModuleOptions | string} [connection='default'] This optional parameter is either 40 | * a MysqlModuleOptions or a string. 41 | * @returns {string | Function} The Connection injection token. 42 | */ 43 | export function getConnectionPrefix( 44 | connection: MysqlModuleOptions | string = DEFAULT_CONNECTION_NAME, 45 | ): string { 46 | if (connection === DEFAULT_CONNECTION_NAME) { 47 | return ''; 48 | } 49 | if (typeof connection === 'string') { 50 | return connection + '_'; 51 | } 52 | if (connection.name === DEFAULT_CONNECTION_NAME || !connection.name) { 53 | return ''; 54 | } 55 | return connection.name + '_'; 56 | } 57 | 58 | export function handleRetry( 59 | retryAttempts = 9, 60 | retryDelay = 3000, 61 | ): (source: Observable) => Observable { 62 | return (source: Observable) => 63 | source.pipe( 64 | retryWhen((e) => 65 | e.pipe( 66 | scan((errorCount, error: Error) => { 67 | logger.error( 68 | `Unable to connect to the database. Retrying (${ 69 | errorCount + 1 70 | })...`, 71 | error.stack, 72 | ); 73 | if (errorCount + 1 >= retryAttempts) { 74 | throw error; 75 | } 76 | return errorCount + 1; 77 | }, 0), 78 | delay(retryDelay), 79 | ), 80 | ), 81 | ); 82 | } 83 | 84 | export function getConnectionName(options: MysqlModuleOptions) { 85 | return options && options.name ? options.name : DEFAULT_CONNECTION_NAME; 86 | } 87 | 88 | export const generateString = () => randomUUID(); 89 | -------------------------------------------------------------------------------- /lib/exceptions/circular-dependency.exception.ts: -------------------------------------------------------------------------------- 1 | export class CircularDependencyException extends Error { 2 | constructor(context?: string) { 3 | const ctx = context ? ` inside ${context}` : ``; 4 | super( 5 | `A circular dependency has been detected${ctx}. Please, make sure that each side of a bidirectional relationships are decorated with "forwardRef()". Also, try to eliminate barrel files because they can lead to an unexpected behavior too.`, 6 | ); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /lib/index.ts: -------------------------------------------------------------------------------- 1 | export * from './common'; 2 | export * from './interfaces'; 3 | export * from './mysql.module'; 4 | -------------------------------------------------------------------------------- /lib/interfaces/index.ts: -------------------------------------------------------------------------------- 1 | export * from './mysql-options.interface'; 2 | export * from './mysql-options-factory.interface'; 3 | export * from './mysql-module-async-options.interface'; 4 | -------------------------------------------------------------------------------- /lib/interfaces/mysql-module-async-options.interface.ts: -------------------------------------------------------------------------------- 1 | import { ModuleMetadata, Type } from '@nestjs/common/interfaces'; 2 | import { MysqlModuleOptions } from './mysql-options.interface'; 3 | import { MysqlOptionsFactory } from './mysql-options-factory.interface'; 4 | 5 | export interface MysqlModuleAsyncOptions 6 | extends Pick { 7 | name?: string; 8 | inject?: any[]; 9 | useClass?: Type; 10 | useExisting?: Type; 11 | useFactory?: ( 12 | ...args: any[] 13 | ) => Promise | MysqlModuleOptions; 14 | } 15 | -------------------------------------------------------------------------------- /lib/interfaces/mysql-options-factory.interface.ts: -------------------------------------------------------------------------------- 1 | import { MysqlModuleOptions } from './mysql-options.interface'; 2 | 3 | export interface MysqlOptionsFactory { 4 | createMysqlOptions( 5 | connectionName?: string, 6 | ): Promise | MysqlModuleOptions; 7 | } 8 | -------------------------------------------------------------------------------- /lib/interfaces/mysql-options.interface.ts: -------------------------------------------------------------------------------- 1 | export interface MysqlModuleOptions { 2 | name?: string; 3 | host?: string; 4 | database?: string; 5 | password?: string; 6 | socketPath?: string; 7 | localAddress?: string; 8 | user?: string; 9 | port?: number; 10 | charset?: string; 11 | retryAttempts?: number; 12 | retryDelay?: number; 13 | waitForConnections?: boolean; 14 | connectionLimit?: number; 15 | queueLimit?: number; 16 | connectTimeout?: number; 17 | timezone?: string; 18 | ssl?: string | object; 19 | insecureAuth?: boolean; 20 | debug?: boolean; 21 | typeCast?: boolean; 22 | trace?: boolean; 23 | stringifyObjects?: boolean; 24 | supportBigNumbers?: boolean; 25 | bigNumberStrings?: boolean; 26 | dateStrings?: boolean; 27 | multipleStatements?: boolean; 28 | localInfile?: boolean; 29 | pool?: boolean; 30 | } 31 | -------------------------------------------------------------------------------- /lib/mysql-core.module.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Global, 3 | Module, 4 | DynamicModule, 5 | Provider, 6 | Type, 7 | OnApplicationShutdown, 8 | Inject, 9 | } from '@nestjs/common'; 10 | import { 11 | MysqlModuleAsyncOptions, 12 | MysqlModuleOptions, 13 | MysqlOptionsFactory, 14 | } from './interfaces'; 15 | import { getConnectionToken, handleRetry } from './common/mysql.utils'; 16 | import { MYSQL_MODULE_OPTIONS } from './mysql.constants'; 17 | import { ModuleRef } from '@nestjs/core'; 18 | import { defer, lastValueFrom } from 'rxjs'; 19 | import * as mysql from 'mysql2/promise'; 20 | 21 | @Global() 22 | @Module({}) 23 | export class MysqlCoreModule implements OnApplicationShutdown { 24 | constructor( 25 | @Inject(MYSQL_MODULE_OPTIONS) 26 | private readonly options: MysqlModuleOptions, 27 | private readonly moduleRef: ModuleRef, 28 | ) {} 29 | 30 | public static forRoot( 31 | options: MysqlModuleOptions, 32 | connection?: string, 33 | ): DynamicModule { 34 | const knexModuleOptions = { 35 | provide: MYSQL_MODULE_OPTIONS, 36 | useValue: options, 37 | }; 38 | 39 | const connectionProvider: Provider = { 40 | provide: getConnectionToken(connection), 41 | useFactory: async () => await this.createConnectionFactory(options), 42 | }; 43 | 44 | return { 45 | module: MysqlCoreModule, 46 | providers: [connectionProvider, knexModuleOptions], 47 | exports: [connectionProvider], 48 | }; 49 | } 50 | 51 | public static forRootAsync( 52 | options: MysqlModuleAsyncOptions, 53 | connection: string, 54 | ): DynamicModule { 55 | const connectionProvider: Provider = { 56 | provide: getConnectionToken(connection), 57 | useFactory: async (options: MysqlModuleOptions) => { 58 | return await this.createConnectionFactory(options); 59 | }, 60 | inject: [MYSQL_MODULE_OPTIONS], 61 | }; 62 | 63 | return { 64 | module: MysqlCoreModule, 65 | imports: options.imports, 66 | providers: [...this.createAsyncProviders(options), connectionProvider], 67 | exports: [connectionProvider], 68 | }; 69 | } 70 | 71 | async onApplicationShutdown(): Promise { 72 | const connection = this.moduleRef.get( 73 | getConnectionToken(this.options as MysqlModuleOptions) as Type, 74 | ); 75 | connection && (await connection.end); 76 | } 77 | 78 | public static createAsyncProviders( 79 | options: MysqlModuleAsyncOptions, 80 | ): Provider[] { 81 | if (options.useExisting || options.useFactory) { 82 | return [this.createAsyncOptionsProvider(options)]; 83 | } 84 | 85 | const useClass = options.useClass as Type; 86 | return [ 87 | this.createAsyncOptionsProvider(options), 88 | { 89 | provide: useClass, 90 | useClass, 91 | }, 92 | ]; 93 | } 94 | 95 | public static createAsyncOptionsProvider( 96 | options: MysqlModuleAsyncOptions, 97 | ): Provider { 98 | if (options.useFactory) { 99 | return { 100 | provide: MYSQL_MODULE_OPTIONS, 101 | useFactory: options.useFactory, 102 | inject: options.inject || [], 103 | }; 104 | } 105 | 106 | // `as Type` is a workaround for microsoft/TypeScript#31603 107 | const inject = [ 108 | (options.useClass || options.useExisting) as Type, 109 | ]; 110 | 111 | return { 112 | provide: MYSQL_MODULE_OPTIONS, 113 | useFactory: async ( 114 | optionsFactory: MysqlOptionsFactory, 115 | ): Promise => { 116 | return await optionsFactory.createMysqlOptions(); 117 | }, 118 | inject, 119 | }; 120 | } 121 | 122 | private static async createConnectionFactory( 123 | options: MysqlModuleOptions, 124 | ): Promise { 125 | return lastValueFrom( 126 | defer(async () => { 127 | if (options.pool === true) { 128 | const clientPool = mysql.createPool(options); 129 | return clientPool; 130 | } else { 131 | const client = mysql.createConnection(options); 132 | return client; 133 | } 134 | }).pipe(handleRetry(options.retryAttempts, options.retryDelay)), 135 | ); 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /lib/mysql.constants.ts: -------------------------------------------------------------------------------- 1 | export const MYSQL_MODULE_OPTIONS = 'MysqlModuleOptions'; 2 | export const MYSQL_MODULE_ID = 'MysqlModuleId'; 3 | export const DEFAULT_CONNECTION_NAME = 'default'; 4 | -------------------------------------------------------------------------------- /lib/mysql.module.ts: -------------------------------------------------------------------------------- 1 | import { DynamicModule, Module } from '@nestjs/common'; 2 | import { MysqlCoreModule } from './mysql-core.module'; 3 | import { MysqlModuleAsyncOptions, MysqlModuleOptions } from './interfaces'; 4 | 5 | @Module({}) 6 | export class MysqlModule { 7 | public static forRoot( 8 | options: MysqlModuleOptions, 9 | connection?: string, 10 | ): DynamicModule { 11 | return { 12 | module: MysqlModule, 13 | imports: [MysqlCoreModule.forRoot(options, connection)], 14 | }; 15 | } 16 | 17 | public static forRootAsync( 18 | options: MysqlModuleAsyncOptions, 19 | connection?: string, 20 | ): DynamicModule { 21 | return { 22 | module: MysqlModule, 23 | imports: [MysqlCoreModule.forRootAsync(options, connection)], 24 | }; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nest-mysql", 3 | "version": "0.0.20", 4 | "description": "Mysql module for Nest framework (node.js) 😻", 5 | "author": "Tony133", 6 | "license": "MIT", 7 | "directories": { 8 | "lib": "lib", 9 | "test": "tests" 10 | }, 11 | "scripts": { 12 | "build": "rm -rf dist && tsc -p tsconfig.json", 13 | "format": "prettier --write \"**/*.ts\"", 14 | "lint": "eslint 'lib/**/*.ts' --fix", 15 | "prepublish:npm": "npm run build", 16 | "publish:npm": "npm publish --access public", 17 | "prepublish:next": "npm run build", 18 | "publish:next": "npm publish --access public --tag next", 19 | "test:e2e": "jest --config ./tests/jest-e2e.json --detectOpenHandles --forceExit", 20 | "test:e2e:dev": "jest --config ./tests/jest-e2e.json --runInBand --watch", 21 | "prerelease": "npm run build", 22 | "release": "release-it", 23 | "prepare": "husky" 24 | }, 25 | "devDependencies": { 26 | "@commitlint/cli": "19.8.1", 27 | "@commitlint/config-angular": "19.8.1", 28 | "@eslint/eslintrc": "3.3.1", 29 | "@eslint/js": "9.28.0", 30 | "@nestjs/common": "11.1.3", 31 | "@nestjs/config": "4.0.2", 32 | "@nestjs/core": "11.1.3", 33 | "@nestjs/platform-fastify": "11.1.3", 34 | "@nestjs/testing": "11.1.3", 35 | "@types/jest": "29.5.14", 36 | "@types/node": "22.15.30", 37 | "@types/supertest": "6.0.3", 38 | "eslint": "9.28.0", 39 | "eslint-config-prettier": "10.1.5", 40 | "eslint-plugin-import": "2.31.0", 41 | "eslint-plugin-prettier": "5.4.1", 42 | "globals": "16.2.0", 43 | "husky": "9.1.7", 44 | "jest": "29.7.0", 45 | "mysql2": "3.14.1", 46 | "prettier": "3.5.3", 47 | "reflect-metadata": "0.2.2", 48 | "release-it": "19.0.3", 49 | "rxjs": "7.8.2", 50 | "supertest": "^7.0.0", 51 | "ts-jest": "29.3.4", 52 | "ts-node": "10.9.2", 53 | "tsconfig-paths": "4.2.0", 54 | "typescript": "5.8.3", 55 | "typescript-eslint": "8.33.1" 56 | }, 57 | "peerDependencies": { 58 | "@nestjs/common": "^9.0.0 || ^10.0.0 || ^11.0.0", 59 | "@nestjs/core": "^9.0.0 || ^10.0.0 || ^11.0.0", 60 | "mysql2": "^2.3.3 || ^3.0.0", 61 | "reflect-metadata": "^0.1.13 || ^0.2.0", 62 | "rxjs": "^6.6.3 || ^7.2.0" 63 | }, 64 | "lint-staged": { 65 | "**/*.{ts,json}": [] 66 | }, 67 | "repository": { 68 | "type": "git", 69 | "url": "https://github.com/tony133/nestjs-mysql" 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "semanticCommits": true, 3 | "packageRules": [{ 4 | "depTypeList": ["devDependencies"], 5 | "automerge": true 6 | }], 7 | "extends": [ 8 | "config:base" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /tests/data.sql: -------------------------------------------------------------------------------- 1 | CREATE DATABASE IF NOT EXISTS test; 2 | 3 | USE test; 4 | 5 | CREATE TABLE IF NOT EXISTS users ( 6 | id INT(11) AUTO_INCREMENT, 7 | firstName VARCHAR(100) NOT NULL, 8 | lastName VARCHAR(50) NOT NULL, 9 | PRIMARY KEY (id) 10 | ); 11 | -------------------------------------------------------------------------------- /tests/docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | mysql: 3 | image: mysql:8.3 4 | environment: 5 | MYSQL_ROOT_USER: root 6 | MYSQL_ROOT_PASSWORD: root 7 | MYSQL_DATABASE: test 8 | volumes: 9 | - db-data:/var/lib/mysql 10 | - ./data.sql:/docker-entrypoint-initdb.d/data.sql 11 | 12 | ports: 13 | - "3306:3306" 14 | restart: always 15 | 16 | volumes: 17 | db-data: 18 | 19 | -------------------------------------------------------------------------------- /tests/e2e/mysql-app-user.spec.ts: -------------------------------------------------------------------------------- 1 | import { HttpStatus } from '@nestjs/common'; 2 | import { TestingModule, Test } from '@nestjs/testing'; 3 | import * as request from 'supertest'; 4 | import { UsersModule } from '../src/apps/app-mysql/app/users/users.module'; 5 | import { AppModule } from '../src/apps/app-mysql/app/app.module'; 6 | import { 7 | FastifyAdapter, 8 | NestFastifyApplication, 9 | } from '@nestjs/platform-fastify'; 10 | 11 | describe('[Feature] User - /user', () => { 12 | let app: NestFastifyApplication; 13 | 14 | beforeAll(async () => { 15 | const moduleFixture: TestingModule = await Test.createTestingModule({ 16 | imports: [AppModule, UsersModule], 17 | }).compile(); 18 | 19 | app = moduleFixture.createNestApplication( 20 | new FastifyAdapter(), 21 | ); 22 | await app.init(); 23 | await app.getHttpAdapter().getInstance().ready(); 24 | }); 25 | 26 | it('should create a new user [POST /users]', () => { 27 | return request(app.getHttpServer()) 28 | .post('/users') 29 | .set('Accept', 'application/json') 30 | .send({ 31 | firstName: 'firstName', 32 | lastName: 'lastName', 33 | }) 34 | .then(({ body }) => { 35 | expect(body[0]).toEqual({ 36 | fieldCount: 0, 37 | changedRows: 0, 38 | affectedRows: 1, 39 | insertId: 1, 40 | info: '', 41 | serverStatus: 2, 42 | warningStatus: 0, 43 | }); 44 | expect(HttpStatus.CREATED); 45 | }); 46 | }); 47 | 48 | it('should get all users [GET /users]', () => { 49 | return request(app.getHttpServer()) 50 | .get('/users') 51 | .expect(HttpStatus.OK) 52 | .set('Accept', 'application/json') 53 | .then(({ body }) => { 54 | expect(body).toEqual([ 55 | { 56 | id: 1, 57 | firstName: 'firstName', 58 | lastName: 'lastName', 59 | }, 60 | ]); 61 | }); 62 | }); 63 | 64 | it('should get a user [GET /users/:id]', () => { 65 | return request(app.getHttpServer()) 66 | .get('/users/1') 67 | .expect(HttpStatus.OK) 68 | .set('Accept', 'application/json') 69 | .then(({ body }) => { 70 | expect(body).toEqual([ 71 | { 72 | id: 1, 73 | firstName: 'firstName', 74 | lastName: 'lastName', 75 | }, 76 | ]); 77 | }); 78 | }); 79 | 80 | it('should update a user [PUT /users/:id]', () => { 81 | return request(app.getHttpServer()) 82 | .put('/users/1') 83 | .expect(HttpStatus.OK) 84 | .send({ 85 | firstName: 'firstName update', 86 | lastName: 'lastName update', 87 | }) 88 | .then(({ body }) => { 89 | expect(body[0]).toEqual({ 90 | fieldCount: 0, 91 | affectedRows: 1, 92 | insertId: 0, 93 | info: 'Rows matched: 1 Changed: 1 Warnings: 0', 94 | serverStatus: 2, 95 | warningStatus: 0, 96 | changedRows: 1, 97 | }); 98 | }); 99 | }); 100 | 101 | it('should delete a user by id [DELETE /users/:id]', () => { 102 | return request(app.getHttpServer()) 103 | .delete('/users/1') 104 | .expect(HttpStatus.OK); 105 | }); 106 | 107 | afterAll(async () => { 108 | await app.close(); 109 | }); 110 | }); 111 | -------------------------------------------------------------------------------- /tests/e2e/mysql-async-class.spec.ts: -------------------------------------------------------------------------------- 1 | import { HttpStatus } from '@nestjs/common'; 2 | import { Test } from '@nestjs/testing'; 3 | import { AsyncOptionsClassModule } from '../src/async-class-options.module'; 4 | import * as request from 'supertest'; 5 | import { 6 | FastifyAdapter, 7 | NestFastifyApplication, 8 | } from '@nestjs/platform-fastify'; 9 | 10 | describe('Mysql (async configuration)', () => { 11 | let app: NestFastifyApplication; 12 | 13 | beforeAll(async () => { 14 | const moduleFixture = await Test.createTestingModule({ 15 | imports: [AsyncOptionsClassModule], 16 | }).compile(); 17 | 18 | app = moduleFixture.createNestApplication( 19 | new FastifyAdapter(), 20 | ); 21 | await app.init(); 22 | await app.getHttpAdapter().getInstance().ready(); 23 | }); 24 | 25 | it(`should return created entity`, () => { 26 | return request(app.getHttpServer()) 27 | .post('/users') 28 | .expect(HttpStatus.CREATED) 29 | .set('Accept', 'application/json') 30 | .send({ 31 | firstName: 'firstName', 32 | lastName: 'lastName', 33 | }) 34 | .expect(({ body }) => { 35 | expect(body[0]).toEqual({ 36 | fieldCount: 0, 37 | changedRows: 0, 38 | affectedRows: 1, 39 | insertId: body[0].insertId, 40 | info: '', 41 | serverStatus: 2, 42 | warningStatus: 0, 43 | }); 44 | }); 45 | }); 46 | 47 | afterAll(async () => { 48 | await app.close(); 49 | }); 50 | }); 51 | -------------------------------------------------------------------------------- /tests/e2e/mysql-async-existing.spec.ts: -------------------------------------------------------------------------------- 1 | import { HttpStatus } from '@nestjs/common'; 2 | import { Test } from '@nestjs/testing'; 3 | import { AsyncOptionsExistingModule } from '../src/async-existing-options.module'; 4 | import * as request from 'supertest'; 5 | import { 6 | FastifyAdapter, 7 | NestFastifyApplication, 8 | } from '@nestjs/platform-fastify'; 9 | 10 | describe('Mysql (async configuration)', () => { 11 | let app: NestFastifyApplication; 12 | 13 | beforeAll(async () => { 14 | const moduleFixture = await Test.createTestingModule({ 15 | imports: [AsyncOptionsExistingModule], 16 | }).compile(); 17 | 18 | app = moduleFixture.createNestApplication( 19 | new FastifyAdapter(), 20 | ); 21 | await app.init(); 22 | await app.getHttpAdapter().getInstance().ready(); 23 | }); 24 | 25 | it(`should return created entity`, () => { 26 | return request(app.getHttpServer()) 27 | .post('/users') 28 | .expect(HttpStatus.CREATED) 29 | .set('Accept', 'application/json') 30 | .send({ 31 | firstName: 'firstName', 32 | lastName: 'lastName', 33 | }) 34 | .expect(({ body }) => { 35 | expect(body[0]).toEqual({ 36 | fieldCount: 0, 37 | changedRows: 0, 38 | affectedRows: 1, 39 | insertId: body[0].insertId, 40 | info: '', 41 | serverStatus: 2, 42 | warningStatus: 0, 43 | }); 44 | }); 45 | }); 46 | 47 | afterAll(async () => { 48 | await app.close(); 49 | }); 50 | }); 51 | -------------------------------------------------------------------------------- /tests/e2e/mysql.spec.ts: -------------------------------------------------------------------------------- 1 | import { Test } from '@nestjs/testing'; 2 | import { ApplicationModule } from '../src/app.module'; 3 | import * as request from 'supertest'; 4 | import { 5 | FastifyAdapter, 6 | NestFastifyApplication, 7 | } from '@nestjs/platform-fastify'; 8 | 9 | describe('Mysql', () => { 10 | let app: NestFastifyApplication; 11 | 12 | beforeAll(async () => { 13 | const moduleFixture = await Test.createTestingModule({ 14 | imports: [ApplicationModule], 15 | }).compile(); 16 | 17 | app = moduleFixture.createNestApplication( 18 | new FastifyAdapter(), 19 | ); 20 | await app.init(); 21 | await app.getHttpAdapter().getInstance().ready(); 22 | }); 23 | 24 | it(`should return created entity`, () => { 25 | return request(app.getHttpServer()) 26 | .post('/users') 27 | .expect(201) 28 | .set('Accept', 'application/json') 29 | .send({ 30 | firstName: 'firstName', 31 | lastName: 'lastName', 32 | }) 33 | .expect(({ body }) => { 34 | expect(body[0]).toEqual({ 35 | fieldCount: 0, 36 | changedRows: 0, 37 | affectedRows: 1, 38 | insertId: body[0].insertId, 39 | info: '', 40 | serverStatus: 2, 41 | warningStatus: 0, 42 | }); 43 | }); 44 | }); 45 | 46 | afterAll(async () => { 47 | await app.close(); 48 | }); 49 | }); 50 | -------------------------------------------------------------------------------- /tests/jest-e2e.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 | } -------------------------------------------------------------------------------- /tests/src/app-async.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { AppModule } from './apps/app-mysql/app/app.module'; 3 | import { DatabaseModule } from './database.module'; 4 | 5 | @Module({ 6 | imports: [DatabaseModule.forRoot(), AppModule], 7 | }) 8 | export class AsyncApplicationModule {} 9 | -------------------------------------------------------------------------------- /tests/src/app.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { MysqlModule } from '../../lib/mysql.module'; 3 | import { UsersModule } from './apps/app-mysql/app/users/users.module'; 4 | 5 | @Module({ 6 | imports: [ 7 | MysqlModule.forRoot({ 8 | host: '127.0.0.1', 9 | database: 'test', 10 | password: 'root', 11 | user: 'root', 12 | port: 3306, 13 | }), 14 | UsersModule, 15 | ], 16 | }) 17 | export class ApplicationModule {} 18 | -------------------------------------------------------------------------------- /tests/src/apps/app-multi-database/.env.example: -------------------------------------------------------------------------------- 1 | MYSQL_HOST_CONNECT_DB_ONE="localhost" 2 | MYSQL_DB_CONNECT_DB_ONE="test1" 3 | MYSQL_PASSWORD_CONNECT_DB_ONE="root" 4 | MYSQL_USER_CONNECT_DB_ONE="root" 5 | MYSQL_PORT_CONNECT_DB_ONE=3306 6 | 7 | MYSQL_HOST_CONNECT_DB_TWO="localhost" 8 | MYSQL_DB_CONNECT_DB_TWO="test2" 9 | MYSQL_PASSWORD_CONNECT_DB_TWO="root" 10 | MYSQL_USER_CONNECT_DB_TWO="root" 11 | MYSQL_PORT_CONNECT_DB_TWO=3307 -------------------------------------------------------------------------------- /tests/src/apps/app-multi-database/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { ConfigModule, ConfigService } from '@nestjs/config'; 3 | import { MysqlModule } from '../../../../../lib'; 4 | import { PostModule } from './post/post.module'; 5 | import { UsersModule } from './users/users.module'; 6 | 7 | @Module({ 8 | imports: [ 9 | MysqlModule.forRootAsync( 10 | { 11 | imports: [ConfigModule], 12 | inject: [ConfigService], 13 | useFactory: (config: ConfigService) => ({ 14 | host: config.get('MYSQL_HOST_CONNECT_DB_ONE'), 15 | database: config.get('MYSQL_DB_CONNECT_DB_ONE'), 16 | password: config.get('MYSQL_PASSWORD_CONNECT_DB_ONE'), 17 | user: config.get('MYSQL_USER_CONNECT_DB_ONE'), 18 | port: config.get('MYSQL_PORT_CONNECT_DB_ONE'), 19 | }), 20 | }, 21 | 'db1Connection', 22 | ), 23 | MysqlModule.forRootAsync( 24 | { 25 | imports: [ConfigModule], 26 | inject: [ConfigService], 27 | useFactory: (config: ConfigService) => ({ 28 | host: config.get('MYSQL_HOST_CONNECT_DB_TWO'), 29 | database: config.get('MYSQL_DB_CONNECT_DB_TWO'), 30 | password: config.get('MYSQL_PASSWORD_CONNECT_DB_TWO'), 31 | user: config.get('MYSQL_USER_CONNECT_DB_TWO'), 32 | port: config.get('MYSQL_PORT_CONNECT_DB_TWO'), 33 | }), 34 | }, 35 | 'db2Connection', 36 | ), 37 | UsersModule, 38 | PostModule, 39 | ], 40 | controllers: [], 41 | providers: [], 42 | }) 43 | export class AppModule {} 44 | -------------------------------------------------------------------------------- /tests/src/apps/app-multi-database/app/main.ts: -------------------------------------------------------------------------------- 1 | import { NestFactory } from '@nestjs/core'; 2 | import { AppModule } from './app.module'; 3 | import { 4 | NestFastifyApplication, 5 | FastifyAdapter, 6 | } from '@nestjs/platform-fastify'; 7 | 8 | async function bootstrap() { 9 | const app = await NestFactory.create( 10 | AppModule, 11 | new FastifyAdapter(), 12 | ); 13 | 14 | await app.listen(3000); 15 | } 16 | bootstrap(); 17 | -------------------------------------------------------------------------------- /tests/src/apps/app-multi-database/app/post/dto/create-post.dto.ts: -------------------------------------------------------------------------------- 1 | export class CreatePostDto { 2 | readonly title: string; 3 | readonly description: string; 4 | } 5 | -------------------------------------------------------------------------------- /tests/src/apps/app-multi-database/app/post/dto/update-post.dto.ts: -------------------------------------------------------------------------------- 1 | export class UpdatePostDto { 2 | readonly title: string; 3 | readonly description: string; 4 | } 5 | -------------------------------------------------------------------------------- /tests/src/apps/app-multi-database/app/post/interfaces/post.interface.ts: -------------------------------------------------------------------------------- 1 | export class Post { 2 | id: number; 3 | title: string; 4 | description: string; 5 | } 6 | -------------------------------------------------------------------------------- /tests/src/apps/app-multi-database/app/post/post.controller.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Controller, 3 | Get, 4 | Post, 5 | Body, 6 | Patch, 7 | Param, 8 | Delete, 9 | } from '@nestjs/common'; 10 | import { PostService } from './post.service'; 11 | import { CreatePostDto } from './dto/create-post.dto'; 12 | import { UpdatePostDto } from './dto/update-post.dto'; 13 | 14 | @Controller('posts') 15 | export class PostController { 16 | constructor(private readonly postService: PostService) {} 17 | 18 | @Post() 19 | create(@Body() createPostDto: CreatePostDto) { 20 | return this.postService.create(createPostDto); 21 | } 22 | 23 | @Get() 24 | findAll() { 25 | return this.postService.findAll(); 26 | } 27 | 28 | @Get(':id') 29 | findOne(@Param('id') id: string) { 30 | return this.postService.findOne(id); 31 | } 32 | 33 | @Patch(':id') 34 | update(@Param('id') id: string, @Body() updatePostDto: UpdatePostDto) { 35 | return this.postService.update(+id, updatePostDto); 36 | } 37 | 38 | @Delete(':id') 39 | remove(@Param('id') id: string) { 40 | return this.postService.remove(id); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /tests/src/apps/app-multi-database/app/post/post.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { PostService } from './post.service'; 3 | import { PostController } from './post.controller'; 4 | 5 | @Module({ 6 | imports: [], 7 | controllers: [PostController], 8 | providers: [PostService], 9 | }) 10 | export class PostModule {} 11 | -------------------------------------------------------------------------------- /tests/src/apps/app-multi-database/app/post/post.service.ts: -------------------------------------------------------------------------------- 1 | import { 2 | BadRequestException, 3 | HttpException, 4 | HttpStatus, 5 | Injectable, 6 | } from '@nestjs/common'; 7 | import { Pool } from 'mysql2'; 8 | import { CreatePostDto } from './dto/create-post.dto'; 9 | import { UpdatePostDto } from './dto/update-post.dto'; 10 | import { NotFoundException } from '@nestjs/common'; 11 | import { InjectConnection } from '../../../../../../lib'; 12 | 13 | @Injectable() 14 | export class PostService { 15 | constructor( 16 | @InjectConnection('db2Connection') 17 | private dbConnection: Pool, 18 | ) {} 19 | 20 | public async findAll(): Promise { 21 | const posts = await this.dbConnection.query('SELECT * FROM posts'); 22 | const results = Object.assign([{}], posts[0]); 23 | 24 | return results; 25 | } 26 | 27 | public async findOne(id: string): Promise { 28 | if (!id) { 29 | throw new BadRequestException(); 30 | } 31 | 32 | const post = await this.dbConnection.query( 33 | 'SELECT * FROM posts WHERE id=?', 34 | [id], 35 | ); 36 | 37 | if (!post) { 38 | throw new NotFoundException(); 39 | } 40 | 41 | const result = Object.assign([{}], post[0]); 42 | 43 | return result; 44 | } 45 | 46 | public async create(createPostDto: CreatePostDto): Promise { 47 | try { 48 | const post = await this.dbConnection.query( 49 | 'INSERT INTO posts (title, description) VALUES (?, ?)', 50 | [createPostDto.title, createPostDto.description], 51 | ); 52 | return post; 53 | } catch (err) { 54 | throw new HttpException(err, HttpStatus.BAD_REQUEST); 55 | } 56 | } 57 | 58 | public async update(id: number, updateUserDto: UpdatePostDto): Promise { 59 | try { 60 | const { title, description } = updateUserDto; 61 | 62 | const post = await this.dbConnection.query( 63 | 'UPDATE posts SET title=?, descriptions=? WHERE id=?', 64 | [title, description, id], 65 | ); 66 | return post; 67 | } catch (err) { 68 | throw new HttpException(err, HttpStatus.BAD_REQUEST); 69 | } 70 | } 71 | 72 | public async remove(id: string): Promise { 73 | if (!id) { 74 | throw new BadRequestException(); 75 | } 76 | 77 | const post = await this.dbConnection.query('DELETE FROM posts WHERE id=?', [ 78 | id, 79 | ]); 80 | return post; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /tests/src/apps/app-multi-database/app/users/dto/create-user.dto.ts: -------------------------------------------------------------------------------- 1 | export class CreateUserDto { 2 | firstName: string; 3 | lastName: string; 4 | } 5 | -------------------------------------------------------------------------------- /tests/src/apps/app-multi-database/app/users/dto/update-user.dto.ts: -------------------------------------------------------------------------------- 1 | export class UpdateUserDto { 2 | firstName: string; 3 | lastName: string; 4 | } 5 | -------------------------------------------------------------------------------- /tests/src/apps/app-multi-database/app/users/interfaces/user.interface.ts: -------------------------------------------------------------------------------- 1 | export class User { 2 | id: number; 3 | firstName: string; 4 | lastName: string; 5 | } 6 | -------------------------------------------------------------------------------- /tests/src/apps/app-multi-database/app/users/users.controller.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Controller, 3 | Get, 4 | Post, 5 | Body, 6 | Put, 7 | Param, 8 | Delete, 9 | } from '@nestjs/common'; 10 | import { UsersService } from './users.service'; 11 | import { CreateUserDto } from './dto/create-user.dto'; 12 | import { UpdateUserDto } from './dto/update-user.dto'; 13 | 14 | @Controller('users') 15 | export class UsersController { 16 | constructor(private readonly usersService: UsersService) {} 17 | 18 | @Post() 19 | create(@Body() createUserDto: CreateUserDto) { 20 | return this.usersService.create(createUserDto); 21 | } 22 | 23 | @Get() 24 | findAll() { 25 | return this.usersService.findAll(); 26 | } 27 | 28 | @Get(':id') 29 | findOne(@Param('id') id: string) { 30 | return this.usersService.findOne(id); 31 | } 32 | 33 | @Put(':id') 34 | update(@Param('id') id: string, @Body() updateUserDto: UpdateUserDto) { 35 | return this.usersService.update(+id, updateUserDto); 36 | } 37 | 38 | @Delete(':id') 39 | remove(@Param('id') id: string) { 40 | return this.usersService.remove(id); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /tests/src/apps/app-multi-database/app/users/users.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { UsersService } from './users.service'; 3 | import { UsersController } from './users.controller'; 4 | 5 | @Module({ 6 | controllers: [UsersController], 7 | providers: [UsersService], 8 | }) 9 | export class UsersModule {} 10 | -------------------------------------------------------------------------------- /tests/src/apps/app-multi-database/app/users/users.service.ts: -------------------------------------------------------------------------------- 1 | import { 2 | BadRequestException, 3 | HttpException, 4 | HttpStatus, 5 | Injectable, 6 | NotFoundException, 7 | } from '@nestjs/common'; 8 | import { Pool } from 'mysql2'; 9 | import { InjectConnection } from '../../../../../../lib'; 10 | import { CreateUserDto } from './dto/create-user.dto'; 11 | import { UpdateUserDto } from './dto/update-user.dto'; 12 | 13 | @Injectable() 14 | export class UsersService { 15 | constructor( 16 | @InjectConnection('db1Connection') 17 | private dbConnection: Pool, 18 | ) {} 19 | 20 | public async findAll(): Promise { 21 | const users = await this.dbConnection.query('SELECT * FROM users'); 22 | const results = Object.assign([{}], users[0]); 23 | 24 | return results; 25 | } 26 | 27 | public async findOne(id: string): Promise { 28 | if (!id) { 29 | throw new BadRequestException(); 30 | } 31 | 32 | const user = await this.dbConnection.query( 33 | 'SELECT * FROM users WHERE id=?', 34 | [id], 35 | ); 36 | 37 | if (!user) { 38 | throw new NotFoundException(); 39 | } 40 | 41 | const result = Object.assign([{}], user[0]); 42 | 43 | return result; 44 | } 45 | 46 | public async create(createUserDto: CreateUserDto): Promise { 47 | try { 48 | const user = await this.dbConnection.query( 49 | 'INSERT INTO users (firstName, lastName) VALUES (?, ?)', 50 | [createUserDto.firstName, createUserDto.lastName], 51 | ); 52 | return user; 53 | } catch (err) { 54 | throw new HttpException(err, HttpStatus.BAD_REQUEST); 55 | } 56 | } 57 | 58 | public async update(id: number, updateUserDto: UpdateUserDto): Promise { 59 | try { 60 | const { firstName, lastName } = updateUserDto; 61 | 62 | const user = await this.dbConnection.query( 63 | 'UPDATE users SET firstName=?, lastName=? WHERE id=?', 64 | [firstName, lastName, id], 65 | ); 66 | return user; 67 | } catch (err) { 68 | throw new HttpException(err, HttpStatus.BAD_REQUEST); 69 | } 70 | } 71 | 72 | public async remove(id: string): Promise { 73 | if (!id) { 74 | throw new BadRequestException(); 75 | } 76 | 77 | const user = await this.dbConnection.query('DELETE FROM users WHERE id=?', [ 78 | id, 79 | ]); 80 | return user; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /tests/src/apps/app-multi-database/data.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE users ( 2 | id INT(11) AUTO_INCREMENT, 3 | firstName VARCHAR(100) NOT NULL, 4 | lastName VARCHAR(50) NOT NULL, 5 | PRIMARY KEY (id) 6 | ); 7 | 8 | CREATE TABLE posts ( 9 | id INT(11) AUTO_INCREMENT, 10 | title VARCHAR(100) NOT NULL, 11 | description VARCHAR(150) NOT NULL, 12 | PRIMARY KEY (id) 13 | ); 14 | -------------------------------------------------------------------------------- /tests/src/apps/app-multi-database/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | services: 4 | db1: 5 | image: mysql:8.0 6 | restart: always 7 | environment: 8 | MYSQL_ROOT_PASSWORD: root 9 | MYSQL_DATABASE: test1 10 | ports: 11 | - "3306:3306" 12 | db2: 13 | image: mysql:8.0 14 | restart: always 15 | environment: 16 | MYSQL_ROOT_PASSWORD: root 17 | MYSQL_DATABASE: test2 18 | ports: 19 | - "3307:3306" 20 | 21 | -------------------------------------------------------------------------------- /tests/src/apps/app-mysql/.env.example: -------------------------------------------------------------------------------- 1 | MYSQL_HOST="localhost" 2 | MYSQL_DB="test" 3 | MYSQL_PASSWORD="root" 4 | MYSQL_USER="root" 5 | MYSQL_PORT=3306 -------------------------------------------------------------------------------- /tests/src/apps/app-mysql/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { MysqlModule } from '../../../../../lib'; 3 | import { UsersModule } from './users/users.module'; 4 | import { ConfigModule, ConfigService } from '@nestjs/config'; 5 | 6 | @Module({ 7 | imports: [ 8 | MysqlModule.forRootAsync({ 9 | imports: [ConfigModule], 10 | inject: [ConfigService], 11 | useFactory: (config: ConfigService) => ({ 12 | host: config.get('MYSQL_HOST', 'localhost'), 13 | database: config.get('MYSQL_DB', 'test'), 14 | password: config.get('MYSQL_PASSWORD', 'root'), 15 | user: config.get('MYSQL_USER', 'root'), 16 | port: config.get('MYSQL_PORT', 3306), 17 | }), 18 | }), 19 | UsersModule, 20 | ], 21 | }) 22 | export class AppModule {} 23 | -------------------------------------------------------------------------------- /tests/src/apps/app-mysql/app/main.ts: -------------------------------------------------------------------------------- 1 | import { NestFactory } from '@nestjs/core'; 2 | import { AppModule } from './app.module'; 3 | import { 4 | NestFastifyApplication, 5 | FastifyAdapter, 6 | } from '@nestjs/platform-fastify'; 7 | 8 | async function bootstrap() { 9 | const app = await NestFactory.create( 10 | AppModule, 11 | new FastifyAdapter(), 12 | ); 13 | 14 | await app.listen(3000); 15 | } 16 | bootstrap(); 17 | -------------------------------------------------------------------------------- /tests/src/apps/app-mysql/app/users/dto/create-user.dto.ts: -------------------------------------------------------------------------------- 1 | export class CreateUserDto { 2 | firstName: string; 3 | lastName: string; 4 | } 5 | -------------------------------------------------------------------------------- /tests/src/apps/app-mysql/app/users/dto/update-user.dto.ts: -------------------------------------------------------------------------------- 1 | export class UpdateUserDto { 2 | firstName: string; 3 | lastName: string; 4 | } 5 | -------------------------------------------------------------------------------- /tests/src/apps/app-mysql/app/users/interfaces/user.interface.ts: -------------------------------------------------------------------------------- 1 | export interface User { 2 | id: number; 3 | firstName: string; 4 | lastName: string; 5 | } 6 | -------------------------------------------------------------------------------- /tests/src/apps/app-mysql/app/users/users.controller.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Controller, 3 | Get, 4 | Post, 5 | Body, 6 | Put, 7 | Param, 8 | Delete, 9 | } from '@nestjs/common'; 10 | import { UsersService } from './users.service'; 11 | import { CreateUserDto } from './dto/create-user.dto'; 12 | import { UpdateUserDto } from './dto/update-user.dto'; 13 | 14 | @Controller('users') 15 | export class UsersController { 16 | constructor(private readonly usersService: UsersService) {} 17 | 18 | @Post() 19 | create(@Body() createUserDto: CreateUserDto) { 20 | return this.usersService.create(createUserDto); 21 | } 22 | 23 | @Get() 24 | findAll() { 25 | return this.usersService.findAll(); 26 | } 27 | 28 | @Get(':id') 29 | findOne(@Param('id') id: string) { 30 | return this.usersService.findOne(id); 31 | } 32 | 33 | @Put(':id') 34 | update(@Param('id') id: string, @Body() updateUserDto: UpdateUserDto) { 35 | return this.usersService.update(+id, updateUserDto); 36 | } 37 | 38 | @Delete(':id') 39 | remove(@Param('id') id: string) { 40 | return this.usersService.remove(id); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /tests/src/apps/app-mysql/app/users/users.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { UsersService } from './users.service'; 3 | import { UsersController } from './users.controller'; 4 | 5 | @Module({ 6 | controllers: [UsersController], 7 | providers: [UsersService], 8 | }) 9 | export class UsersModule {} 10 | -------------------------------------------------------------------------------- /tests/src/apps/app-mysql/app/users/users.service.ts: -------------------------------------------------------------------------------- 1 | import { 2 | BadRequestException, 3 | HttpException, 4 | HttpStatus, 5 | Injectable, 6 | NotFoundException, 7 | } from '@nestjs/common'; 8 | import { InjectClient } from '../../../../../../lib'; 9 | import { CreateUserDto } from './dto/create-user.dto'; 10 | import { UpdateUserDto } from './dto/update-user.dto'; 11 | import { User } from './interfaces/user.interface'; 12 | import { Connection } from 'mysql2'; 13 | 14 | @Injectable() 15 | export class UsersService { 16 | constructor(@InjectClient() private readonly connection: Connection) {} 17 | 18 | public async findAll(): Promise { 19 | const users = await this.connection.query('SELECT * FROM users'); 20 | const results = Object.assign([{}], users[0]); 21 | 22 | return results; 23 | } 24 | 25 | public async findOne(id: string): Promise { 26 | if (!id) { 27 | throw new BadRequestException(); 28 | } 29 | 30 | const user = await this.connection.query('SELECT * FROM users WHERE id=?', [ 31 | id, 32 | ]); 33 | 34 | if (!user) { 35 | throw new NotFoundException(); 36 | } 37 | const result = Object.assign([{}], user[0]); 38 | 39 | return result; 40 | } 41 | 42 | public async create(createUserDto: CreateUserDto): Promise { 43 | try { 44 | const { firstName, lastName } = createUserDto; 45 | const user = await this.connection.query( 46 | 'INSERT INTO users (firstName, lastName) VALUES (?, ?)', 47 | [firstName, lastName], 48 | ); 49 | return user; 50 | } catch (err) { 51 | throw new HttpException(err, HttpStatus.BAD_REQUEST); 52 | } 53 | } 54 | 55 | public async update(id: number, updateUserDto: UpdateUserDto): Promise { 56 | try { 57 | const { firstName, lastName } = updateUserDto; 58 | 59 | const user = await this.connection.query( 60 | 'UPDATE users SET firstName=?, lastName=? WHERE id=?', 61 | [firstName, lastName, id], 62 | ); 63 | return user; 64 | } catch (err) { 65 | throw new HttpException(err, HttpStatus.BAD_REQUEST); 66 | } 67 | } 68 | 69 | public async remove(id: string): Promise { 70 | if (!id) { 71 | throw new BadRequestException(); 72 | } 73 | 74 | const user = await this.connection.query('DELETE FROM users WHERE id=?', [ 75 | id, 76 | ]); 77 | return user; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /tests/src/apps/app-mysql/data.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE users ( 2 | id INT(11) AUTO_INCREMENT, 3 | firstName VARCHAR(100) NOT NULL, 4 | lastName VARCHAR(50) NOT NULL, 5 | PRIMARY KEY (id) 6 | ); 7 | -------------------------------------------------------------------------------- /tests/src/apps/app-mysql/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | services: 4 | mysql: 5 | image: mysql:8.0 6 | restart: always 7 | environment: 8 | MYSQL_ROOT_USER: root 9 | MYSQL_ROOT_PASSWORD: root 10 | MYSQL_DATABASE: nest 11 | ports: 12 | - "3306:3306" 13 | -------------------------------------------------------------------------------- /tests/src/async-class-options.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { MysqlOptionsFactory, MysqlModuleOptions, MysqlModule } from '../../lib'; 3 | import { UsersModule } from './apps/app-mysql/app/users/users.module'; 4 | 5 | class ConfigService implements MysqlOptionsFactory { 6 | createMysqlOptions(): MysqlModuleOptions { 7 | return { 8 | host: '127.0.0.1', 9 | database: 'test', 10 | password: 'root', 11 | user: 'root', 12 | port: 3306, 13 | }; 14 | } 15 | } 16 | 17 | @Module({ 18 | imports: [ 19 | MysqlModule.forRootAsync({ 20 | useClass: ConfigService, 21 | }), 22 | MysqlModule.forRoot({ 23 | host: '127.0.0.1', 24 | database: 'test', 25 | password: 'root', 26 | user: 'root', 27 | port: 3306, 28 | }), 29 | UsersModule, 30 | ], 31 | }) 32 | export class AsyncOptionsClassModule {} 33 | -------------------------------------------------------------------------------- /tests/src/async-existing-options.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { UsersModule } from '../src/apps/app-mysql/app/users/users.module'; 3 | import { MysqlModule } from '../../lib/mysql.module'; 4 | import { MysqlModuleOptions, MysqlOptionsFactory } from '../../lib'; 5 | 6 | class ConfigService implements MysqlOptionsFactory { 7 | createMysqlOptions(): MysqlModuleOptions { 8 | return { 9 | host: '127.0.0.1', 10 | database: 'test', 11 | password: 'root', 12 | user: 'root', 13 | port: 3306, 14 | }; 15 | } 16 | } 17 | 18 | @Module({ 19 | providers: [ConfigService], 20 | exports: [ConfigService], 21 | }) 22 | class ConfigModule {} 23 | 24 | @Module({ 25 | imports: [ 26 | MysqlModule.forRootAsync({ 27 | imports: [ConfigModule], 28 | useExisting: ConfigService, 29 | }), 30 | MysqlModule.forRoot({ 31 | host: '127.0.0.1', 32 | database: 'test', 33 | password: 'root', 34 | user: 'root', 35 | port: 3306, 36 | }), 37 | UsersModule, 38 | ], 39 | }) 40 | export class AsyncOptionsExistingModule {} 41 | -------------------------------------------------------------------------------- /tests/src/database.module.ts: -------------------------------------------------------------------------------- 1 | import { DynamicModule, Module } from '@nestjs/common'; 2 | import { MysqlModule } from '../../lib'; 3 | 4 | @Module({}) 5 | export class DatabaseModule { 6 | static async forRoot(): Promise { 7 | await new Promise((resolve) => setTimeout(resolve, 1000)); 8 | return { 9 | module: DatabaseModule, 10 | imports: [ 11 | MysqlModule.forRootAsync( 12 | { 13 | useFactory: () => ({ 14 | name: 'db1Connection', 15 | host: '127.0.0.1', 16 | database: 'test', 17 | password: 'root', 18 | user: 'root', 19 | port: 3306, 20 | }), 21 | }, 22 | 'db1Connection', 23 | ), 24 | MysqlModule.forRootAsync( 25 | { 26 | useFactory: () => ({ 27 | name: 'db2Connection', 28 | host: '127.0.0.1', 29 | database: 'test', 30 | password: 'root', 31 | user: 'root', 32 | port: 3306, 33 | }), 34 | }, 35 | 'db2Connection', 36 | ), 37 | ], 38 | }; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /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": false, 6 | "emitDecoratorMetadata": true, 7 | "experimentalDecorators": true, 8 | "target": "ES2021", 9 | "sourceMap": false, 10 | "outDir": "./dist", 11 | "baseUrl": "./", 12 | "noLib": false, 13 | "noImplicitAny": false, 14 | "skipLibCheck": true 15 | }, 16 | "include": [ 17 | "lib/**/*" 18 | ], 19 | "exclude": [ 20 | "node_modules", 21 | "**/*.spec.ts", 22 | "tests", 23 | ] 24 | } --------------------------------------------------------------------------------