├── .eslintignore ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── Bug_report.yml │ ├── Feature_request.yml │ ├── Regression.yml │ └── config.yml ├── PULL_REQUEST_TEMPLATE.md └── workflows │ ├── build-samples.yml │ ├── e2e.yml │ ├── format.yml │ ├── lint.yml │ └── unit-test.yml ├── .gitignore ├── .husky ├── .gitignore ├── commit-msg └── pre-commit ├── .npmignore ├── .npmrc ├── .nvmrc ├── .prettierignore ├── .release-it.json ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── docker-compose.yml ├── e2e ├── graceful-shutdown.e2e-spec.ts ├── health-checks │ ├── disk.health.e2e-spec.ts │ ├── health-check.e2e-spec.ts │ ├── http.health.e2e-spec.ts │ ├── memory.health.e2e-spec.ts │ ├── microservice.health.e2e-spec.ts │ ├── mikro-orm.health.e2e-spec.ts │ ├── mongoose.health.e2e-spec.ts │ ├── prisma.health.e2e-spec.ts │ ├── sequelize.health.e2e-spec.ts │ └── typeorm.health.e2e-spec.ts ├── helper │ ├── bootstrap-microservice.ts │ ├── bootstrap-remote-server.ts │ ├── bootstrap-testing-module.ts │ └── index.ts ├── index.d.ts ├── jest-e2e.json ├── jest.setup.ts ├── prisma │ ├── schema-mongodb.prisma │ └── schema-mysql.prisma └── tsconfig.json ├── index.d.ts ├── index.js ├── index.ts ├── lib ├── PACKAGE.md ├── errors │ ├── axios.error.ts │ ├── connection-not-found.error.ts │ ├── database-not-connected.error.ts │ ├── index.ts │ ├── messages.constant.ts │ ├── mongo-connection.error.ts │ ├── storage-exceeded.error.ts │ ├── timeout-error.ts │ └── unhealthy-response-code.error.ts ├── graceful-shutdown-timeout │ ├── graceful-shutdown-timeout.service.spec.ts │ └── graceful-shutdown-timeout.service.ts ├── health-check │ ├── error-logger │ │ ├── error-logger.interface.ts │ │ ├── error-logger.provider.ts │ │ ├── error-loggers.provider.ts │ │ ├── json-error-logger.service.ts │ │ ├── pretty-error-logger.service.spec.ts │ │ └── pretty-error-logger.service.ts │ ├── health-check-executor.service.spec.ts │ ├── health-check-executor.service.ts │ ├── health-check-result.interface.ts │ ├── health-check.decorator.ts │ ├── health-check.error.ts │ ├── health-check.schema.ts │ ├── health-check.service.spec.ts │ ├── health-check.service.ts │ ├── index.ts │ └── logger │ │ └── logger.provider.ts ├── health-indicator │ ├── database │ │ ├── database-ping-check-settings.interface.ts │ │ ├── mikro-orm.health.ts │ │ ├── mongoose.health.ts │ │ ├── prisma.health.ts │ │ ├── sequelize.health.ts │ │ └── typeorm.health.ts │ ├── disk │ │ ├── disk-health-options.type.ts │ │ ├── disk-usage-lib.provider.ts │ │ ├── disk.health.ts │ │ └── index.ts │ ├── health-indicator-result.interface.ts │ ├── health-indicator.service.ts │ ├── health-indicator.ts │ ├── health-indicators.provider.ts │ ├── http │ │ ├── axios.interfaces.ts │ │ ├── http.health.spec.ts │ │ ├── http.health.ts │ │ └── index.ts │ ├── index.ts │ ├── memory │ │ ├── index.ts │ │ └── memory.health.ts │ └── microservice │ │ ├── grpc.health.spec.ts │ │ ├── grpc.health.ts │ │ ├── microservice.health.ts │ │ └── protos │ │ └── health.proto ├── index.ts ├── terminus-options.interface.ts ├── terminus.constants.ts ├── terminus.module.ts └── utils │ ├── checkPackage.util.ts │ ├── index.ts │ ├── is-error.ts │ ├── promise-timeout.ts │ ├── sleep.ts │ └── types.ts ├── nest-cli.json ├── package.json ├── pnpm-lock.yaml ├── pnpm-workspace.yaml ├── sample ├── 000-dogs-app │ ├── .eslintrc.js │ ├── .gitignore │ ├── nest-cli.json │ ├── package.json │ ├── src │ │ ├── app.module.ts │ │ ├── dog │ │ │ ├── dog.health.spec.ts │ │ │ ├── dog.health.ts │ │ │ ├── dog.module.ts │ │ │ ├── dog.service.ts │ │ │ └── interfaces │ │ │ │ └── dog.interface.ts │ │ ├── health │ │ │ ├── health.controller.ts │ │ │ └── health.module.ts │ │ └── main.ts │ ├── test │ │ ├── health.e2e-spec.ts │ │ └── jest-e2e.json │ └── tsconfig.json ├── 001-mongoose-app │ ├── .eslintrc.js │ ├── .gitignore │ ├── docker-compose.yml │ ├── package.json │ ├── src │ │ ├── app.module.ts │ │ ├── health │ │ │ ├── health.controller.ts │ │ │ └── health.module.ts │ │ └── main.ts │ └── tsconfig.json ├── 002-microservice-app │ ├── .eslintrc.js │ ├── .gitignore │ ├── docker-compose.yml │ ├── package.json │ ├── src │ │ ├── app.module.ts │ │ ├── health │ │ │ ├── health.controller.ts │ │ │ └── health.module.ts │ │ └── main.ts │ └── tsconfig.json ├── 003-memory-app │ ├── .eslintrc.js │ ├── .gitignore │ ├── package.json │ ├── src │ │ ├── app.module.ts │ │ ├── health │ │ │ ├── health.controller.ts │ │ │ └── health.module.ts │ │ └── main.ts │ └── tsconfig.json ├── 004-grpc-app │ ├── .eslintrc.js │ ├── .gitignore │ ├── package.json │ ├── src │ │ ├── client │ │ │ ├── app.module.ts │ │ │ ├── health │ │ │ │ ├── health.controller.ts │ │ │ │ └── health.module.ts │ │ │ └── main.ts │ │ ├── protos │ │ │ └── health.proto │ │ └── server │ │ │ ├── app.module.ts │ │ │ ├── health │ │ │ └── health.controller.ts │ │ │ └── main.ts │ └── tsconfig.json ├── 005-typeorm-app │ ├── .eslintrc.js │ ├── .gitignore │ ├── docker-compose.yml │ ├── package.json │ ├── src │ │ ├── app.module.ts │ │ ├── health │ │ │ ├── health.controller.ts │ │ │ └── health.module.ts │ │ └── main.ts │ └── tsconfig.json ├── 006-multi-db-typeorm-app │ ├── .eslintrc.js │ ├── .gitignore │ ├── docker-compose.yml │ ├── package.json │ ├── src │ │ ├── app.module.ts │ │ ├── health │ │ │ ├── health.controller.ts │ │ │ └── health.module.ts │ │ └── main.ts │ └── tsconfig.json ├── 007-sequelize-app │ ├── .eslintrc.js │ ├── .gitignore │ ├── docker-compose.yml │ ├── package.json │ ├── src │ │ ├── app.module.ts │ │ ├── health │ │ │ ├── health.controller.ts │ │ │ └── health.module.ts │ │ └── main.ts │ └── tsconfig.json ├── 008-multi-database-mongoose-app │ ├── .eslintrc.js │ ├── .gitignore │ ├── docker-compose.yml │ ├── package.json │ ├── src │ │ ├── app.module.ts │ │ ├── health │ │ │ ├── health.controller.ts │ │ │ └── health.module.ts │ │ └── main.ts │ └── tsconfig.json ├── 009-http-app │ ├── .eslintrc.js │ ├── .gitignore │ ├── package.json │ ├── src │ │ ├── app.module.ts │ │ ├── health │ │ │ ├── health.controller.ts │ │ │ └── health.module.ts │ │ └── main.ts │ └── tsconfig.json ├── 010-disk-app │ ├── .eslintrc.js │ ├── .gitignore │ ├── package.json │ ├── src │ │ ├── app.module.ts │ │ ├── health │ │ │ ├── health.controller.ts │ │ │ └── health.module.ts │ │ └── main.ts │ └── tsconfig.json ├── 011-mirkoorm-app │ ├── .eslintrc.js │ ├── .gitignore │ ├── docker-compose.yml │ ├── package.json │ ├── src │ │ ├── app.module.ts │ │ ├── health │ │ │ ├── health.controller.ts │ │ │ └── health.module.ts │ │ └── main.ts │ └── tsconfig.json ├── 012-prisma-app │ ├── .eslintrc.js │ ├── .gitignore │ ├── .prettierrc │ ├── docker-compose.yml │ ├── nest-cli.json │ ├── package.json │ ├── prisma │ │ └── schema.prisma │ ├── src │ │ ├── app.module.ts │ │ ├── health │ │ │ ├── health.controller.ts │ │ │ └── health.module.ts │ │ ├── main.ts │ │ └── prisma │ │ │ ├── prisma.module.ts │ │ │ └── prisma.service.ts │ ├── tsconfig.build.json │ └── tsconfig.json └── README.md ├── tools └── import-check.ts ├── tsconfig.base.json ├── tsconfig.build.json └── tsconfig.json /.eslintignore: -------------------------------------------------------------------------------- 1 | tests/** 2 | lib/**/*.spec.ts 3 | e2e/prisma/generated/** 4 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | open_collective: nest 4 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/Feature_request.yml: -------------------------------------------------------------------------------- 1 | name: "\U0001F680 Feature Request" 2 | description: "I have a suggestion \U0001F63B!" 3 | labels: ["type: feature"] 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: | 8 | ## :warning: We use GitHub Issues to track bug reports, feature requests and regressions 9 | 10 | If you are not sure that your issue is a bug, you could: 11 | 12 | - use our [Discord community](https://discord.gg/NestJS) 13 | - use [StackOverflow using the tag `nestjs`](https://stackoverflow.com/questions/tagged/nestjs) 14 | - If it's just a quick question you can ping [our Twitter](https://twitter.com/nestframework) 15 | 16 | --- 17 | 18 | - type: checkboxes 19 | attributes: 20 | label: "Is there an existing issue that is already proposing this?" 21 | description: "Please search [here](./?q=is%3Aissue) to see if an issue already exists for the feature you are requesting" 22 | options: 23 | - label: "I have searched the existing issues" 24 | required: true 25 | 26 | - type: textarea 27 | validations: 28 | required: true 29 | attributes: 30 | label: "Is your feature request related to a problem? Please describe it" 31 | description: "A clear and concise description of what the problem is" 32 | placeholder: | 33 | I have an issue when ... 34 | 35 | - type: textarea 36 | validations: 37 | required: true 38 | attributes: 39 | label: "Describe the solution you'd like" 40 | description: "A clear and concise description of what you want to happen. Add any considered drawbacks" 41 | 42 | - type: textarea 43 | validations: 44 | required: true 45 | attributes: 46 | label: "Teachability, documentation, adoption, migration strategy" 47 | description: "If you can, explain how users will be able to use this and possibly write out a version the docs. Maybe a screenshot or design?" 48 | 49 | - type: textarea 50 | validations: 51 | required: true 52 | attributes: 53 | label: "What is the motivation / use case for changing the behavior?" 54 | description: "Describe the motivation or the concrete use case" 55 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/Regression.yml: -------------------------------------------------------------------------------- 1 | name: "\U0001F4A5 Regression" 2 | description: "Report an unexpected behavior while upgrading your Nest application!" 3 | labels: ["type: bug"] 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: | 8 | ## :warning: We use GitHub Issues to track bug reports, feature requests and regressions 9 | 10 | If you are not sure that your issue is a bug, you could: 11 | 12 | - use our [Discord community](https://discord.gg/NestJS) 13 | - use [StackOverflow using the tag `nestjs`](https://stackoverflow.com/questions/tagged/nestjs) 14 | - If it's just a quick question you can ping [our Twitter](https://twitter.com/nestframework) 15 | 16 | **NOTE:** You don't need to answer questions that you know that aren't relevant. 17 | 18 | --- 19 | 20 | - type: checkboxes 21 | attributes: 22 | label: "Did you read the migration guide?" 23 | description: "Check out the [migration guide here](https://docs.nestjs.com/migration-guide)!" 24 | options: 25 | - label: "I have read the whole migration guide" 26 | required: false 27 | 28 | - type: checkboxes 29 | attributes: 30 | label: "Is there an existing issue that is already proposing this?" 31 | description: "Please search [here](./?q=is%3Aissue) to see if an issue already exists for the feature you are requesting" 32 | options: 33 | - label: "I have searched the existing issues" 34 | required: true 35 | 36 | - type: input 37 | attributes: 38 | label: "Potential Commit/PR that introduced the regression" 39 | description: "If you have time to investigate, what PR/date/version introduced this issue" 40 | placeholder: "PR #123 or commit 5b3c4a4" 41 | 42 | - type: input 43 | attributes: 44 | label: "Versions" 45 | description: "From which version of `@nestjs/terminus` to which version you are upgrading" 46 | placeholder: "8.1.0 -> 8.1.3" 47 | 48 | - type: textarea 49 | validations: 50 | required: true 51 | attributes: 52 | label: "Describe the regression" 53 | description: "A clear and concise description of what the regression is" 54 | 55 | - type: textarea 56 | attributes: 57 | label: "Minimum reproduction code" 58 | description: "Please share a git repo, a gist, or step-by-step instructions. [Wtf is a minimum reproduction?](https://jmcdo29.github.io/wtf-is-a-minimum-reproduction)" 59 | value: | 60 | ```ts 61 | 62 | ``` 63 | 64 | - type: textarea 65 | validations: 66 | required: true 67 | attributes: 68 | label: "Expected behavior" 69 | description: "A clear and concise description of what you expected to happend (or code)" 70 | 71 | - type: textarea 72 | attributes: 73 | label: "Other" 74 | description: | 75 | Anything else relevant? eg: Logs, OS version, IDE, package manager, etc. 76 | **Tip:** You can attach images, recordings or log files by clicking this area to highlight it and then dragging files in 77 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: true 2 | 3 | contact_links: 4 | - name: "\u2753 Discord Community of NestJS" 5 | url: "https://discord.gg/NestJS" 6 | about: "Please ask support questions here. Checkout the #terminus channel." 7 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## PR Checklist 2 | Please check if your PR fulfills the following requirements: 3 | 4 | - [ ] The commit message follows our guidelines: https://github.com/nestjs/terminus/blob/master/CONTRIBUTING.md 5 | - [ ] Tests for the changes have been added (for bug fixes / features) 6 | - [ ] Docs have been added / updated (for bug fixes / features) 7 | 8 | 9 | ## PR Type 10 | What kind of change does this PR introduce? 11 | 12 | 13 | - [ ] Bugfix 14 | - [ ] Feature 15 | - [ ] Code style update (formatting, local variables) 16 | - [ ] Refactoring (no functional changes, no api changes) 17 | - [ ] Build related changes 18 | - [ ] CI related changes 19 | - [ ] Other... Please describe: 20 | 21 | ## What is the current behavior? 22 | 23 | 24 | Issue Number: N/A 25 | 26 | 27 | ## What is the new behavior? 28 | 29 | 30 | ## Does this PR introduce a breaking change? 31 | - [ ] Yes 32 | - [ ] No 33 | 34 | 35 | 36 | 37 | ## Other information 38 | -------------------------------------------------------------------------------- /.github/workflows/build-samples.yml: -------------------------------------------------------------------------------- 1 | name: Build Samples 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | strategy: 11 | matrix: 12 | node-version: [18.x, 20.x, 22.x] 13 | 14 | steps: 15 | - uses: actions/checkout@v4 16 | - name: Install pnpm 17 | uses: pnpm/action-setup@v4 18 | - name: Use Node.js ${{ matrix.node-version }} 19 | uses: actions/setup-node@v4 20 | with: 21 | node-version: ${{ matrix.node-version }} 22 | cache: 'pnpm' 23 | 24 | - run: pnpm i 25 | - run: pnpm build:all 26 | -------------------------------------------------------------------------------- /.github/workflows/e2e.yml: -------------------------------------------------------------------------------- 1 | name: e2e tests 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | strategy: 11 | matrix: 12 | node-version: [18.x, 20.x, 22.x] 13 | 14 | steps: 15 | - uses: actions/checkout@v4 16 | - name: Install pnpm 17 | uses: pnpm/action-setup@v4 18 | - name: Use Node.js ${{ matrix.node-version }} 19 | uses: actions/setup-node@v4 20 | with: 21 | node-version: ${{ matrix.node-version }} 22 | cache: 'pnpm' 23 | 24 | - run: docker compose up -d 25 | - run: pnpm i 26 | - run: pnpm build:all 27 | - run: pnpm test:e2e:all -------------------------------------------------------------------------------- /.github/workflows/format.yml: -------------------------------------------------------------------------------- 1 | name: Format 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-24.04 9 | strategy: 10 | matrix: 11 | node-version: [20] 12 | 13 | steps: 14 | - uses: actions/checkout@v4 15 | - name: Install pnpm 16 | uses: pnpm/action-setup@v4 17 | - name: Use Node.js ${{ matrix.node-version }} 18 | uses: actions/setup-node@v4 19 | with: 20 | node-version: ${{ matrix.node-version }} 21 | cache: 'pnpm' 22 | 23 | - run: pnpm i 24 | - run: pnpm format -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: Format 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-24.04 9 | strategy: 10 | matrix: 11 | node-version: [20] 12 | 13 | steps: 14 | - uses: actions/checkout@v4 15 | - name: Install pnpm 16 | uses: pnpm/action-setup@v4 17 | - name: Use Node.js ${{ matrix.node-version }} 18 | uses: actions/setup-node@v4 19 | with: 20 | node-version: ${{ matrix.node-version }} 21 | cache: 'pnpm' 22 | 23 | - run: pnpm i 24 | - run: pnpm lint -------------------------------------------------------------------------------- /.github/workflows/unit-test.yml: -------------------------------------------------------------------------------- 1 | name: Unit Tests 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | strategy: 11 | matrix: 12 | node-version: [18.x, 20.x, 22.x] 13 | 14 | steps: 15 | - uses: actions/checkout@v4 16 | - name: Install pnpm 17 | uses: pnpm/action-setup@v4 18 | - name: Use Node.js ${{ matrix.node-version }} 19 | uses: actions/setup-node@v4 20 | with: 21 | node-version: ${{ matrix.node-version }} 22 | cache: 'pnpm' 23 | 24 | 25 | - run: pnpm i 26 | - run: pnpm build:all 27 | - run: pnpm test:all 28 | env: 29 | CI: true -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # dependencies 2 | node_modules 3 | 4 | # IDE 5 | /.idea 6 | /.awcache 7 | /.vscode 8 | 9 | # misc 10 | npm-debug.log 11 | .DS_Store 12 | 13 | # tests 14 | /test 15 | /coverage 16 | /.nyc_output 17 | e2e/prisma/generated 18 | 19 | # dist 20 | dist 21 | 22 | documentation 23 | -------------------------------------------------------------------------------- /.husky/.gitignore: -------------------------------------------------------------------------------- 1 | _ -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | npx --no-install commitlint --edit $1 -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | npx lint-staged 2 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # source 2 | lib 3 | index.ts 4 | package-lock.json 5 | .eslintrc.js 6 | tsconfig.json 7 | .prettierrc 8 | documentation 9 | sample 10 | .github 11 | e2e 12 | tools 13 | jest.json 14 | renovate.json 15 | LICENSE 16 | Gulpfile.js 17 | Dockerfile 18 | CONTRIBUTING.md 19 | .release-it.json 20 | .commitlintrc.json 21 | .eslintignore 22 | docker-compose.yml 23 | .nvmrc 24 | .husky 25 | coverage -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | link-workspace-packages=true -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 22.14.0 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | e2e/prisma/generated/ 2 | -------------------------------------------------------------------------------- /.release-it.json: -------------------------------------------------------------------------------- 1 | { 2 | "git": { 3 | "commitMessage": "chore(): release v${version}" 4 | }, 5 | "github": { 6 | "release": true 7 | }, 8 | "plugins": { 9 | "@release-it/conventional-changelog": { 10 | "preset": "angular", 11 | "infile": "CHANGELOG.md" 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | (The MIT License) 2 | 3 | Copyright (c) 2018-2025 Livio Brunner, Kamil Myśliwiec 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | 'Software'), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | mysql: 3 | image: mysql:9 4 | environment: 5 | MYSQL_ROOT_PASSWORD: root 6 | MYSQL_DATABASE: test 7 | ports: 8 | - "3306:3306" 9 | networks: 10 | - overlay 11 | 12 | mongodb: 13 | image: mongo:latest 14 | hostname: mongodb 15 | environment: 16 | - MONGODB_DATABASE="test" 17 | networks: 18 | - overlay 19 | ports: 20 | - 27017:27017 21 | 22 | rabbitmq: 23 | image: rabbitmq:4-management-alpine 24 | container_name: 'rabbitmq' 25 | networks: 26 | - overlay 27 | ports: 28 | - 5672:5672 29 | - 15672:15672 30 | 31 | redis: 32 | image: redis:latest 33 | networks: 34 | - overlay 35 | ports: 36 | - 6379:6379 37 | 38 | kafka_zookeeper: 39 | image: confluentinc/cp-zookeeper:7.9.0 40 | container_name: kafka_zookeeper 41 | environment: 42 | ZOOKEEPER_CLIENT_PORT: 2181 43 | ZOOKEEPER_TICK_TIME: 2000 44 | 45 | kafka_broker: 46 | image: confluentinc/cp-kafka:7.9.0 47 | container_name: broker 48 | ports: 49 | - "9092:9092" 50 | depends_on: 51 | - kafka_zookeeper 52 | environment: 53 | KAFKA_BROKER_ID: 1 54 | KAFKA_ZOOKEEPER_CONNECT: 'kafka_zookeeper:2181' 55 | KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_INTERNAL:PLAINTEXT 56 | KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://localhost:9092,PLAINTEXT_INTERNAL://kafka_broker:29092 57 | KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1 58 | KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 1 59 | KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 1 60 | networks: 61 | overlay: 62 | -------------------------------------------------------------------------------- /e2e/graceful-shutdown.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { ShutdownSignal } from '@nestjs/common'; 2 | import { type NestApplicationContext } from '@nestjs/core'; 3 | import * as request from 'supertest'; 4 | import { bootstrapTestingModule } from './helper'; 5 | import { sleep } from '../lib/utils'; 6 | 7 | describe('Graceful shutdown', () => { 8 | afterEach(() => { 9 | jest.clearAllMocks(); 10 | }); 11 | 12 | it('should gracefully shutdown the application', async () => { 13 | jest.spyOn(global, 'setTimeout'); 14 | const setHealthEndpoint = bootstrapTestingModule({ 15 | gracefulShutdownTimeoutMs: 64, 16 | }).setHealthEndpoint; 17 | 18 | const app = await setHealthEndpoint(({ healthCheck }) => 19 | healthCheck.check([]), 20 | ).start(); 21 | 22 | const { status } = await request(app.getHttpServer()).get('/health'); 23 | 24 | expect(status).toBe(200); 25 | 26 | let isClosed = false; 27 | (app.close as NestApplicationContext['close'])(ShutdownSignal.SIGTERM).then( 28 | () => { 29 | isClosed = true; 30 | }, 31 | ); 32 | 33 | await sleep(16); 34 | // 1. setTimeout is called by the `GracefulShutdownService` 35 | // 2. setTimeout is called above 36 | expect(setTimeout).toHaveBeenCalledTimes(2); 37 | expect(isClosed).toBe(false); 38 | await sleep(16); 39 | expect(isClosed).toBe(false); 40 | await sleep(16); 41 | expect(isClosed).toBe(false); 42 | await sleep(64); 43 | expect(isClosed).toBe(true); 44 | }); 45 | 46 | it('should not delay the shutdown if the application if the timeout is 0', async () => { 47 | jest.spyOn(global, 'setTimeout'); 48 | const setHealthEndpoint = bootstrapTestingModule({ 49 | gracefulShutdownTimeoutMs: 0, 50 | }).setHealthEndpoint; 51 | 52 | const app = await setHealthEndpoint(({ healthCheck }) => 53 | healthCheck.check([]), 54 | ).start(); 55 | 56 | const { status } = await request(app.getHttpServer()).get('/health'); 57 | 58 | expect(status).toBe(200); 59 | 60 | await (app.close as NestApplicationContext['close'])( 61 | ShutdownSignal.SIGTERM, 62 | ); 63 | 64 | expect(setTimeout).not.toHaveBeenCalled(); 65 | }); 66 | }); 67 | -------------------------------------------------------------------------------- /e2e/health-checks/health-check.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { type INestApplication } from '@nestjs/common'; 2 | import * as request from 'supertest'; 3 | import { type HealthIndicatorResult } from '../../lib'; 4 | import { 5 | type DynamicHealthEndpointFn, 6 | bootstrapTestingModule, 7 | } from '../helper'; 8 | 9 | describe.only('HealthCheck', () => { 10 | let app: INestApplication; 11 | let setHealthEndpoint: DynamicHealthEndpointFn; 12 | 13 | const healthyCheck = () => 14 | Promise.resolve({ status: 'up' } as any); 15 | 16 | beforeEach( 17 | () => (setHealthEndpoint = bootstrapTestingModule().setHealthEndpoint), 18 | ); 19 | 20 | it('should set the Cache-Control header to no-cache, no-store, must-revalidate', async () => { 21 | app = await setHealthEndpoint(({ healthCheck }) => 22 | healthCheck.check([healthyCheck]), 23 | ).start(); 24 | 25 | return request(app.getHttpServer()) 26 | .get('/health') 27 | .expect('Cache-Control', 'no-cache, no-store, must-revalidate'); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /e2e/health-checks/memory.health.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { type INestApplication } from '@nestjs/common'; 2 | import * as request from 'supertest'; 3 | import { 4 | bootstrapTestingModule, 5 | type DynamicHealthEndpointFn, 6 | } from '../helper'; 7 | 8 | describe('MemoryHealthIndicator', () => { 9 | let app: INestApplication; 10 | let setHealthEndpoint: DynamicHealthEndpointFn; 11 | 12 | beforeEach( 13 | () => (setHealthEndpoint = bootstrapTestingModule().setHealthEndpoint), 14 | ); 15 | 16 | describe('#checkHeap', () => { 17 | it('should check if the rss threshold is not exceeded', async () => { 18 | app = await setHealthEndpoint(({ healthCheck, memory }) => 19 | healthCheck.check([ 20 | async () => { 21 | const { rss } = process.memoryUsage(); 22 | return memory.checkHeap('memory_rss', rss + 1); 23 | }, 24 | ]), 25 | ).start(); 26 | 27 | const details = { memory_rss: { status: 'up' } }; 28 | 29 | return request(app.getHttpServer()).get('/health').expect(200).expect({ 30 | status: 'ok', 31 | info: details, 32 | error: {}, 33 | details, 34 | }); 35 | }); 36 | 37 | it('should check if the heap threshold is not exceeded', async () => { 38 | app = await setHealthEndpoint(({ healthCheck, memory }) => 39 | healthCheck.check([ 40 | async () => 41 | memory.checkHeap('memory_heap', 1 * 1024 * 1024 * 1024 * 1024), 42 | ]), 43 | ).start(); 44 | const details = { memory_heap: { status: 'up' } }; 45 | return request(app.getHttpServer()).get('/health').expect(200).expect({ 46 | status: 'ok', 47 | info: details, 48 | error: {}, 49 | details, 50 | }); 51 | }); 52 | 53 | it('should check if correctly displays a heap exceeded error', async () => { 54 | app = await setHealthEndpoint(({ healthCheck, memory }) => 55 | healthCheck.check([async () => memory.checkHeap('memory_heap', 0)]), 56 | ).start(); 57 | 58 | const details = { 59 | memory_heap: { 60 | status: 'down', 61 | message: 'Used heap exceeded the set threshold', 62 | }, 63 | }; 64 | 65 | return request(app.getHttpServer()).get('/health').expect(503).expect({ 66 | status: 'error', 67 | info: {}, 68 | error: details, 69 | details, 70 | }); 71 | }); 72 | }); 73 | 74 | afterEach(async () => await app.close()); 75 | }); 76 | -------------------------------------------------------------------------------- /e2e/health-checks/mongoose.health.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { type INestApplication } from '@nestjs/common'; 2 | import * as request from 'supertest'; 3 | import { 4 | bootstrapTestingModule, 5 | type DynamicHealthEndpointFn, 6 | } from '../helper'; 7 | 8 | describe('MongooseHealthIndicator', () => { 9 | let app: INestApplication; 10 | let setHealthEndpoint: DynamicHealthEndpointFn; 11 | 12 | beforeEach( 13 | () => 14 | (setHealthEndpoint = 15 | bootstrapTestingModule().withMongoose().setHealthEndpoint), 16 | ); 17 | 18 | describe('#pingCheck', () => { 19 | it('should check if the mongodb is available', async () => { 20 | app = await setHealthEndpoint(({ healthCheck, mongoose }) => 21 | healthCheck.check([async () => mongoose.pingCheck('mongo')]), 22 | ).start(); 23 | const details = { mongo: { status: 'up' } }; 24 | return request(app.getHttpServer()).get('/health').expect(200).expect({ 25 | status: 'ok', 26 | info: details, 27 | error: {}, 28 | details, 29 | }); 30 | }); 31 | }); 32 | 33 | afterEach(async () => await app.close()); 34 | }); 35 | -------------------------------------------------------------------------------- /e2e/health-checks/sequelize.health.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { type INestApplication } from '@nestjs/common'; 2 | import * as request from 'supertest'; 3 | import { 4 | bootstrapTestingModule, 5 | type DynamicHealthEndpointFn, 6 | } from '../helper'; 7 | 8 | describe('SequelizeHealthIndicator', () => { 9 | let app: INestApplication; 10 | let setHealthEndpoint: DynamicHealthEndpointFn; 11 | 12 | beforeEach( 13 | () => 14 | (setHealthEndpoint = 15 | bootstrapTestingModule().withSequelize().setHealthEndpoint), 16 | ); 17 | 18 | describe('#pingCheck', () => { 19 | it('should check if the sequelize is available', async () => { 20 | app = await setHealthEndpoint(({ healthCheck, sequelize }) => 21 | healthCheck.check([async () => sequelize.pingCheck('sequelize')]), 22 | ).start(); 23 | const details = { sequelize: { status: 'up' } }; 24 | return request(app.getHttpServer()).get('/health').expect(200).expect({ 25 | status: 'ok', 26 | info: details, 27 | error: {}, 28 | details, 29 | }); 30 | }); 31 | 32 | it('should throw an error if runs into timeout error', async () => { 33 | app = await setHealthEndpoint(({ healthCheck, sequelize }) => 34 | healthCheck.check([ 35 | async () => sequelize.pingCheck('sequelize', { timeout: 1 }), 36 | ]), 37 | ).start(); 38 | 39 | const details = { 40 | sequelize: { 41 | status: 'down', 42 | message: 'timeout of 1ms exceeded', 43 | }, 44 | }; 45 | 46 | return request(app.getHttpServer()).get('/health').expect(503).expect({ 47 | status: 'error', 48 | info: {}, 49 | error: details, 50 | details, 51 | }); 52 | }); 53 | }); 54 | 55 | afterEach(async () => await app.close()); 56 | }); 57 | -------------------------------------------------------------------------------- /e2e/health-checks/typeorm.health.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { type INestApplication } from '@nestjs/common'; 2 | import * as request from 'supertest'; 3 | import { 4 | bootstrapTestingModule, 5 | type DynamicHealthEndpointFn, 6 | } from '../helper'; 7 | 8 | describe('TypeOrmHealthIndicator', () => { 9 | let app: INestApplication; 10 | let setHealthEndpoint: DynamicHealthEndpointFn; 11 | 12 | beforeEach( 13 | () => 14 | (setHealthEndpoint = 15 | bootstrapTestingModule().withTypeOrm().setHealthEndpoint), 16 | ); 17 | 18 | describe('#pingCheck', () => { 19 | it('should check if the typeorm is available', async () => { 20 | app = await setHealthEndpoint(({ healthCheck, typeorm }) => 21 | healthCheck.check([async () => typeorm.pingCheck('typeorm')]), 22 | ).start(); 23 | 24 | const details = { typeorm: { status: 'up' } }; 25 | return request(app.getHttpServer()).get('/health').expect(200).expect({ 26 | status: 'ok', 27 | info: details, 28 | error: {}, 29 | details, 30 | }); 31 | }); 32 | 33 | // FIXME: Find a better way to test timeout errors 34 | // This test has been disabled because it is flaky 35 | // it('should throw an error if runs into timeout error', async () => { 36 | // app = await setHealthEndpoint(({ healthCheck, typeorm }) => 37 | // healthCheck.check([ 38 | // async () => typeorm.pingCheck('typeorm', { timeout: 1 }), 39 | // ]), 40 | // ).start(); 41 | 42 | // const details = { 43 | // typeorm: { 44 | // status: 'down', 45 | // message: 'timeout of 1ms exceeded', 46 | // }, 47 | // }; 48 | 49 | // return request(app.getHttpServer()).get('/health').expect(503).expect({ 50 | // status: 'error', 51 | // info: {}, 52 | // error: details, 53 | // details, 54 | // }); 55 | // }); 56 | }); 57 | 58 | afterEach(async () => await app.close()); 59 | }); 60 | -------------------------------------------------------------------------------- /e2e/helper/bootstrap-microservice.ts: -------------------------------------------------------------------------------- 1 | import { type INestMicroservice, Module } from '@nestjs/common'; 2 | import { NestFactory } from '@nestjs/core'; 3 | import { Transport } from '@nestjs/microservices'; 4 | import * as waitPort from 'wait-port'; 5 | 6 | @Module({}) 7 | class ApplicationModule {} 8 | 9 | export async function bootstrapMicroservice(): Promise { 10 | const app = await NestFactory.createMicroservice(ApplicationModule, { 11 | transport: Transport.TCP, 12 | options: { 13 | host: '0.0.0.0', 14 | port: 8889, 15 | }, 16 | }); 17 | 18 | await app.listen(); 19 | await waitPort({ host: '0.0.0.0', port: 8889 }); 20 | return app; 21 | } 22 | -------------------------------------------------------------------------------- /e2e/helper/bootstrap-remote-server.ts: -------------------------------------------------------------------------------- 1 | import { type Server } from 'http'; 2 | import * as express from 'express'; 3 | import * as portfinder from 'portfinder'; 4 | 5 | type ThenArg = T extends PromiseLike ? U : T; 6 | export type DynamicRemoteServerFn = ThenArg< 7 | ReturnType 8 | >; 9 | 10 | export async function bootstrapRemoteServer(port?: number) { 11 | const app = express(); 12 | let server: Server; 13 | 14 | if (!port) { 15 | port = await portfinder.getPortPromise({ 16 | port: 3000, 17 | stopPort: 8888, 18 | }); 19 | } 20 | 21 | function close() { 22 | server?.close?.(); 23 | } 24 | function get( 25 | endpoint: string, 26 | handler: (req: express.Request, res: express.Response) => void, 27 | ) { 28 | app.get(endpoint, handler); 29 | 30 | return { 31 | start: async () => { 32 | if (!server) { 33 | server = app.listen(port, '0.0.0.0'); 34 | } 35 | }, 36 | }; 37 | } 38 | 39 | return { 40 | get, 41 | close, 42 | url: `http://0.0.0.0:${port}`, 43 | }; 44 | } 45 | -------------------------------------------------------------------------------- /e2e/helper/index.ts: -------------------------------------------------------------------------------- 1 | export * from './bootstrap-testing-module'; 2 | export * from './bootstrap-remote-server'; 3 | export * from './bootstrap-microservice'; 4 | -------------------------------------------------------------------------------- /e2e/index.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*prisma/generated/mongodb' { 2 | class PrismaClient { 3 | $runCommandRaw(command: unknown): any; 4 | } 5 | } 6 | 7 | declare module '*prisma/generated/mysql' { 8 | class PrismaClient { 9 | $queryRawUnsafe(query: string): any; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /e2e/jest-e2e.json: -------------------------------------------------------------------------------- 1 | { 2 | "moduleFileExtensions": ["ts", "tsx", "js", "json"], 3 | "transform": { 4 | "^.+\\.tsx?$": "ts-jest" 5 | }, 6 | "setupFiles": ["/jest.setup.ts"], 7 | "testRegex": "/e2e/.*\\.(e2e-test|e2e-spec).(ts|tsx|js)$", 8 | "collectCoverageFrom": [ 9 | "src/**/*.{js,jsx,tsx,ts}", 10 | "!**/node_modules/**", 11 | "!**/vendor/**" 12 | ], 13 | "coverageReporters": ["json", "lcov"], 14 | "testEnvironment": "node" 15 | } 16 | -------------------------------------------------------------------------------- /e2e/jest.setup.ts: -------------------------------------------------------------------------------- 1 | import { execSync } from 'child_process'; 2 | 3 | execSync(`npx prisma@6 generate --schema e2e/prisma/schema-mysql.prisma`); 4 | execSync(`npx prisma@6 generate --schema e2e/prisma/schema-mongodb.prisma`); 5 | -------------------------------------------------------------------------------- /e2e/prisma/schema-mongodb.prisma: -------------------------------------------------------------------------------- 1 | // This is your Prisma schema file, 2 | // learn more about it in the docs: https://pris.ly/d/prisma-schema 3 | 4 | generator client { 5 | provider = "prisma-client-js" 6 | output = "./generated/mongodb" 7 | binaryTargets = ["native", "debian-openssl-1.1.x"] 8 | } 9 | 10 | datasource db { 11 | provider = "mongodb" 12 | url = "mongodb://0.0.0.0:27017/test" 13 | } 14 | 15 | model Test { 16 | id String @id @map("_id") @db.ObjectId 17 | } 18 | -------------------------------------------------------------------------------- /e2e/prisma/schema-mysql.prisma: -------------------------------------------------------------------------------- 1 | // This is your Prisma schema file, 2 | // learn more about it in the docs: https://pris.ly/d/prisma-schema 3 | 4 | generator client { 5 | provider = "prisma-client-js" 6 | output = "./generated/mysql" 7 | binaryTargets = ["native", "debian-openssl-1.1.x"] 8 | } 9 | 10 | datasource db { 11 | provider = "mysql" 12 | url = "mysql://root:root@0.0.0.0/test" 13 | } 14 | 15 | model Test { 16 | id Int @id @default(autoincrement()) 17 | } 18 | -------------------------------------------------------------------------------- /e2e/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "declaration": false, 5 | "noImplicitAny": false, 6 | "removeComments": true, 7 | "noLib": false, 8 | "emitDecoratorMetadata": true, 9 | "experimentalDecorators": true, 10 | "target": "es6", 11 | "sourceMap": true, 12 | "allowJs": true, 13 | "outDir": "./dist", 14 | }, 15 | "include": ["./**/*"], 16 | "exclude": ["node_modules"] 17 | } 18 | -------------------------------------------------------------------------------- /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")); -------------------------------------------------------------------------------- /index.ts: -------------------------------------------------------------------------------- 1 | export * from './dist'; 2 | -------------------------------------------------------------------------------- /lib/PACKAGE.md: -------------------------------------------------------------------------------- 1 | This module contains integrated healthchecks for [Nest](https://github.com/nestjs/nest). These healthchecks run in an internal execution manager based on the [Terminus](https://github.com/godaddy/terminus)-package. -------------------------------------------------------------------------------- /lib/errors/axios.error.ts: -------------------------------------------------------------------------------- 1 | export interface AxiosError extends Error { 2 | code?: string; 3 | request?: any; 4 | response?: any; 5 | isAxiosError: boolean; 6 | status?: string; 7 | toJSON: () => object; 8 | } 9 | -------------------------------------------------------------------------------- /lib/errors/connection-not-found.error.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable deprecation/deprecation */ 2 | import { CONNECTION_NOT_FOUND } from './messages.constant'; 3 | import { HealthCheckError } from '../health-check/health-check.error'; 4 | 5 | /** 6 | * Error which gets thrown when the connection 7 | * instance was not found in the application context 8 | * @publicApi 9 | * 10 | * @deprecated 11 | * This class has been deprecated and will be removed in the next major release. 12 | * Instead utilise the `HealthIndicatorService` to indicate the health of your health indicator. 13 | * 14 | */ 15 | export class ConnectionNotFoundError extends HealthCheckError { 16 | /** 17 | * Initializes the error 18 | * @param {any} cause The cause of the health check error 19 | * 20 | * @internal 21 | */ 22 | constructor(cause: any) { 23 | super(CONNECTION_NOT_FOUND, cause); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lib/errors/database-not-connected.error.ts: -------------------------------------------------------------------------------- 1 | import { DATABASE_NOT_CONNECTED } from './messages.constant'; 2 | 3 | /** 4 | * Error which gets thrown when the database is not connected 5 | * @publicApi 6 | */ 7 | export class DatabaseNotConnectedError extends Error { 8 | constructor() { 9 | super(DATABASE_NOT_CONNECTED); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /lib/errors/index.ts: -------------------------------------------------------------------------------- 1 | export * from './connection-not-found.error'; 2 | export * from './timeout-error'; 3 | export * from './storage-exceeded.error'; 4 | export * from './unhealthy-response-code.error'; 5 | export * from './mongo-connection.error'; 6 | -------------------------------------------------------------------------------- /lib/errors/messages.constant.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @internal 3 | */ 4 | export const CONNECTION_NOT_FOUND = 5 | 'Connection provider not found in application context'; 6 | 7 | /** 8 | * @internal 9 | */ 10 | export const TIMEOUT_EXCEEDED = (timeout: number) => 11 | `Timeout of ${timeout.toString()}ms exceeded`; 12 | 13 | /** 14 | * @internal 15 | */ 16 | export const STORAGE_EXCEEDED = (keyword: string) => 17 | `Used ${keyword} exceeded the set threshold`; 18 | 19 | /** 20 | * @internal 21 | */ 22 | export const UNHEALTHY_RESPONSE_CODE = (responseCode: string | number) => 23 | `The service returned an unhealthy response code: ${responseCode}`; 24 | 25 | /** 26 | * @internal 27 | */ 28 | export const DATABASE_NOT_CONNECTED = `Not connected to database`; 29 | -------------------------------------------------------------------------------- /lib/errors/mongo-connection.error.ts: -------------------------------------------------------------------------------- 1 | export class MongoConnectionError extends Error {} 2 | -------------------------------------------------------------------------------- /lib/errors/storage-exceeded.error.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable deprecation/deprecation */ 2 | import { STORAGE_EXCEEDED } from './messages.constant'; 3 | import { HealthCheckError } from '../health-check/health-check.error'; 4 | 5 | /** 6 | * Error which gets thrown when the given storage threshold 7 | * has exceeded. 8 | * @publicApi 9 | * 10 | * @deprecated 11 | * This class has been deprecated and will be removed in the next major release. 12 | * Instead utilise the `HealthIndicatorService` to indicate the health of your health indicator. 13 | */ 14 | export class StorageExceededError extends HealthCheckError { 15 | /** 16 | * Initializes the error 17 | * 18 | * @param {string} keyword The keyword (heap, rss, disk e.g.) 19 | * @param {any} cause The cause of the health check error 20 | * 21 | * @internal 22 | */ 23 | constructor(keyword: string, cause: any) { 24 | super(STORAGE_EXCEEDED(keyword), cause); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lib/errors/timeout-error.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable deprecation/deprecation */ 2 | import { TIMEOUT_EXCEEDED } from './messages.constant'; 3 | import { HealthCheckError } from '../health-check/health-check.error'; 4 | 5 | /** 6 | * Gets thrown when the timeout of the health check exceeds 7 | * @publicApi 8 | * 9 | * @deprecated 10 | * This class has been deprecated and will be removed in the next major release. 11 | * Instead utilise the `HealthIndicatorService` to indicate the health of your health indicator. 12 | */ 13 | export class TimeoutError extends HealthCheckError { 14 | /** 15 | * Initializes the error 16 | * @param {number} timeout The given timeout in ms 17 | * @param {any} cause The cause of the health check error 18 | * 19 | * @internal 20 | */ 21 | constructor(timeout: number, cause: any) { 22 | super(TIMEOUT_EXCEEDED(timeout), cause); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lib/errors/unhealthy-response-code.error.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable deprecation/deprecation */ 2 | import { UNHEALTHY_RESPONSE_CODE } from './messages.constant'; 3 | import { HealthCheckError } from '../health-check/health-check.error'; 4 | 5 | /** 6 | * Error which gets thrown when the terminus client receives 7 | * an unhealthy response code from the server. 8 | * @publicApi 9 | * 10 | * @deprecated 11 | * This class has been deprecated and will be removed in the next major release. 12 | * Instead utilise the `HealthIndicatorService` to indicate the health of your health indicator. 13 | */ 14 | export class UnhealthyResponseCodeError extends HealthCheckError { 15 | /** 16 | * Initializes the error 17 | * 18 | * @param {string | number} responseCode The response code 19 | * @param {any} cause The cause of the health check error 20 | * 21 | * @internal 22 | */ 23 | constructor(responseCode: string, cause: any) { 24 | super(UNHEALTHY_RESPONSE_CODE(responseCode), cause); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lib/graceful-shutdown-timeout/graceful-shutdown-timeout.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { Test } from '@nestjs/testing'; 2 | import { LoggerService } from '@nestjs/common'; 3 | import { 4 | GracefulShutdownService, 5 | TERMINUS_GRACEFUL_SHUTDOWN_TIMEOUT, 6 | } from './graceful-shutdown-timeout.service'; 7 | import { TERMINUS_LOGGER } from '../health-check/logger/logger.provider'; 8 | import { sleep } from '../utils'; 9 | 10 | jest.mock('../utils', () => ({ 11 | sleep: jest.fn(), 12 | })); 13 | 14 | const loggerMock: Partial = { 15 | log: jest.fn(), 16 | error: jest.fn(), 17 | warn: jest.fn(), 18 | debug: jest.fn(), 19 | }; 20 | 21 | describe('GracefulShutdownService', () => { 22 | let service: GracefulShutdownService; 23 | let logger: LoggerService; 24 | 25 | beforeEach(async () => { 26 | const module = await Test.createTestingModule({ 27 | providers: [ 28 | GracefulShutdownService, 29 | { provide: TERMINUS_LOGGER, useValue: loggerMock }, 30 | { provide: TERMINUS_GRACEFUL_SHUTDOWN_TIMEOUT, useValue: 1000 }, 31 | ], 32 | }).compile(); 33 | 34 | logger = module.get(TERMINUS_LOGGER); 35 | service = module.get(GracefulShutdownService); 36 | }); 37 | 38 | it('should not trigger sleep if signal is not SIGTERM', async () => { 39 | await service.beforeApplicationShutdown('SIGINT'); 40 | expect(sleep).not.toHaveBeenCalled(); 41 | }); 42 | 43 | it('should trigger sleep if signal is SIGTERM', async () => { 44 | await service.beforeApplicationShutdown('SIGTERM'); 45 | expect(sleep).toHaveBeenCalledWith(1000); 46 | }); 47 | }); 48 | -------------------------------------------------------------------------------- /lib/graceful-shutdown-timeout/graceful-shutdown-timeout.service.ts: -------------------------------------------------------------------------------- 1 | import { 2 | type BeforeApplicationShutdown, 3 | ConsoleLogger, 4 | Inject, 5 | Injectable, 6 | LoggerService, 7 | } from '@nestjs/common'; 8 | import { TERMINUS_LOGGER } from '../health-check/logger/logger.provider'; 9 | import { sleep } from '../utils'; 10 | 11 | export const TERMINUS_GRACEFUL_SHUTDOWN_TIMEOUT = 12 | 'TERMINUS_GRACEFUL_SHUTDOWN_TIMEOUT'; 13 | 14 | /** 15 | * Handles Graceful shutdown timeout useful to await 16 | * for some time before the application shuts down. 17 | */ 18 | @Injectable() 19 | export class GracefulShutdownService implements BeforeApplicationShutdown { 20 | constructor( 21 | @Inject(TERMINUS_LOGGER) 22 | private readonly logger: LoggerService, 23 | @Inject(TERMINUS_GRACEFUL_SHUTDOWN_TIMEOUT) 24 | private readonly gracefulShutdownTimeoutMs: number, 25 | ) { 26 | if (this.logger instanceof ConsoleLogger) { 27 | this.logger.setContext(GracefulShutdownService.name); 28 | } 29 | } 30 | 31 | async beforeApplicationShutdown(signal: string) { 32 | this.logger.log(`Received termination signal ${signal || ''}`); 33 | 34 | if (signal === 'SIGTERM') { 35 | this.logger.log( 36 | `Awaiting ${this.gracefulShutdownTimeoutMs}ms before shutdown`, 37 | ); 38 | await sleep(this.gracefulShutdownTimeoutMs); 39 | this.logger.log(`Timeout reached, shutting down now`); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /lib/health-check/error-logger/error-logger.interface.ts: -------------------------------------------------------------------------------- 1 | import { type HealthIndicatorResult } from '../../health-indicator'; 2 | 3 | export interface ErrorLogger { 4 | getErrorMessage(message: string, causes: HealthIndicatorResult): string; 5 | } 6 | -------------------------------------------------------------------------------- /lib/health-check/error-logger/error-logger.provider.ts: -------------------------------------------------------------------------------- 1 | import { type Provider } from '@nestjs/common'; 2 | import { type ErrorLogger } from './error-logger.interface'; 3 | import { JsonErrorLogger } from './json-error-logger.service'; 4 | import { PrettyErrorLogger } from './pretty-error-logger.service'; 5 | import { type ErrorLogStyle } from '../../terminus-options.interface'; 6 | 7 | export const ERROR_LOGGER = 'TERMINUS_ERROR_LOGGER'; 8 | 9 | export function getErrorLoggerProvider( 10 | errorLogStyle?: ErrorLogStyle, 11 | ): Provider { 12 | switch (errorLogStyle) { 13 | case 'pretty': 14 | return { 15 | provide: ERROR_LOGGER, 16 | useClass: PrettyErrorLogger, 17 | }; 18 | default: 19 | return { 20 | provide: ERROR_LOGGER, 21 | useClass: JsonErrorLogger, 22 | }; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lib/health-check/error-logger/error-loggers.provider.ts: -------------------------------------------------------------------------------- 1 | import { type Type } from '@nestjs/common'; 2 | import { type ErrorLogger } from './error-logger.interface'; 3 | import { JsonErrorLogger } from './json-error-logger.service'; 4 | import { PrettyErrorLogger } from './pretty-error-logger.service'; 5 | 6 | export const ERROR_LOGGERS: Type[] = [ 7 | JsonErrorLogger, 8 | PrettyErrorLogger, 9 | ]; 10 | -------------------------------------------------------------------------------- /lib/health-check/error-logger/json-error-logger.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common'; 2 | import { type ErrorLogger } from './error-logger.interface'; 3 | 4 | @Injectable() 5 | export class JsonErrorLogger implements ErrorLogger { 6 | getErrorMessage(message: string, causes: any) { 7 | return `${message} ${JSON.stringify(causes)}`; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /lib/health-check/error-logger/pretty-error-logger.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common'; 2 | import * as boxen from 'boxen'; 3 | import { type ErrorLogger } from './error-logger.interface'; 4 | import { type HealthIndicatorResult } from '../../health-indicator'; 5 | 6 | const GREEN = '\x1b[0m\x1b[32m'; 7 | const RED = '\x1b[0m\x1b[31m'; 8 | const STOP_COLOR = '\x1b[0m'; 9 | 10 | @Injectable() 11 | export class PrettyErrorLogger implements ErrorLogger { 12 | private printIndent(level: number) { 13 | if (level === 0) { 14 | return ''; 15 | } 16 | return `${' '.repeat(level * 2)}- `; 17 | } 18 | 19 | private printIndicatorSummary(result: any, level = 0) { 20 | const messages: string[] = []; 21 | for (const [key, value] of Object.entries(result)) { 22 | if (typeof value === 'object' && value !== null) { 23 | messages.push( 24 | `${this.printIndent(level)}${key}:\n${this.printIndicatorSummary( 25 | value, 26 | level + 1, 27 | )}`, 28 | ); 29 | } else { 30 | const val = (value as any)?.toString 31 | ? (value as any).toString() 32 | : value; 33 | messages.push(`${this.printIndent(level)}${key}: ${val}`); 34 | } 35 | } 36 | return messages.join('\n'); 37 | } 38 | 39 | private printSummary(result: HealthIndicatorResult) { 40 | let message = ''; 41 | 42 | for (const [key, value] of Object.entries(result)) { 43 | const summary = this.printIndicatorSummary(value); 44 | 45 | if (value.status === 'up') { 46 | message += 47 | GREEN + 48 | (boxen as any)(summary, { 49 | padding: 1, 50 | title: `✅ ${key}`, 51 | }) + 52 | STOP_COLOR + 53 | '\n'; 54 | } 55 | if (value.status === 'down') { 56 | message += 57 | RED + 58 | (boxen as any)(summary, { 59 | padding: 1, 60 | title: `❌ ${key}`, 61 | }) + 62 | STOP_COLOR + 63 | '\n'; 64 | } 65 | } 66 | return message; 67 | } 68 | 69 | getErrorMessage(message: string, causes: HealthIndicatorResult) { 70 | return `${message}\n\n${this.printSummary(causes)}`; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /lib/health-check/health-check-result.interface.ts: -------------------------------------------------------------------------------- 1 | import { type HealthIndicatorResult } from '../health-indicator'; 2 | 3 | /** 4 | * @publicApi 5 | */ 6 | export type HealthCheckStatus = 'error' | 'ok' | 'shutting_down'; 7 | 8 | /** 9 | * The result of a health check 10 | * @publicApi 11 | */ 12 | export interface HealthCheckResult { 13 | /** 14 | * The overall status of the Health Check 15 | */ 16 | status: HealthCheckStatus; 17 | /** 18 | * The info object contains information of each health indicator 19 | * which is of status "up" 20 | */ 21 | info?: HealthIndicatorResult; 22 | /** 23 | * The error object contains information of each health indicator 24 | * which is of status "down" 25 | */ 26 | error?: HealthIndicatorResult; 27 | /** 28 | * The details object contains information of every health indicator. 29 | */ 30 | details: HealthIndicatorResult; 31 | } 32 | -------------------------------------------------------------------------------- /lib/health-check/health-check.decorator.ts: -------------------------------------------------------------------------------- 1 | import { Header } from '@nestjs/common'; 2 | import { getHealthCheckSchema } from './health-check.schema'; 3 | 4 | // eslint-disable-next-line @typescript-eslint/consistent-type-imports 5 | type Swagger = typeof import('@nestjs/swagger'); 6 | 7 | /** 8 | * @publicApi 9 | */ 10 | export interface HealthCheckOptions { 11 | /** 12 | * Whether to cache the response or not. 13 | * - If set to `true`, the response header will be set to `Cache-Control: no-cache, no-store, must-revalidate`. 14 | * - If set to `false`, no header will be set and can be set manually with e.g. `@Header('Cache-Control', 'max-age=604800')`. 15 | * 16 | * @default true 17 | */ 18 | noCache?: boolean; 19 | /** 20 | * Whether to document the endpoint with Swagger or not. 21 | * 22 | * @default true 23 | */ 24 | swaggerDocumentation?: boolean; 25 | } 26 | 27 | /** 28 | * Marks the endpoint as a Health Check endpoint. 29 | * 30 | * - If the `@nestjs/swagger` package is installed, the endpoint will be documented. 31 | * - Per default, the response will not be cached. 32 | * 33 | * @publicApi 34 | */ 35 | export const HealthCheck = ( 36 | { noCache, swaggerDocumentation }: HealthCheckOptions = { 37 | noCache: true, 38 | swaggerDocumentation: true, 39 | }, 40 | ) => { 41 | const decorators: MethodDecorator[] = []; 42 | 43 | if (swaggerDocumentation) { 44 | let swagger: Swagger | null = null; 45 | try { 46 | swagger = require('@nestjs/swagger'); 47 | } catch {} 48 | 49 | if (swagger) { 50 | decorators.push(...getSwaggerDefinitions(swagger)); 51 | } 52 | } 53 | 54 | if (noCache) { 55 | const CacheControl = Header( 56 | 'Cache-Control', 57 | 'no-cache, no-store, must-revalidate', 58 | ); 59 | 60 | decorators.push(CacheControl); 61 | } 62 | 63 | return (target: any, key: any, descriptor: PropertyDescriptor) => { 64 | decorators.forEach((decorator) => { 65 | decorator(target, key, descriptor); 66 | }); 67 | }; 68 | }; 69 | 70 | function getSwaggerDefinitions(swagger: Swagger) { 71 | const { ApiOkResponse, ApiServiceUnavailableResponse } = swagger; 72 | 73 | const ServiceUnavailable = ApiServiceUnavailableResponse({ 74 | description: 'The Health Check is not successful', 75 | schema: getHealthCheckSchema('error'), 76 | }); 77 | 78 | const Ok = ApiOkResponse({ 79 | description: 'The Health Check is successful', 80 | schema: getHealthCheckSchema('ok'), 81 | }); 82 | 83 | return [ServiceUnavailable, Ok]; 84 | } 85 | -------------------------------------------------------------------------------- /lib/health-check/health-check.error.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @deprecated 3 | * **This class has been deprecated and will be removed in the next major release.** 4 | * Instead utilise the `HealthIndicatorService` to indicate the health of your health indicator. 5 | * 6 | * @see {@link https://docs.nestjs.com/migration-guide#terminus-module|Migration Guide} 7 | */ 8 | export class HealthCheckError extends Error { 9 | causes: any; 10 | isHealthCheckError = true; 11 | constructor(message: string, causes: any) { 12 | super(message); 13 | 14 | this.causes = causes; 15 | 16 | Error.captureStackTrace(this, this.constructor); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /lib/health-check/health-check.schema.ts: -------------------------------------------------------------------------------- 1 | import { type HealthCheckStatus } from './health-check-result.interface'; 2 | import { type HealthIndicatorResult } from '../health-indicator'; 3 | import type {} from '@nestjs/swagger'; 4 | 5 | // These examples will be displayed on Swagger 6 | const DB_EXAMPLE: HealthIndicatorResult = { database: { status: 'up' } }; 7 | const REDIS_EXAMPLE: HealthIndicatorResult = { 8 | redis: { status: 'down', message: 'Could not connect' }, 9 | }; 10 | const COMBINED_EXAMPLE: HealthIndicatorResult = { 11 | ...DB_EXAMPLE, 12 | ...REDIS_EXAMPLE, 13 | }; 14 | 15 | const healthIndicatorSchema = (example: HealthIndicatorResult) => ({ 16 | type: 'object', 17 | example, 18 | additionalProperties: { 19 | type: 'object', 20 | required: ['status'], 21 | properties: { 22 | status: { 23 | type: 'string', 24 | }, 25 | }, 26 | additionalProperties: true, 27 | }, 28 | }); 29 | 30 | export function getHealthCheckSchema(status: HealthCheckStatus) { 31 | return { 32 | type: 'object', 33 | properties: { 34 | status: { 35 | type: 'string', 36 | example: status, 37 | }, 38 | info: { 39 | ...healthIndicatorSchema(DB_EXAMPLE), 40 | nullable: true, 41 | }, 42 | error: { 43 | ...healthIndicatorSchema(status === 'error' ? REDIS_EXAMPLE : {}), 44 | nullable: true, 45 | }, 46 | details: healthIndicatorSchema( 47 | status === 'error' ? COMBINED_EXAMPLE : DB_EXAMPLE, 48 | ), 49 | }, 50 | }; 51 | } 52 | -------------------------------------------------------------------------------- /lib/health-check/health-check.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { Test } from '@nestjs/testing'; 2 | import { HealthCheckService } from './health-check.service'; 3 | import { HealthCheckExecutor } from './health-check-executor.service'; 4 | import { ERROR_LOGGER } from './error-logger/error-logger.provider'; 5 | import { ErrorLogger } from './error-logger/error-logger.interface'; 6 | import { TERMINUS_LOGGER } from './logger/logger.provider'; 7 | import { ConsoleLogger, LoggerService } from '@nestjs/common'; 8 | 9 | const healthCheckExecutorMock: Partial = { 10 | execute: jest.fn(), 11 | }; 12 | 13 | const errorLoggerMock: ErrorLogger = { 14 | getErrorMessage: jest.fn(), 15 | }; 16 | 17 | const loggerMock: Partial = { 18 | log: jest.fn(), 19 | error: jest.fn(), 20 | warn: jest.fn(), 21 | debug: jest.fn(), 22 | }; 23 | 24 | describe('HealthCheckService', () => { 25 | let healthCheckExecutor: HealthCheckExecutor; 26 | let healthCheckService: HealthCheckService; 27 | let logger: LoggerService; 28 | let errorLogger: ErrorLogger; 29 | 30 | beforeEach(async () => { 31 | const module = Test.createTestingModule({ 32 | providers: [ 33 | HealthCheckService, 34 | { 35 | provide: HealthCheckExecutor, 36 | useValue: healthCheckExecutorMock, 37 | }, 38 | { 39 | provide: ERROR_LOGGER, 40 | useValue: errorLoggerMock, 41 | }, 42 | { 43 | provide: TERMINUS_LOGGER, 44 | useValue: loggerMock, 45 | }, 46 | ], 47 | }); 48 | const context = await module.compile(); 49 | 50 | healthCheckService = context.get(HealthCheckService); 51 | healthCheckExecutor = context.get(HealthCheckExecutor); 52 | logger = context.get(TERMINUS_LOGGER); 53 | errorLogger = context.get(ERROR_LOGGER); 54 | }); 55 | 56 | it('should return the result', async () => { 57 | (healthCheckExecutor.execute as jest.Mock).mockReturnValue({ 58 | status: 'ok', 59 | }); 60 | const result = await healthCheckService.check([() => Promise.resolve({})]); 61 | expect(result).toEqual({ status: 'ok' }); 62 | }); 63 | 64 | it('should throw a ServiceUnavailableException', async () => { 65 | (healthCheckExecutor.execute as jest.Mock).mockReturnValue({ 66 | status: 'error', 67 | }); 68 | try { 69 | await healthCheckService.check([() => Promise.resolve({})]); 70 | } catch (error) { 71 | expect((error as any).response).toEqual({ status: 'error' }); 72 | expect((error as any).status).toBe(503); 73 | } 74 | }); 75 | 76 | it('should print an error message', async () => { 77 | (healthCheckExecutor.execute as jest.Mock).mockReturnValue({ 78 | status: 'error', 79 | }); 80 | (errorLogger.getErrorMessage as jest.Mock).mockReturnValue('error message'); 81 | 82 | try { 83 | await healthCheckService.check([() => Promise.resolve({})]); 84 | } catch (error) { 85 | expect(logger.error).toHaveBeenCalledWith('error message'); 86 | } 87 | }); 88 | }); 89 | -------------------------------------------------------------------------------- /lib/health-check/health-check.service.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Injectable, 3 | ServiceUnavailableException, 4 | Inject, 5 | ConsoleLogger, 6 | LoggerService, 7 | } from '@nestjs/common'; 8 | import { ErrorLogger } from './error-logger/error-logger.interface'; 9 | import { ERROR_LOGGER } from './error-logger/error-logger.provider'; 10 | import { HealthCheckExecutor } from './health-check-executor.service'; 11 | import { type HealthCheckResult } from './health-check-result.interface'; 12 | import { type HealthIndicatorFunction } from '../health-indicator'; 13 | import { TERMINUS_LOGGER } from './logger/logger.provider'; 14 | 15 | /** 16 | * Handles Health Checks which can be used in 17 | * Controllers. 18 | */ 19 | @Injectable() 20 | export class HealthCheckService { 21 | constructor( 22 | private readonly healthCheckExecutor: HealthCheckExecutor, 23 | @Inject(ERROR_LOGGER) 24 | private readonly errorLogger: ErrorLogger, 25 | @Inject(TERMINUS_LOGGER) 26 | private readonly logger: LoggerService, 27 | ) { 28 | if (this.logger instanceof ConsoleLogger) { 29 | this.logger.setContext(HealthCheckService.name); 30 | } 31 | } 32 | 33 | /** 34 | * Checks the given health indicators 35 | * 36 | * ```typescript 37 | * 38 | * healthCheckService.check([ 39 | * () => this.http.pingCheck('google', 'https://google.com'), 40 | * ]); 41 | * 42 | * 43 | * ``` 44 | * @param healthIndicators The health indicators which should be checked 45 | */ 46 | async check( 47 | healthIndicators: HealthIndicatorFunction[], 48 | ): Promise { 49 | const result = await this.healthCheckExecutor.execute(healthIndicators); 50 | if (result.status === 'ok') { 51 | return result; 52 | } 53 | 54 | if (result.status === 'error') { 55 | const msg = this.errorLogger.getErrorMessage( 56 | 'Health Check has failed!', 57 | result.details, 58 | ); 59 | this.logger.error(msg); 60 | } 61 | 62 | throw new ServiceUnavailableException(result); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /lib/health-check/index.ts: -------------------------------------------------------------------------------- 1 | export * from './health-check.service'; 2 | export * from './health-check.decorator'; 3 | export * from './health-check-result.interface'; 4 | export * from './health-check.error'; 5 | -------------------------------------------------------------------------------- /lib/health-check/logger/logger.provider.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Logger, 3 | type LoggerService, 4 | type Provider, 5 | type Type, 6 | } from '@nestjs/common'; 7 | 8 | export const TERMINUS_LOGGER = 'TERMINUS_LOGGER'; 9 | 10 | const NOOP_LOGGER = { 11 | // eslint-disable-next-line @typescript-eslint/no-empty-function 12 | log: () => {}, 13 | // eslint-disable-next-line @typescript-eslint/no-empty-function 14 | error: () => {}, 15 | // eslint-disable-next-line @typescript-eslint/no-empty-function 16 | warn: () => {}, 17 | }; 18 | 19 | export function getLoggerProvider( 20 | logger?: Type | boolean, 21 | ): Provider { 22 | // Enable logging 23 | if (logger === true || logger === undefined) { 24 | return { 25 | provide: TERMINUS_LOGGER, 26 | useClass: Logger, 27 | }; 28 | } 29 | 30 | // Disable logging 31 | if (logger === false) { 32 | return { 33 | provide: TERMINUS_LOGGER, 34 | useValue: NOOP_LOGGER, 35 | }; 36 | } 37 | 38 | // Custom logger 39 | return { 40 | provide: TERMINUS_LOGGER, 41 | useClass: logger, 42 | }; 43 | } 44 | -------------------------------------------------------------------------------- /lib/health-indicator/database/database-ping-check-settings.interface.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * The settings for the typeorm ping check 3 | */ 4 | import { type Connection as MongooseConnection } from 'mongoose'; 5 | import { type DataSource } from 'typeorm'; 6 | 7 | /** 8 | * @publicApi 9 | */ 10 | export interface DatabasePingCheckSettings { 11 | /** 12 | * The connection which the ping check should get executed 13 | */ 14 | connection?: DataSource | MongooseConnection; 15 | /** 16 | * The amount of time the check should require in ms 17 | */ 18 | timeout?: number; 19 | } 20 | -------------------------------------------------------------------------------- /lib/health-indicator/database/mongoose.health.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, Scope } from '@nestjs/common'; 2 | import { ModuleRef } from '@nestjs/core'; 3 | import type * as NestJSMongoose from '@nestjs/mongoose'; 4 | import { type HealthIndicatorResult } from '../..'; 5 | import { 6 | promiseTimeout, 7 | TimeoutError as PromiseTimeoutError, 8 | checkPackages, 9 | } from '../../utils'; 10 | import { HealthIndicatorService } from '../health-indicator.service'; 11 | 12 | export interface MongoosePingCheckSettings { 13 | /** 14 | * The connection which the ping check should get executed 15 | */ 16 | connection?: any; 17 | /** 18 | * The amount of time the check should require in ms 19 | */ 20 | timeout?: number; 21 | } 22 | 23 | /** 24 | * The MongooseHealthIndicator contains health indicators 25 | * which are used for health checks related to Mongoose 26 | * 27 | * @publicApi 28 | * @module TerminusModule 29 | */ 30 | @Injectable({ scope: Scope.TRANSIENT }) 31 | export class MongooseHealthIndicator { 32 | constructor( 33 | private readonly moduleRef: ModuleRef, 34 | private readonly healthIndicatorService: HealthIndicatorService, 35 | ) { 36 | this.checkDependantPackages(); 37 | } 38 | 39 | /** 40 | * Checks if the dependant packages are present 41 | */ 42 | private checkDependantPackages() { 43 | checkPackages(['@nestjs/mongoose', 'mongoose'], this.constructor.name); 44 | } 45 | 46 | /** 47 | * Returns the connection of the current DI context 48 | */ 49 | private getContextConnection(): any | null { 50 | const { getConnectionToken } = 51 | // eslint-disable-next-line @typescript-eslint/no-var-requires 52 | require('@nestjs/mongoose') as typeof NestJSMongoose; 53 | 54 | try { 55 | return this.moduleRef.get( 56 | getConnectionToken('DatabaseConnection') as string, 57 | { 58 | strict: false, 59 | }, 60 | ); 61 | } catch (err) { 62 | return null; 63 | } 64 | } 65 | 66 | /** 67 | * Pings a mongoose connection 68 | * @param connection The connection which the ping should get executed 69 | * @param timeout The timeout how long the ping should maximum take 70 | * 71 | */ 72 | private async pingDb(connection: any, timeout: number) { 73 | const promise = 74 | connection.readyState === 1 ? Promise.resolve() : Promise.reject(); 75 | return await promiseTimeout(timeout, promise); 76 | } 77 | 78 | /** 79 | * Checks if the MongoDB responds in (default) 1000ms and 80 | * returns a result object corresponding to the result 81 | * 82 | * @param key The key which will be used for the result object 83 | * @param options The options for the ping 84 | * @example 85 | * mongooseHealthIndicator.pingCheck('mongodb', { timeout: 1500 }); 86 | */ 87 | public async pingCheck( 88 | key: Key, 89 | options: MongoosePingCheckSettings = {}, 90 | ): Promise> { 91 | this.checkDependantPackages(); 92 | const check = this.healthIndicatorService.check(key); 93 | 94 | const connection = options.connection || this.getContextConnection(); 95 | const timeout = options.timeout || 1000; 96 | 97 | if (!connection) { 98 | return check.down('Connection provider not found in application context'); 99 | } 100 | 101 | try { 102 | await this.pingDb(connection, timeout); 103 | } catch (err) { 104 | if (err instanceof PromiseTimeoutError) { 105 | return check.down(`timeout of ${timeout}ms exceeded`); 106 | } 107 | 108 | return check.down(); 109 | } 110 | 111 | return check.up(); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /lib/health-indicator/database/prisma.health.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common'; 2 | import { 3 | promiseTimeout, 4 | TimeoutError as PromiseTimeoutError, 5 | } from '../../utils'; 6 | import { type HealthIndicatorResult } from '../health-indicator-result.interface'; 7 | import { HealthIndicatorService } from '../health-indicator.service'; 8 | 9 | type PingCommandSignature = { [Key in string]?: number }; 10 | 11 | type PrismaClientDocument = { 12 | $runCommandRaw: (command: PingCommandSignature) => any; 13 | }; 14 | 15 | type PrismaClientSQL = { 16 | $queryRawUnsafe: (query: string) => any; 17 | }; 18 | 19 | type PrismaClient = PrismaClientDocument | PrismaClientSQL; 20 | 21 | export interface PrismaClientPingCheckSettings { 22 | /** 23 | * The amount of time the check should require in ms 24 | */ 25 | timeout?: number; 26 | } 27 | 28 | /** 29 | * The PrismaHealthIndicator contains health indicators 30 | * which are used for health checks related to Prisma 31 | * 32 | * @publicApi 33 | * @module TerminusModule 34 | */ 35 | @Injectable() 36 | export class PrismaHealthIndicator { 37 | constructor( 38 | private readonly healthIndicatorService: HealthIndicatorService, 39 | ) {} 40 | 41 | private async pingDb(timeout: number, prismaClientSQLOrMongo: PrismaClient) { 42 | // The prisma client generates two different typescript types for different databases 43 | // but inside they've the same methods 44 | // But they will fail when using a document method on sql database, that's why we do the try catch down below 45 | const prismaClient = prismaClientSQLOrMongo as PrismaClientSQL & 46 | PrismaClientDocument; 47 | 48 | try { 49 | await promiseTimeout(timeout, prismaClient.$runCommandRaw({ ping: 1 })); 50 | } catch (error) { 51 | if ( 52 | error instanceof Error && 53 | error.toString().includes('Use the mongodb provider') 54 | ) { 55 | await promiseTimeout(timeout, prismaClient.$queryRawUnsafe('SELECT 1')); 56 | return; 57 | } 58 | 59 | throw error; 60 | } 61 | } 62 | 63 | /** 64 | * Checks if the Prisma responds in (default) 1000ms and 65 | * returns a result object corresponding to the result 66 | * 67 | * @param key The key which will be used for the result object 68 | * @param prismaClient PrismaClient 69 | * @param options The options for the ping 70 | */ 71 | public async pingCheck( 72 | key: Key, 73 | prismaClient: PrismaClient, 74 | options: PrismaClientPingCheckSettings = {}, 75 | ): Promise> { 76 | const check = this.healthIndicatorService.check(key); 77 | const timeout = options.timeout || 1000; 78 | 79 | try { 80 | await this.pingDb(timeout, prismaClient); 81 | } catch (error) { 82 | if (error instanceof PromiseTimeoutError) { 83 | return check.down(`timeout of ${timeout}ms exceeded`); 84 | } 85 | 86 | return check.down(); 87 | } 88 | 89 | return check.up(); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /lib/health-indicator/database/sequelize.health.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, Scope } from '@nestjs/common'; 2 | import { ModuleRef } from '@nestjs/core'; 3 | import type * as NestJSSequelize from '@nestjs/sequelize'; 4 | import { type HealthIndicatorResult } from '../..'; 5 | import { 6 | promiseTimeout, 7 | TimeoutError as PromiseTimeoutError, 8 | checkPackages, 9 | } from '../../utils'; 10 | import { HealthIndicatorService } from '../health-indicator.service'; 11 | 12 | export interface SequelizePingCheckSettings { 13 | /** 14 | * The connection which the ping check should get executed 15 | */ 16 | connection?: any; 17 | /** 18 | * The amount of time the check should require in ms 19 | */ 20 | timeout?: number; 21 | } 22 | 23 | /** 24 | * The SequelizeHealthIndicator contains health indicators 25 | * which are used for health checks related to Sequelize 26 | * 27 | * @publicApi 28 | * @module TerminusModule 29 | */ 30 | @Injectable({ scope: Scope.TRANSIENT }) 31 | export class SequelizeHealthIndicator { 32 | constructor( 33 | private readonly moduleRef: ModuleRef, 34 | private readonly healthIndicatorService: HealthIndicatorService, 35 | ) { 36 | this.checkDependantPackages(); 37 | } 38 | 39 | /** 40 | * Checks if the dependant packages are present 41 | */ 42 | private checkDependantPackages() { 43 | checkPackages(['@nestjs/sequelize', 'sequelize'], this.constructor.name); 44 | } 45 | 46 | /** 47 | * Returns the connection of the current DI context 48 | */ 49 | private getContextConnection(): any | null { 50 | const { getConnectionToken } = 51 | // eslint-disable-next-line @typescript-eslint/no-var-requires 52 | require('@nestjs/sequelize/dist/common/sequelize.utils') as typeof NestJSSequelize; 53 | 54 | try { 55 | return this.moduleRef.get(getConnectionToken() as string, { 56 | strict: false, 57 | }); 58 | } catch (err) { 59 | return null; 60 | } 61 | } 62 | 63 | /** 64 | * Pings a sequelize connection 65 | * @param connection The connection which the ping should get executed 66 | * @param timeout The timeout how long the ping should maximum take 67 | * 68 | */ 69 | private async pingDb(connection: any, timeout: number) { 70 | const check: Promise = connection.query('SELECT 1'); 71 | return await promiseTimeout(timeout, check); 72 | } 73 | 74 | /** 75 | * Checks if the Sequelize responds in (default) 1000ms and 76 | * returns a result object corresponding to the result 77 | * 78 | * @param key The key which will be used for the result object 79 | * @param options The options for the ping 80 | * @example 81 | * sequelizeHealthIndicator.pingCheck('database', { timeout: 1500 }); 82 | */ 83 | public async pingCheck( 84 | key: Key, 85 | options: SequelizePingCheckSettings = {}, 86 | ): Promise> { 87 | this.checkDependantPackages(); 88 | const check = this.healthIndicatorService.check(key); 89 | 90 | const connection = options.connection || this.getContextConnection(); 91 | const timeout = options.timeout || 1000; 92 | 93 | if (!connection) { 94 | return check.down('Connection provider not found in application context'); 95 | } 96 | 97 | try { 98 | await this.pingDb(connection, timeout); 99 | } catch (err) { 100 | if (err instanceof PromiseTimeoutError) { 101 | return check.down(`timeout of ${timeout}ms exceeded`); 102 | } 103 | 104 | return check.down(); 105 | } 106 | 107 | return check.up(); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /lib/health-indicator/disk/disk-health-options.type.ts: -------------------------------------------------------------------------------- 1 | import { type XOR } from '../../utils/types'; 2 | 3 | /** 4 | * @internal 5 | */ 6 | interface DiskOptionsBase { 7 | /** 8 | * The path which should get checked 9 | */ 10 | path: string; 11 | } 12 | 13 | /** 14 | * @internal 15 | */ 16 | interface DiskOptionsThreshold { 17 | /** 18 | * The threshold in bytes 19 | */ 20 | threshold: number; 21 | } 22 | 23 | /** 24 | * @internal 25 | */ 26 | interface DiskOptionsThresholdPercent { 27 | /** 28 | * The threshold in percent (e.g. 0.5) 29 | */ 30 | thresholdPercent: number; 31 | } 32 | 33 | /** 34 | * @internal 35 | */ 36 | export type DiskOptionsWithThreshold = DiskOptionsBase & DiskOptionsThreshold; 37 | /** 38 | * @internal 39 | */ 40 | export type DiskOptionsWithThresholdPercent = DiskOptionsBase & 41 | DiskOptionsThresholdPercent; 42 | 43 | /** 44 | * The options of the disk health indicator 45 | * @publicApi 46 | */ 47 | export type DiskHealthIndicatorOptions = XOR< 48 | DiskOptionsWithThreshold, 49 | DiskOptionsWithThresholdPercent 50 | >; 51 | -------------------------------------------------------------------------------- /lib/health-indicator/disk/disk-usage-lib.provider.ts: -------------------------------------------------------------------------------- 1 | import checkDiskSpace from 'check-disk-space'; 2 | import { CHECK_DISK_SPACE_LIB } from '../../terminus.constants'; 3 | 4 | /** 5 | * Wrapper of the check-disk-space library. 6 | * 7 | * @internal 8 | */ 9 | export const DiskUsageLibProvider = { 10 | provide: CHECK_DISK_SPACE_LIB, 11 | useValue: checkDiskSpace, 12 | }; 13 | -------------------------------------------------------------------------------- /lib/health-indicator/disk/disk.health.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, Inject } from '@nestjs/common'; 2 | import { isNil } from '@nestjs/common/utils/shared.utils'; 3 | import type checkDiskSpace from 'check-disk-space'; 4 | import { 5 | type DiskHealthIndicatorOptions, 6 | type DiskOptionsWithThresholdPercent, 7 | } from './disk-health-options.type'; 8 | import { type HealthIndicatorResult } from '../'; 9 | import { STORAGE_EXCEEDED } from '../../errors/messages.constant'; 10 | import { CHECK_DISK_SPACE_LIB } from '../../terminus.constants'; 11 | import { HealthIndicatorService } from '../health-indicator.service'; 12 | 13 | type CheckDiskSpace = typeof checkDiskSpace; 14 | 15 | /** 16 | * The DiskHealthIndicator contains checks which are related 17 | * to the disk storage of the current running machine 18 | * 19 | * @publicApi 20 | * @module TerminusModule 21 | */ 22 | @Injectable() 23 | export class DiskHealthIndicator { 24 | constructor( 25 | @Inject(CHECK_DISK_SPACE_LIB) 26 | private readonly checkDiskSpace: CheckDiskSpace, 27 | private readonly healthIndicatorService: HealthIndicatorService, 28 | ) {} 29 | 30 | /** 31 | * Checks if the given option has the property the `thresholdPercent` attribute 32 | * 33 | * @param {DiskHealthIndicatorOptions} options The options of the `DiskHealthIndicator` 34 | * 35 | * @private 36 | * 37 | * @returns {boolean} whether given option has the property the `thresholdPercent` attribute 38 | */ 39 | private isOptionThresholdPercent( 40 | options: DiskHealthIndicatorOptions, 41 | ): options is DiskOptionsWithThresholdPercent { 42 | return !isNil( 43 | (options as DiskOptionsWithThresholdPercent).thresholdPercent, 44 | ); 45 | } 46 | 47 | /** 48 | * Checks if the size of the given size has exceeded the 49 | * given threshold 50 | * 51 | * @param key The key which will be used for the result object 52 | * 53 | * @throws {HealthCheckError} In case the health indicator failed 54 | * @throws {StorageExceededError} In case the disk storage has exceeded the given threshold 55 | * 56 | * @returns {Promise} The result of the health indicator check 57 | * 58 | * @example 59 | * // The used disk storage should not exceed 250 GB 60 | * diskHealthIndicator.checkStorage('storage', { threshold: 250 * 1024 * 1024 * 1024, path: '/' }); 61 | * @example 62 | * // The used disk storage should not exceed 50% of the full disk size 63 | * diskHealthIndicator.checkStorage('storage', { thresholdPercent: 0.5, path: 'C:\\' }); 64 | */ 65 | public async checkStorage( 66 | key: Key, 67 | options: DiskHealthIndicatorOptions, 68 | ): Promise> { 69 | const check = this.healthIndicatorService.check(key); 70 | const { free, size } = await this.checkDiskSpace(options.path); 71 | const used = size - free; 72 | 73 | // Prevent division by zero 74 | if (isNaN(size) || size === 0) { 75 | return check.down(STORAGE_EXCEEDED('disk storage')); 76 | } 77 | 78 | let isHealthy = false; 79 | if (this.isOptionThresholdPercent(options)) { 80 | isHealthy = options.thresholdPercent >= used / size; 81 | } else { 82 | isHealthy = options.threshold >= used; 83 | } 84 | 85 | if (!isHealthy) { 86 | return check.down(STORAGE_EXCEEDED('disk storage')); 87 | } 88 | return check.up(); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /lib/health-indicator/disk/index.ts: -------------------------------------------------------------------------------- 1 | export * from './disk.health'; 2 | export { DiskHealthIndicatorOptions } from './disk-health-options.type'; 3 | -------------------------------------------------------------------------------- /lib/health-indicator/health-indicator-result.interface.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @publicApi 3 | */ 4 | export type HealthIndicatorStatus = 'up' | 'down'; 5 | 6 | /** 7 | * The result object of a health indicator 8 | * @publicApi 9 | */ 10 | export type HealthIndicatorResult< 11 | Key extends string = string, 12 | Status extends HealthIndicatorStatus = HealthIndicatorStatus, 13 | OptionalData extends Record = Record, 14 | > = Record; 15 | -------------------------------------------------------------------------------- /lib/health-indicator/health-indicator.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common'; 2 | import { type HealthIndicatorResult } from './health-indicator-result.interface'; 3 | 4 | /** 5 | * Helper service which can be used to create health indicator results 6 | * @publicApi 7 | */ 8 | @Injectable() 9 | export class HealthIndicatorService { 10 | check(key: Key) { 11 | return new HealthIndicatorSession(key); 12 | } 13 | } 14 | 15 | type AdditionalData = Record; 16 | 17 | /** 18 | * Indicate the health of a health indicator with the given key 19 | * 20 | * @publicApi 21 | */ 22 | export class HealthIndicatorSession = string> { 23 | constructor(private readonly key: Key) {} 24 | 25 | /** 26 | * Mark the health indicator as `down` 27 | * @param data additional data which will get appended to the result object 28 | */ 29 | down( 30 | data?: T, 31 | ): HealthIndicatorResult; 32 | down( 33 | data?: T, 34 | ): HealthIndicatorResult; 35 | down( 36 | data?: T, 37 | ): HealthIndicatorResult { 38 | let additionalData: AdditionalData = {}; 39 | 40 | if (typeof data === 'string') { 41 | additionalData = { message: data }; 42 | } else if (typeof data === 'object') { 43 | additionalData = data; 44 | } 45 | 46 | const detail = { 47 | status: 'down' as const, 48 | ...additionalData, 49 | }; 50 | 51 | return { 52 | [this.key]: detail, 53 | // TypeScript does not infer this.key as Key correctly. 54 | } as Record; 55 | } 56 | 57 | /** 58 | * Mark the health indicator as `up` 59 | * @param data additional data which will get appended to the result object 60 | */ 61 | up(data?: T): HealthIndicatorResult; 62 | up( 63 | data?: T, 64 | ): HealthIndicatorResult; 65 | up( 66 | data?: T, 67 | ): HealthIndicatorResult { 68 | let additionalData: AdditionalData = {}; 69 | 70 | if (typeof data === 'string') { 71 | additionalData = { message: data }; 72 | } else if (typeof data === 'object') { 73 | additionalData = data; 74 | } 75 | 76 | const detail = { 77 | status: 'up' as const, 78 | ...additionalData, 79 | }; 80 | 81 | return { 82 | [this.key]: detail, 83 | // TypeScript does not infer this.key as Key correctly. 84 | } as Record; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /lib/health-indicator/health-indicator.ts: -------------------------------------------------------------------------------- 1 | import { type HealthIndicatorResult } from './'; 2 | 3 | /** 4 | * A health indicator function for a health check 5 | * 6 | * @publicApi 7 | */ 8 | export type HealthIndicatorFunction = () => 9 | | PromiseLike 10 | | HealthIndicatorResult; 11 | 12 | /** 13 | * @deprecated 14 | * **This class has been deprecated and will be removed in the next major release.** 15 | * Instead utilise the `HealthIndicatorService` to indicate the health of your health indicator. 16 | * 17 | * @see {@link https://docs.nestjs.com/migration-guide#terminus-module|Migration Guide} 18 | * 19 | * @description 20 | * Represents an abstract health indicator 21 | * with common functionalities 22 | * 23 | * A custom HealthIndicator should inherit the `HealthIndicator` class. 24 | * 25 | * @example 26 | * ```typescript 27 | * class MyHealthIndicator extends HealthIndicator { 28 | * public check(key: string) { 29 | * // Replace with the actual check 30 | * const isHealthy = true; 31 | * // Returns { [key]: { status: 'up', message: 'Up and running' } } 32 | * return super.getStatus(key, isHealthy, { message: 'Up and running' }); 33 | * } 34 | * } 35 | * ``` 36 | * @publicApi 37 | */ 38 | export abstract class HealthIndicator { 39 | /** 40 | * Generates the health indicator result object 41 | * @param key The key which will be used as key for the result object 42 | * @param isHealthy Whether the health indicator is healthy 43 | * @param data Additional data which will get appended to the result object 44 | */ 45 | protected getStatus( 46 | key: string, 47 | isHealthy: boolean, 48 | data?: { [key: string]: any }, 49 | ): HealthIndicatorResult { 50 | return { 51 | [key]: { 52 | status: isHealthy ? 'up' : 'down', 53 | ...data, 54 | }, 55 | }; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /lib/health-indicator/health-indicators.provider.ts: -------------------------------------------------------------------------------- 1 | import { 2 | TypeOrmHealthIndicator, 3 | HttpHealthIndicator, 4 | MongooseHealthIndicator, 5 | SequelizeHealthIndicator, 6 | DiskHealthIndicator, 7 | MemoryHealthIndicator, 8 | MicroserviceHealthIndicator, 9 | GRPCHealthIndicator, 10 | PrismaHealthIndicator, 11 | } from '.'; 12 | import { MikroOrmHealthIndicator } from './database/mikro-orm.health'; 13 | 14 | /** 15 | * All the health indicators terminus provides as array 16 | */ 17 | export const HEALTH_INDICATORS = [ 18 | TypeOrmHealthIndicator, 19 | HttpHealthIndicator, 20 | MongooseHealthIndicator, 21 | SequelizeHealthIndicator, 22 | DiskHealthIndicator, 23 | MemoryHealthIndicator, 24 | MicroserviceHealthIndicator, 25 | GRPCHealthIndicator, 26 | MikroOrmHealthIndicator, 27 | PrismaHealthIndicator, 28 | ]; 29 | -------------------------------------------------------------------------------- /lib/health-indicator/http/axios.interfaces.ts: -------------------------------------------------------------------------------- 1 | // Note: We copy over Axios interfaces in order to not depend on Axios directly 2 | // since it is a optional peer dependency 3 | type Method = 4 | | 'get' 5 | | 'GET' 6 | | 'delete' 7 | | 'DELETE' 8 | | 'head' 9 | | 'HEAD' 10 | | 'options' 11 | | 'OPTIONS' 12 | | 'post' 13 | | 'POST' 14 | | 'put' 15 | | 'PUT' 16 | | 'patch' 17 | | 'PATCH' 18 | | 'purge' 19 | | 'PURGE' 20 | | 'link' 21 | | 'LINK' 22 | | 'unlink' 23 | | 'UNLINK'; 24 | 25 | export interface AxiosRequestConfig { 26 | url?: string; 27 | method?: Method | string; 28 | baseURL?: string; 29 | transformRequest?: any | any[]; 30 | transformResponse?: any | any[]; 31 | headers?: any; 32 | params?: any; 33 | paramsSerializer?: (params: any) => string; 34 | data?: any; 35 | timeout?: number; 36 | timeoutErrorMessage?: string; 37 | withCredentials?: boolean; 38 | adapter?: any; 39 | auth?: any; 40 | responseType?: any; 41 | xsrfCookieName?: string; 42 | xsrfHeaderName?: string; 43 | onUploadProgress?: (progressEvent: any) => void; 44 | onDownloadProgress?: (progressEvent: any) => void; 45 | maxContentLength?: number; 46 | validateStatus?: ((status: number) => boolean) | null; 47 | maxBodyLength?: number; 48 | maxRedirects?: number; 49 | socketPath?: string | null; 50 | httpAgent?: any; 51 | httpsAgent?: any; 52 | proxy?: any | false; 53 | cancelToken?: any; 54 | decompress?: boolean; 55 | } 56 | 57 | export interface AxiosResponse { 58 | data: T; 59 | status: number; 60 | statusText: string; 61 | headers: any; 62 | config: any; 63 | request?: any; 64 | } 65 | -------------------------------------------------------------------------------- /lib/health-indicator/http/index.ts: -------------------------------------------------------------------------------- 1 | export * from './http.health'; 2 | -------------------------------------------------------------------------------- /lib/health-indicator/index.ts: -------------------------------------------------------------------------------- 1 | export * from './health-indicator-result.interface'; 2 | export * from './health-indicator'; 3 | export { HealthIndicatorService } from './health-indicator.service'; 4 | 5 | /** Health Indicators */ 6 | export * from './http/http.health'; 7 | export * from './database/mongoose.health'; 8 | export * from './database/typeorm.health'; 9 | export * from './database/mikro-orm.health'; 10 | export * from './database/sequelize.health'; 11 | export * from './database/prisma.health'; 12 | export * from './microservice/microservice.health'; 13 | export * from './microservice/grpc.health'; 14 | export * from './disk'; 15 | export * from './memory'; 16 | -------------------------------------------------------------------------------- /lib/health-indicator/memory/index.ts: -------------------------------------------------------------------------------- 1 | export * from './memory.health'; 2 | -------------------------------------------------------------------------------- /lib/health-indicator/memory/memory.health.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common'; 2 | import { type HealthIndicatorResult } from '../'; 3 | import { STORAGE_EXCEEDED } from '../../errors/messages.constant'; 4 | import { HealthIndicatorService } from '../health-indicator.service'; 5 | 6 | /** 7 | * The MemoryHealthIndicator contains checks which are related 8 | * to the memory storage of the current running machine 9 | * 10 | * @publicApi 11 | * @module TerminusModule 12 | */ 13 | @Injectable() 14 | export class MemoryHealthIndicator { 15 | constructor( 16 | private readonly healthIndicatorService: HealthIndicatorService, 17 | ) {} 18 | 19 | /** 20 | * Checks the heap space and returns the status 21 | * 22 | * @param key The key which will be used for the result object 23 | * @param options The options of the `MemoryHealthIndicator` 24 | * 25 | * @throws {StorageExceededError} In case the heap has exceeded the given threshold 26 | * 27 | * 28 | * @returns {Promise} The result of the health indicator check 29 | * 30 | * @example 31 | * // The process should not use more than 150MB memory 32 | * memoryHealthIndicator.checkHeap('memory_heap', 150 * 1024 * 1024); 33 | */ 34 | public async checkHeap( 35 | key: Key, 36 | heapUsedThreshold: number, 37 | ): Promise> { 38 | const check = this.healthIndicatorService.check(key); 39 | const { heapUsed } = process.memoryUsage(); 40 | 41 | if (heapUsedThreshold < heapUsed) { 42 | return check.down(STORAGE_EXCEEDED('heap')); 43 | } 44 | 45 | return check.up(); 46 | } 47 | 48 | /** 49 | * Checks the rss space and returns the status 50 | * 51 | * @param key The key which will be used for the result object 52 | * @param options The options of the `MemoryHealthIndicator` 53 | * 54 | * @throws {StorageExceededError} In case the rss has exceeded the given threshold 55 | * 56 | * @returns {Promise} The result of the health indicator check 57 | * 58 | * @example 59 | * // The process should not have more than 150MB allocated 60 | * memoryHealthIndicator.checkRSS('memory_rss', 150 * 1024 * 1024); 61 | */ 62 | public async checkRSS( 63 | key: Key, 64 | rssThreshold: number, 65 | ): Promise> { 66 | const check = this.healthIndicatorService.check(key); 67 | const { rss } = process.memoryUsage(); 68 | 69 | if (rssThreshold < rss) { 70 | return check.down(STORAGE_EXCEEDED('rss')); 71 | } 72 | 73 | return check.up(); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /lib/health-indicator/microservice/protos/health.proto: -------------------------------------------------------------------------------- 1 | // Proto according to the GRPC specs 2 | // https://github.com/grpc/grpc/blob/master/doc/health-checking.md 3 | syntax = "proto3"; 4 | 5 | package grpc.health.v1; 6 | 7 | message HealthCheckRequest { 8 | string service = 1; 9 | } 10 | 11 | message HealthCheckResponse { 12 | enum ServingStatus { 13 | UNKNOWN = 0; 14 | SERVING = 1; 15 | NOT_SERVING = 2; 16 | } 17 | ServingStatus status = 1; 18 | } 19 | 20 | service Health { 21 | rpc Check(HealthCheckRequest) returns (HealthCheckResponse); 22 | } -------------------------------------------------------------------------------- /lib/index.ts: -------------------------------------------------------------------------------- 1 | export { TerminusModule } from './terminus.module'; 2 | export { TerminusModuleOptions } from './terminus-options.interface'; 3 | export * from './health-indicator'; 4 | export * from './errors'; 5 | export { 6 | HealthCheck, 7 | HealthCheckService, 8 | // eslint-disable-next-line deprecation/deprecation 9 | HealthCheckError, 10 | HealthCheckStatus, 11 | HealthCheckResult, 12 | } from './health-check'; 13 | -------------------------------------------------------------------------------- /lib/terminus-options.interface.ts: -------------------------------------------------------------------------------- 1 | import { type LoggerService, type Type } from '@nestjs/common'; 2 | 3 | export type ErrorLogStyle = 'pretty' | 'json'; 4 | 5 | /** 6 | * The Terminus module options 7 | * 8 | * errorLogStyle: The style of the error logger. Either 'pretty' or 'json'. Default to 'json'. 9 | * logger: The logger to use. Either default logger or your own. 10 | * gracefulShutdownTimeoutMs: The timeout to wait in ms before the application shuts down. Default to 0ms. 11 | * @publicApi 12 | */ 13 | export interface TerminusModuleOptions { 14 | /** 15 | * The style of the error logger 16 | * @default 'json' 17 | */ 18 | errorLogStyle?: ErrorLogStyle; 19 | /** 20 | * The logger to use. Either default logger or your own. 21 | */ 22 | logger?: Type | boolean; 23 | /** 24 | * The timeout to wait in ms before the application shuts down 25 | * @default 0 26 | */ 27 | gracefulShutdownTimeoutMs?: number; 28 | } 29 | -------------------------------------------------------------------------------- /lib/terminus.constants.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * The inject token for the third party `check-disk-space` library 3 | * @internal 4 | */ 5 | export const CHECK_DISK_SPACE_LIB = 'CheckDiskSpaceLib'; 6 | -------------------------------------------------------------------------------- /lib/terminus.module.ts: -------------------------------------------------------------------------------- 1 | import { type DynamicModule, Module, type Provider } from '@nestjs/common'; 2 | import { 3 | GracefulShutdownService, 4 | TERMINUS_GRACEFUL_SHUTDOWN_TIMEOUT, 5 | } from './graceful-shutdown-timeout/graceful-shutdown-timeout.service'; 6 | import { HealthCheckService } from './health-check'; 7 | import { getErrorLoggerProvider } from './health-check/error-logger/error-logger.provider'; 8 | import { ERROR_LOGGERS } from './health-check/error-logger/error-loggers.provider'; 9 | import { HealthCheckExecutor } from './health-check/health-check-executor.service'; 10 | import { getLoggerProvider } from './health-check/logger/logger.provider'; 11 | import { DiskUsageLibProvider } from './health-indicator/disk/disk-usage-lib.provider'; 12 | import { HealthIndicatorService } from './health-indicator/health-indicator.service'; 13 | import { HEALTH_INDICATORS } from './health-indicator/health-indicators.provider'; 14 | import { type TerminusModuleOptions } from './terminus-options.interface'; 15 | 16 | const baseProviders: Provider[] = [ 17 | ...ERROR_LOGGERS, 18 | HealthIndicatorService, 19 | DiskUsageLibProvider, 20 | HealthCheckExecutor, 21 | HealthCheckService, 22 | ...HEALTH_INDICATORS, 23 | ]; 24 | 25 | const exports_ = [ 26 | HealthIndicatorService, 27 | HealthCheckService, 28 | ...HEALTH_INDICATORS, 29 | ]; 30 | 31 | /** 32 | * The Terminus module integrates health checks 33 | * and graceful shutdowns in your Nest application 34 | * 35 | * @publicApi 36 | */ 37 | @Module({ 38 | providers: [...baseProviders, getErrorLoggerProvider(), getLoggerProvider()], 39 | exports: exports_, 40 | }) 41 | export class TerminusModule { 42 | static forRoot(options: TerminusModuleOptions = {}): DynamicModule { 43 | const { 44 | errorLogStyle = 'json', 45 | logger = true, 46 | gracefulShutdownTimeoutMs = 0, 47 | } = options; 48 | 49 | const providers: Provider[] = [ 50 | ...baseProviders, 51 | getErrorLoggerProvider(errorLogStyle), 52 | getLoggerProvider(logger), 53 | ]; 54 | 55 | if (gracefulShutdownTimeoutMs > 0) { 56 | providers.push({ 57 | provide: TERMINUS_GRACEFUL_SHUTDOWN_TIMEOUT, 58 | useValue: gracefulShutdownTimeoutMs, 59 | }); 60 | 61 | providers.push(GracefulShutdownService); 62 | } 63 | 64 | return { 65 | module: TerminusModule, 66 | providers, 67 | exports: exports_, 68 | }; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /lib/utils/checkPackage.util.ts: -------------------------------------------------------------------------------- 1 | import { Logger } from '@nestjs/common/services/logger.service'; 2 | 3 | /** 4 | * Generates the string which packages are missing and 5 | * how to install them 6 | * 7 | * @param name The name of the packages 8 | * @param reason The reason why these packages are important 9 | * 10 | * @internal 11 | */ 12 | const MISSING_REQUIRED_DEPENDENCY = (names: string[], reason: string): string => 13 | `The "${names.join('", "')}" package${ 14 | names.length > 1 ? 's are' : ' is' 15 | } missing. Please, make sure to install the librar${ 16 | names.length > 1 ? 'ies' : 'y' 17 | } ($ npm install ${names.join(' ')}) to take advantage of ${reason}.`; 18 | 19 | /** 20 | * @internal 21 | */ 22 | const logger = new Logger('PackageLoader'); 23 | 24 | /** 25 | * Loads an optional module 26 | * 27 | * @param module The module name 28 | * @internal 29 | * 30 | * @returns {T | null} The module or null if has not found 31 | */ 32 | function optional(module: string): T | null { 33 | try { 34 | if (module[0] in { '.': 1 }) { 35 | module = process.cwd() + module.substring(1); 36 | } 37 | return require(`${module}`); 38 | } catch (err) {} 39 | return null; 40 | } 41 | 42 | /** 43 | * Checks if the given packages are available and logs using the Nest Logger 44 | * which packages are not available 45 | * @param packageNames The package names 46 | * @param reason The reason why these packages are important 47 | * 48 | * @internal 49 | * 50 | * @example 51 | * // The "no_package" package is missing. Please, make sure to install the library ($ npm install no_package) to take advantage of TEST. 52 | * checkPackages(['process', 'no_package'], 'TEST') 53 | */ 54 | export function checkPackages(packageNames: string[], reason: string): any[] { 55 | const packages = packageNames.map((packageName, index) => ({ 56 | pkg: optional(packageName), 57 | index, 58 | })); 59 | 60 | const missingDependenciesNames = packages 61 | .filter((pkg) => pkg.pkg === null) 62 | .map((pkg) => packageNames[pkg.index]); 63 | 64 | if (missingDependenciesNames.length) { 65 | logger.error(MISSING_REQUIRED_DEPENDENCY(missingDependenciesNames, reason)); 66 | Logger.flush(); 67 | process.exit(1); 68 | } 69 | return packages.map((pkg) => pkg.pkg); 70 | } 71 | -------------------------------------------------------------------------------- /lib/utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from './promise-timeout'; 2 | export * from './checkPackage.util'; 3 | export * from './types'; 4 | export * from './is-error'; 5 | export * from './sleep'; 6 | -------------------------------------------------------------------------------- /lib/utils/is-error.ts: -------------------------------------------------------------------------------- 1 | import { type HealthCheckError } from '../'; 2 | import { type AxiosError } from '../errors/axios.error'; 3 | 4 | // eslint-disable-next-line deprecation/deprecation 5 | export function isHealthCheckError(err: any): err is HealthCheckError { 6 | return err?.isHealthCheckError; 7 | } 8 | 9 | export function isAxiosError(err: any): err is AxiosError { 10 | return err?.isAxiosError; 11 | } 12 | 13 | export function isError(err: any): err is Error { 14 | return !!err?.message; 15 | } 16 | -------------------------------------------------------------------------------- /lib/utils/promise-timeout.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * An errors which gets raised when the timeout 3 | * exceeded 4 | * 5 | * @internal 6 | */ 7 | export class TimeoutError extends Error {} 8 | 9 | /** 10 | * Executes a promise in the given timeout. If the promise 11 | * does not finish in the given timeout, it will 12 | * raise a TimeoutError 13 | * 14 | * @param {number} ms The timeout in milliseconds 15 | * @param {Promise} promise The promise which should get executed 16 | * 17 | * @internal 18 | */ 19 | export const promiseTimeout = ( 20 | ms: number, 21 | promise: Promise, 22 | ): Promise => { 23 | let timer: NodeJS.Timeout; 24 | return Promise.race([ 25 | promise, 26 | new Promise( 27 | (_, reject) => 28 | (timer = setTimeout( 29 | () => reject(new TimeoutError(`Timed out in ${ms}ms.`)), 30 | ms, 31 | )), 32 | ), 33 | ]).finally(() => clearTimeout(timer)); 34 | }; 35 | -------------------------------------------------------------------------------- /lib/utils/sleep.ts: -------------------------------------------------------------------------------- 1 | export const sleep = (ms: number) => 2 | new Promise((resolve) => setTimeout(resolve, ms)); 3 | -------------------------------------------------------------------------------- /lib/utils/types.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @internal 3 | */ 4 | export type Without = { [P in Exclude]?: never }; 5 | /** 6 | * @internal 7 | */ 8 | export type XOR = T | U extends object 9 | ? (Without & U) | (Without & T) 10 | : T | U; 11 | 12 | /** 13 | * @internal 14 | */ 15 | export type PropType = TObj[TProp]; 16 | -------------------------------------------------------------------------------- /nest-cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/nest-cli", 3 | "collection": "@nestjs/schematics", 4 | "sourceRoot": "lib", 5 | "compilerOptions": { 6 | "assets": ["**/*.proto"] 7 | } 8 | } -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - 'sample/**' -------------------------------------------------------------------------------- /sample/000-dogs-app/.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 | -------------------------------------------------------------------------------- /sample/000-dogs-app/.gitignore: -------------------------------------------------------------------------------- 1 | # File created using '.gitignore Generator' for Visual Studio Code: https://bit.ly/vscode-gig 2 | 3 | # Created by https://www.gitignore.io/api/linux,visualstudiocode,node 4 | 5 | ### Linux ### 6 | *~ 7 | 8 | # temporary files which can be created if a process still has a handle open of a deleted file 9 | .fuse_hidden* 10 | 11 | # KDE directory preferences 12 | .directory 13 | 14 | # Linux trash folder which might appear on any partition or disk 15 | .Trash-* 16 | 17 | # .nfs files are created when an open file is removed but is still being accessed 18 | .nfs* 19 | 20 | ### Node ### 21 | # Logs 22 | logs 23 | *.log 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # Runtime data 29 | pids 30 | *.pid 31 | *.seed 32 | *.pid.lock 33 | 34 | # Directory for instrumented libs generated by jscoverage/JSCover 35 | lib-cov 36 | 37 | # Coverage directory used by tools like istanbul 38 | coverage 39 | 40 | # nyc test coverage 41 | .nyc_output 42 | 43 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 44 | .grunt 45 | 46 | # Bower dependency directory (https://bower.io/) 47 | bower_components 48 | 49 | # node-waf configuration 50 | .lock-wscript 51 | 52 | # Compiled binary addons (https://nodejs.org/api/addons.html) 53 | build/Release 54 | 55 | # Dependency directories 56 | node_modules/ 57 | jspm_packages/ 58 | 59 | # TypeScript v1 declaration files 60 | typings/ 61 | 62 | # Optional npm cache directory 63 | .npm 64 | 65 | # Optional eslint cache 66 | .eslintcache 67 | 68 | # Optional REPL history 69 | .node_repl_history 70 | 71 | # Output of 'npm pack' 72 | *.tgz 73 | 74 | # Yarn Integrity file 75 | .yarn-integrity 76 | 77 | # dotenv environment variables file 78 | .env 79 | 80 | # parcel-bundler cache (https://parceljs.org/) 81 | .cache 82 | 83 | # next.js build output 84 | .next 85 | 86 | # nuxt.js build output 87 | .nuxt 88 | 89 | # vuepress build output 90 | .vuepress/dist 91 | 92 | # Serverless directories 93 | .serverless 94 | 95 | ### VisualStudioCode ### 96 | .vscode/* 97 | !.vscode/settings.json 98 | !.vscode/tasks.json 99 | !.vscode/launch.json 100 | !.vscode/extensions.json 101 | 102 | 103 | # End of https://www.gitignore.io/api/linux,visualstudiocode,node 104 | 105 | # Custom rules (everything added below won't be overriden by 'Generate .gitignore File' if you use 'Update' option) 106 | 107 | -------------------------------------------------------------------------------- /sample/000-dogs-app/nest-cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/nest-cli", 3 | "collection": "@nestjs/schematics", 4 | "sourceRoot": "src", 5 | "compilerOptions": { 6 | "deleteOutDir": true 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /sample/000-dogs-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sample-000-dogs-app", 3 | "version": "1.0.0", 4 | "description": "Nest TypeScript starter repository", 5 | "license": "MIT", 6 | "scripts": { 7 | "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", 8 | "start": "nest start", 9 | "start:dev": "nest start --watch", 10 | "start:debug": "nest start --debug --watch", 11 | "start:prod": "node dist/main", 12 | "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", 13 | "test": "jest", 14 | "test:watch": "jest --watch", 15 | "test:cov": "jest --coverage", 16 | "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", 17 | "test:e2e": "jest --config ./test/jest-e2e.json" 18 | }, 19 | "dependencies": { 20 | "@nestjs/common": "11.0.11", 21 | "@nestjs/core": "11.0.11", 22 | "@nestjs/platform-express": "11.0.11", 23 | "@nestjs/terminus": "11.0.0", 24 | "@nestjs/testing": "11.0.11", 25 | "reflect-metadata": "0.2.2", 26 | "rxjs": "7.8.2" 27 | }, 28 | "devDependencies": { 29 | "@types/jest": "29.5.14", 30 | "@types/node": "22.13.10", 31 | "@typescript-eslint/eslint-plugin": "6.21.0", 32 | "@typescript-eslint/parser": "6.21.0", 33 | "eslint": "8.57.1", 34 | "eslint-config-prettier": "10.0.1", 35 | "eslint-plugin-import": "2.31.0", 36 | "jest": "29.7.0", 37 | "supertest": "7.0.0", 38 | "ts-jest": "29.2.6", 39 | "ts-node": "10.9.2", 40 | "typescript": "5.7.3" 41 | }, 42 | "jest": { 43 | "moduleFileExtensions": [ 44 | "js", 45 | "json", 46 | "ts" 47 | ], 48 | "rootDir": "src", 49 | "testRegex": ".spec.ts$", 50 | "transform": { 51 | "^.+\\.(t|j)s$": "ts-jest" 52 | }, 53 | "collectCoverageFrom": [ 54 | "**/*.(t|j)s" 55 | ], 56 | "coverageDirectory": "../coverage", 57 | "testEnvironment": "node" 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /sample/000-dogs-app/src/app.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { HealthModule } from './health/health.module'; 3 | import { DogModule } from './dog/dog.module'; 4 | 5 | @Module({ 6 | imports: [HealthModule, DogModule], 7 | }) 8 | export class AppModule {} 9 | -------------------------------------------------------------------------------- /sample/000-dogs-app/src/dog/dog.health.spec.ts: -------------------------------------------------------------------------------- 1 | import { Test } from '@nestjs/testing'; 2 | import { DogHealthIndicator } from './dog.health'; 3 | import { DogService } from './dog.service'; 4 | import { HealthIndicatorService } from '@nestjs/terminus'; 5 | import { DogState } from './interfaces/dog.interface'; 6 | 7 | /////////////////////////////////////////////////////////// 8 | 9 | const dogServiceMock = { 10 | getDogs: jest.fn(), 11 | }; 12 | 13 | const healthIndicatorSessionMock = { 14 | up: jest.fn(), 15 | down: jest.fn(), 16 | }; 17 | 18 | const healthIndicatorServiceMock = { 19 | check: jest.fn().mockImplementation(() => healthIndicatorSessionMock), 20 | }; 21 | 22 | /////////////////////////////////////////////////////////// 23 | 24 | describe('DogHealthIndicator', () => { 25 | let dogHealthIndicator: DogHealthIndicator; 26 | 27 | beforeEach(async () => { 28 | const moduleRef = await Test.createTestingModule({ 29 | providers: [ 30 | DogHealthIndicator, 31 | { 32 | provide: DogService, 33 | useValue: dogServiceMock, 34 | }, 35 | { 36 | provide: HealthIndicatorService, 37 | useValue: healthIndicatorServiceMock, 38 | }, 39 | ], 40 | }).compile(); 41 | 42 | dogHealthIndicator = await moduleRef.resolve(DogHealthIndicator); 43 | }); 44 | 45 | it('marks the indicator as down if there are badboys', async () => { 46 | // Arrange 47 | dogServiceMock.getDogs.mockResolvedValue([ 48 | { name: 'Felix', state: DogState.BAD_BOY }, 49 | ]); 50 | 51 | // Act 52 | await dogHealthIndicator.isHealthy('dog'); 53 | 54 | // Assert 55 | expect(healthIndicatorSessionMock.down).toHaveBeenCalledWith({ 56 | badboys: 1, 57 | }); 58 | }); 59 | 60 | it('marks the indicator as up if there are no badboys', async () => { 61 | // Arrange 62 | dogServiceMock.getDogs.mockResolvedValue([ 63 | { name: 'Felix', state: DogState.GOOD_BOY }, 64 | ]); 65 | 66 | // Act 67 | await dogHealthIndicator.isHealthy('dog'); 68 | 69 | // Assert 70 | expect(healthIndicatorSessionMock.up).toHaveBeenCalled(); 71 | }); 72 | }); 73 | -------------------------------------------------------------------------------- /sample/000-dogs-app/src/dog/dog.health.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common'; 2 | import { DogService } from './dog.service'; 3 | import { DogState } from './interfaces/dog.interface'; 4 | import { HealthIndicatorService } from '@nestjs/terminus'; 5 | 6 | @Injectable() 7 | export class DogHealthIndicator { 8 | constructor( 9 | private readonly dogService: DogService, 10 | private readonly healthIndicatorService: HealthIndicatorService, 11 | ) {} 12 | 13 | async isHealthy(key: TKey) { 14 | const indicator = this.healthIndicatorService.check(key); 15 | 16 | const dogs = await this.dogService.getDogs(); 17 | const badboys = dogs.filter((dog) => dog.state === DogState.BAD_BOY); 18 | const isHealthy = badboys.length === 0; 19 | 20 | if (!isHealthy) { 21 | return indicator.down({ 22 | badboys: badboys.length, 23 | }); 24 | } 25 | 26 | return indicator.up(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /sample/000-dogs-app/src/dog/dog.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { DogService } from './dog.service'; 3 | import { DogHealthIndicator } from './dog.health'; 4 | import { TerminusModule } from '@nestjs/terminus'; 5 | 6 | @Module({ 7 | imports: [TerminusModule], 8 | providers: [DogService, DogHealthIndicator], 9 | exports: [DogHealthIndicator], 10 | }) 11 | export class DogModule {} 12 | -------------------------------------------------------------------------------- /sample/000-dogs-app/src/dog/dog.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common'; 2 | import { DogState, Dog } from './interfaces/dog.interface'; 3 | 4 | @Injectable() 5 | export class DogService { 6 | private dogs: Dog[] = [ 7 | { name: 'Felix', state: DogState.GOOD_BOY }, 8 | { name: 'Fido', state: DogState.GOOD_BOY }, 9 | { name: 'Jazz', state: DogState.GOOD_BOY }, 10 | { name: 'Sweetheart', state: DogState.GOOD_BOY }, 11 | { name: 'Buttercup II', state: DogState.GOOD_BOY }, 12 | ]; 13 | 14 | public async getDogs(): Promise { 15 | return this.dogs; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /sample/000-dogs-app/src/dog/interfaces/dog.interface.ts: -------------------------------------------------------------------------------- 1 | export enum DogState { 2 | GOOD_BOY, 3 | BAD_BOY, 4 | } 5 | 6 | export interface Dog { 7 | name: string; 8 | state: DogState; 9 | } 10 | -------------------------------------------------------------------------------- /sample/000-dogs-app/src/health/health.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Get } from '@nestjs/common'; 2 | import { DogHealthIndicator } from '../dog/dog.health'; 3 | import { HealthCheck, HealthCheckService } from '@nestjs/terminus'; 4 | 5 | @Controller('health') 6 | export class HealthController { 7 | constructor( 8 | private health: HealthCheckService, 9 | private dogHealthIndicator: DogHealthIndicator, 10 | ) {} 11 | 12 | @Get() 13 | @HealthCheck() 14 | check() { 15 | return this.health.check([ 16 | async () => this.dogHealthIndicator.isHealthy('dog'), 17 | ]); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /sample/000-dogs-app/src/health/health.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { TerminusModule } from '@nestjs/terminus'; 3 | import { DogModule } from '../dog/dog.module'; 4 | import { HealthController } from './health.controller'; 5 | 6 | @Module({ 7 | imports: [TerminusModule, DogModule], 8 | controllers: [HealthController], 9 | }) 10 | export class HealthModule {} 11 | -------------------------------------------------------------------------------- /sample/000-dogs-app/src/main.ts: -------------------------------------------------------------------------------- 1 | import { NestFactory } from '@nestjs/core'; 2 | import { AppModule } from './app.module'; 3 | 4 | async function bootstrap() { 5 | const app = await NestFactory.create(AppModule); 6 | await app.listen(3000); 7 | } 8 | bootstrap(); 9 | -------------------------------------------------------------------------------- /sample/000-dogs-app/test/health.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import * as request from 'supertest'; 2 | import { Test } from '@nestjs/testing'; 3 | import { HealthModule } from '../src/health/health.module'; 4 | import { INestApplication } from '@nestjs/common'; 5 | 6 | describe('HealthModule (e2e)', () => { 7 | let app: INestApplication; 8 | 9 | beforeAll(async () => { 10 | const moduleFixture = await Test.createTestingModule({ 11 | imports: [HealthModule], 12 | }).compile(); 13 | 14 | app = moduleFixture.createNestApplication(); 15 | await app.init(); 16 | }); 17 | 18 | it('/health (GET)', () => { 19 | return request(app.getHttpServer()) 20 | .get('/health') 21 | .expect(200) 22 | .expect({ 23 | status: 'ok', 24 | info: { dog: { status: 'up' } }, 25 | error: {}, 26 | details: { dog: { status: 'up' } }, 27 | }); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /sample/000-dogs-app/test/jest-e2e.json: -------------------------------------------------------------------------------- 1 | { 2 | "moduleFileExtensions": ["js", "json", "ts"], 3 | "rootDir": ".", 4 | "testEnvironment": "node", 5 | "testRegex": ".e2e-spec.ts$", 6 | "transform": { 7 | "^.+\\.(t|j)s$": "ts-jest" 8 | } 9 | } -------------------------------------------------------------------------------- /sample/000-dogs-app/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "declaration": false, 5 | "noImplicitAny": false, 6 | "removeComments": true, 7 | "noLib": false, 8 | "emitDecoratorMetadata": true, 9 | "experimentalDecorators": true, 10 | "target": "es6", 11 | "sourceMap": true, 12 | "allowJs": true, 13 | "outDir": "./dist", 14 | "skipLibCheck": true 15 | }, 16 | "include": [ 17 | "src/**/*" 18 | ], 19 | "exclude": [ 20 | "node_modules", 21 | "**/*.spec.ts" 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /sample/001-mongoose-app/.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 | -------------------------------------------------------------------------------- /sample/001-mongoose-app/.gitignore: -------------------------------------------------------------------------------- 1 | # File created using '.gitignore Generator' for Visual Studio Code: https://bit.ly/vscode-gig 2 | 3 | # Created by https://www.gitignore.io/api/linux,visualstudiocode,node 4 | 5 | ### Linux ### 6 | *~ 7 | 8 | # temporary files which can be created if a process still has a handle open of a deleted file 9 | .fuse_hidden* 10 | 11 | # KDE directory preferences 12 | .directory 13 | 14 | # Linux trash folder which might appear on any partition or disk 15 | .Trash-* 16 | 17 | # .nfs files are created when an open file is removed but is still being accessed 18 | .nfs* 19 | 20 | ### Node ### 21 | # Logs 22 | logs 23 | *.log 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # Runtime data 29 | pids 30 | *.pid 31 | *.seed 32 | *.pid.lock 33 | 34 | # Directory for instrumented libs generated by jscoverage/JSCover 35 | lib-cov 36 | 37 | # Coverage directory used by tools like istanbul 38 | coverage 39 | 40 | # nyc test coverage 41 | .nyc_output 42 | 43 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 44 | .grunt 45 | 46 | # Bower dependency directory (https://bower.io/) 47 | bower_components 48 | 49 | # node-waf configuration 50 | .lock-wscript 51 | 52 | # Compiled binary addons (https://nodejs.org/api/addons.html) 53 | build/Release 54 | 55 | # Dependency directories 56 | node_modules/ 57 | jspm_packages/ 58 | 59 | # TypeScript v1 declaration files 60 | typings/ 61 | 62 | # Optional npm cache directory 63 | .npm 64 | 65 | # Optional eslint cache 66 | .eslintcache 67 | 68 | # Optional REPL history 69 | .node_repl_history 70 | 71 | # Output of 'npm pack' 72 | *.tgz 73 | 74 | # Yarn Integrity file 75 | .yarn-integrity 76 | 77 | # dotenv environment variables file 78 | .env 79 | 80 | # parcel-bundler cache (https://parceljs.org/) 81 | .cache 82 | 83 | # next.js build output 84 | .next 85 | 86 | # nuxt.js build output 87 | .nuxt 88 | 89 | # vuepress build output 90 | .vuepress/dist 91 | 92 | # Serverless directories 93 | .serverless 94 | 95 | ### VisualStudioCode ### 96 | .vscode/* 97 | !.vscode/settings.json 98 | !.vscode/tasks.json 99 | !.vscode/launch.json 100 | !.vscode/extensions.json 101 | 102 | 103 | # End of https://www.gitignore.io/api/linux,visualstudiocode,node 104 | 105 | # Custom rules (everything added below won't be overriden by 'Generate .gitignore File' if you use 'Update' option) 106 | 107 | -------------------------------------------------------------------------------- /sample/001-mongoose-app/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | services: 4 | mongodb: 5 | image: mongo:latest 6 | environment: 7 | - MONGODB_DATABASE="test" 8 | ports: 9 | - 27017:27017 10 | -------------------------------------------------------------------------------- /sample/001-mongoose-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "001-mongoose-app", 3 | "version": "1.0.0", 4 | "description": "Mongoose Health Check sample app", 5 | "license": "MIT", 6 | "scripts": { 7 | "start": "ts-node src/main.ts", 8 | "prestart:prod": "tsc", 9 | "start:prod": "node dist/main.js", 10 | "build": "rm -rf dist && tsc -p tsconfig.json", 11 | "test": "jest", 12 | "test:watch": "jest --watch --config=jest.json", 13 | "test:coverage": "jest --config=jest.json --coverage --coverageDirectory=coverage", 14 | "e2e": "echo 'No e2e tests implemented yet.'", 15 | "e2e:watch": "jest --watch --config=e2e/jest-e2e.json" 16 | }, 17 | "dependencies": { 18 | "@nestjs/common": "11.0.11", 19 | "@nestjs/core": "11.0.11", 20 | "@nestjs/mongoose": "11.0.1", 21 | "@nestjs/platform-express": "11.0.11", 22 | "@nestjs/terminus": "11.0.0", 23 | "mongoose": "8.10.1", 24 | "reflect-metadata": "0.2.2", 25 | "rxjs": "7.8.2" 26 | }, 27 | "devDependencies": { 28 | "@nestjs/testing": "11.0.11", 29 | "@types/express": "5.0.0", 30 | "@types/jest": "29.5.14", 31 | "@types/node": "22.13.10", 32 | "@types/supertest": "6.0.2", 33 | "@typescript-eslint/eslint-plugin": "6.21.0", 34 | "@typescript-eslint/parser": "6.21.0", 35 | "eslint": "8.57.1", 36 | "eslint-config-prettier": "10.0.1", 37 | "eslint-plugin-import": "2.31.0", 38 | "jest": "29.7.0", 39 | "nodemon": "3.1.9", 40 | "prettier": "3.5.3", 41 | "supertest": "7.0.0", 42 | "ts-jest": "29.2.6", 43 | "ts-loader": "9.5.2", 44 | "ts-node": "10.9.2", 45 | "tsconfig-paths": "4.2.0", 46 | "typescript": "5.7.3" 47 | }, 48 | "jest": { 49 | "moduleFileExtensions": [ 50 | "js", 51 | "json", 52 | "ts" 53 | ], 54 | "rootDir": "src", 55 | "testRegex": ".spec.ts$", 56 | "transform": { 57 | "^.+\\.(t|j)s$": "ts-jest" 58 | }, 59 | "coverageDirectory": "../coverage", 60 | "testEnvironment": "node" 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /sample/001-mongoose-app/src/app.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { HealthModule } from './health/health.module'; 3 | 4 | @Module({ 5 | imports: [HealthModule], 6 | }) 7 | export class AppModule {} 8 | -------------------------------------------------------------------------------- /sample/001-mongoose-app/src/health/health.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Get } from '@nestjs/common'; 2 | import { 3 | HealthCheck, 4 | HealthCheckService, 5 | MongooseHealthIndicator, 6 | } from '@nestjs/terminus'; 7 | 8 | @Controller('health') 9 | export class HealthController { 10 | constructor( 11 | private health: HealthCheckService, 12 | private mongoose: MongooseHealthIndicator, 13 | ) {} 14 | 15 | @Get() 16 | @HealthCheck() 17 | check() { 18 | return this.health.check([async () => this.mongoose.pingCheck('mongoose')]); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /sample/001-mongoose-app/src/health/health.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { MongooseModule } from '@nestjs/mongoose'; 3 | import { TerminusModule } from '@nestjs/terminus'; 4 | import { HealthController } from './health.controller'; 5 | 6 | @Module({ 7 | imports: [ 8 | TerminusModule.forRoot(), 9 | MongooseModule.forRoot('mongodb://localhost:27017/test'), 10 | ], 11 | controllers: [HealthController], 12 | }) 13 | export class HealthModule {} 14 | -------------------------------------------------------------------------------- /sample/001-mongoose-app/src/main.ts: -------------------------------------------------------------------------------- 1 | import { NestFactory } from '@nestjs/core'; 2 | import { AppModule } from './app.module'; 3 | 4 | async function bootstrap() { 5 | const app = await NestFactory.create(AppModule); 6 | await app.listen(3000); 7 | } 8 | 9 | bootstrap(); 10 | -------------------------------------------------------------------------------- /sample/001-mongoose-app/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "declaration": true, 5 | "noImplicitAny": false, 6 | "removeComments": true, 7 | "noLib": false, 8 | "allowSyntheticDefaultImports": true, 9 | "emitDecoratorMetadata": true, 10 | "experimentalDecorators": true, 11 | "target": "es6", 12 | "sourceMap": true, 13 | "outDir": "./dist", 14 | "baseUrl": "./", 15 | "skipLibCheck": true, 16 | }, 17 | "include": [ 18 | "src/**/*" 19 | ], 20 | "exclude": [ 21 | "node_modules", 22 | "**/*.spec.ts" 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /sample/002-microservice-app/.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 | -------------------------------------------------------------------------------- /sample/002-microservice-app/.gitignore: -------------------------------------------------------------------------------- 1 | # File created using '.gitignore Generator' for Visual Studio Code: https://bit.ly/vscode-gig 2 | 3 | # Created by https://www.gitignore.io/api/linux,visualstudiocode,node 4 | 5 | ### Linux ### 6 | *~ 7 | 8 | # temporary files which can be created if a process still has a handle open of a deleted file 9 | .fuse_hidden* 10 | 11 | # KDE directory preferences 12 | .directory 13 | 14 | # Linux trash folder which might appear on any partition or disk 15 | .Trash-* 16 | 17 | # .nfs files are created when an open file is removed but is still being accessed 18 | .nfs* 19 | 20 | ### Node ### 21 | # Logs 22 | logs 23 | *.log 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # Runtime data 29 | pids 30 | *.pid 31 | *.seed 32 | *.pid.lock 33 | 34 | # Directory for instrumented libs generated by jscoverage/JSCover 35 | lib-cov 36 | 37 | # Coverage directory used by tools like istanbul 38 | coverage 39 | 40 | # nyc test coverage 41 | .nyc_output 42 | 43 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 44 | .grunt 45 | 46 | # Bower dependency directory (https://bower.io/) 47 | bower_components 48 | 49 | # node-waf configuration 50 | .lock-wscript 51 | 52 | # Compiled binary addons (https://nodejs.org/api/addons.html) 53 | build/Release 54 | 55 | # Dependency directories 56 | node_modules/ 57 | jspm_packages/ 58 | 59 | # TypeScript v1 declaration files 60 | typings/ 61 | 62 | # Optional npm cache directory 63 | .npm 64 | 65 | # Optional eslint cache 66 | .eslintcache 67 | 68 | # Optional REPL history 69 | .node_repl_history 70 | 71 | # Output of 'npm pack' 72 | *.tgz 73 | 74 | # Yarn Integrity file 75 | .yarn-integrity 76 | 77 | # dotenv environment variables file 78 | .env 79 | 80 | # parcel-bundler cache (https://parceljs.org/) 81 | .cache 82 | 83 | # next.js build output 84 | .next 85 | 86 | # nuxt.js build output 87 | .nuxt 88 | 89 | # vuepress build output 90 | .vuepress/dist 91 | 92 | # Serverless directories 93 | .serverless 94 | 95 | ### VisualStudioCode ### 96 | .vscode/* 97 | !.vscode/settings.json 98 | !.vscode/tasks.json 99 | !.vscode/launch.json 100 | !.vscode/extensions.json 101 | 102 | 103 | # End of https://www.gitignore.io/api/linux,visualstudiocode,node 104 | 105 | # Custom rules (everything added below won't be overriden by 'Generate .gitignore File' if you use 'Update' option) 106 | 107 | -------------------------------------------------------------------------------- /sample/002-microservice-app/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | services: 4 | redis: 5 | container_name: test-redis 6 | image: redis 7 | ports: 8 | - "6379:6379" 9 | restart: always 10 | -------------------------------------------------------------------------------- /sample/002-microservice-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "002-microservice-app", 3 | "version": "1.0.0", 4 | "description": "Microservice ping check app", 5 | "license": "MIT", 6 | "scripts": { 7 | "start": "ts-node src/main.ts", 8 | "prestart:prod": "tsc", 9 | "start:prod": "node dist/main.js", 10 | "build": "rm -rf dist && tsc -p tsconfig.json", 11 | "test": "jest", 12 | "test:watch": "jest --watch --config=jest.json", 13 | "test:coverage": "jest --config=jest.json --coverage --coverageDirectory=coverage", 14 | "e2e": "echo 'No e2e tests implemented yet.'", 15 | "e2e:watch": "jest --watch --config=e2e/jest-e2e.json" 16 | }, 17 | "dependencies": { 18 | "@nestjs/common": "11.0.11", 19 | "@nestjs/core": "11.0.11", 20 | "@nestjs/microservices": "11.0.11", 21 | "@nestjs/platform-express": "11.0.11", 22 | "@nestjs/terminus": "11.0.0", 23 | "class-transformer": "0.5.1", 24 | "class-validator": "0.14.1", 25 | "ioredis": "5.4.2", 26 | "lodash": "4.17.21", 27 | "reflect-metadata": "0.2.2", 28 | "rxjs": "7.8.2" 29 | }, 30 | "devDependencies": { 31 | "@types/jest": "29.5.14", 32 | "@types/node": "22.13.10", 33 | "@typescript-eslint/eslint-plugin": "6.21.0", 34 | "@typescript-eslint/parser": "6.21.0", 35 | "eslint": "8.57.1", 36 | "eslint-config-prettier": "10.0.1", 37 | "eslint-plugin-import": "2.31.0", 38 | "jest": "29.7.0", 39 | "supertest": "7.0.0", 40 | "ts-jest": "29.2.6", 41 | "ts-node": "10.9.2", 42 | "typescript": "5.7.3" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /sample/002-microservice-app/src/app.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { HealthModule } from './health/health.module'; 3 | 4 | @Module({ 5 | imports: [HealthModule], 6 | }) 7 | export class AppModule {} 8 | -------------------------------------------------------------------------------- /sample/002-microservice-app/src/health/health.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Get } from '@nestjs/common'; 2 | import { 3 | RedisOptions, 4 | TcpClientOptions, 5 | Transport, 6 | } from '@nestjs/microservices'; 7 | import { 8 | HealthCheck, 9 | HealthCheckService, 10 | MicroserviceHealthIndicator, 11 | } from '@nestjs/terminus'; 12 | 13 | @Controller('health') 14 | export class HealthController { 15 | constructor( 16 | private health: HealthCheckService, 17 | private microservice: MicroserviceHealthIndicator, 18 | ) {} 19 | 20 | @Get() 21 | @HealthCheck() 22 | check() { 23 | return this.health.check([ 24 | async () => 25 | this.microservice.pingCheck('tcp', { 26 | transport: Transport.TCP, 27 | options: { host: 'localhost', port: 8889 }, 28 | }), 29 | async () => 30 | this.microservice.pingCheck('redis', { 31 | transport: Transport.REDIS, 32 | options: { 33 | host: 'localhost', 34 | port: 6379, 35 | }, 36 | }), 37 | ]); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /sample/002-microservice-app/src/health/health.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { TerminusModule } from '@nestjs/terminus'; 3 | import { HealthController } from './health.controller'; 4 | 5 | @Module({ 6 | imports: [TerminusModule], 7 | controllers: [HealthController], 8 | }) 9 | export class HealthModule {} 10 | -------------------------------------------------------------------------------- /sample/002-microservice-app/src/main.ts: -------------------------------------------------------------------------------- 1 | import { NestFactory } from '@nestjs/core'; 2 | import { AppModule } from './app.module'; 3 | import { Transport } from '@nestjs/microservices'; 4 | 5 | async function bootstrap() { 6 | const app = await NestFactory.create(AppModule); 7 | app.connectMicroservice({ 8 | transport: Transport.TCP, 9 | options: { 10 | host: 'localhost', 11 | port: 8889, 12 | }, 13 | }); 14 | await app.startAllMicroservices(); 15 | await app.listen(3000); 16 | } 17 | bootstrap(); 18 | -------------------------------------------------------------------------------- /sample/002-microservice-app/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "declaration": false, 5 | "noImplicitAny": false, 6 | "removeComments": true, 7 | "noLib": false, 8 | "emitDecoratorMetadata": true, 9 | "experimentalDecorators": true, 10 | "target": "es6", 11 | "sourceMap": true, 12 | "allowJs": true, 13 | "outDir": "./dist", 14 | "skipLibCheck": true, 15 | }, 16 | "include": [ 17 | "src/**/*" 18 | ], 19 | "exclude": [ 20 | "node_modules", 21 | "**/*.spec.ts" 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /sample/003-memory-app/.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 | -------------------------------------------------------------------------------- /sample/003-memory-app/.gitignore: -------------------------------------------------------------------------------- 1 | # File created using '.gitignore Generator' for Visual Studio Code: https://bit.ly/vscode-gig 2 | 3 | # Created by https://www.gitignore.io/api/linux,visualstudiocode,node 4 | 5 | ### Linux ### 6 | *~ 7 | 8 | # temporary files which can be created if a process still has a handle open of a deleted file 9 | .fuse_hidden* 10 | 11 | # KDE directory preferences 12 | .directory 13 | 14 | # Linux trash folder which might appear on any partition or disk 15 | .Trash-* 16 | 17 | # .nfs files are created when an open file is removed but is still being accessed 18 | .nfs* 19 | 20 | ### Node ### 21 | # Logs 22 | logs 23 | *.log 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # Runtime data 29 | pids 30 | *.pid 31 | *.seed 32 | *.pid.lock 33 | 34 | # Directory for instrumented libs generated by jscoverage/JSCover 35 | lib-cov 36 | 37 | # Coverage directory used by tools like istanbul 38 | coverage 39 | 40 | # nyc test coverage 41 | .nyc_output 42 | 43 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 44 | .grunt 45 | 46 | # Bower dependency directory (https://bower.io/) 47 | bower_components 48 | 49 | # node-waf configuration 50 | .lock-wscript 51 | 52 | # Compiled binary addons (https://nodejs.org/api/addons.html) 53 | build/Release 54 | 55 | # Dependency directories 56 | node_modules/ 57 | jspm_packages/ 58 | 59 | # TypeScript v1 declaration files 60 | typings/ 61 | 62 | # Optional npm cache directory 63 | .npm 64 | 65 | # Optional eslint cache 66 | .eslintcache 67 | 68 | # Optional REPL history 69 | .node_repl_history 70 | 71 | # Output of 'npm pack' 72 | *.tgz 73 | 74 | # Yarn Integrity file 75 | .yarn-integrity 76 | 77 | # dotenv environment variables file 78 | .env 79 | 80 | # parcel-bundler cache (https://parceljs.org/) 81 | .cache 82 | 83 | # next.js build output 84 | .next 85 | 86 | # nuxt.js build output 87 | .nuxt 88 | 89 | # vuepress build output 90 | .vuepress/dist 91 | 92 | # Serverless directories 93 | .serverless 94 | 95 | ### VisualStudioCode ### 96 | .vscode/* 97 | !.vscode/settings.json 98 | !.vscode/tasks.json 99 | !.vscode/launch.json 100 | !.vscode/extensions.json 101 | 102 | 103 | # End of https://www.gitignore.io/api/linux,visualstudiocode,node 104 | 105 | # Custom rules (everything added below won't be overriden by 'Generate .gitignore File' if you use 'Update' option) 106 | 107 | -------------------------------------------------------------------------------- /sample/003-memory-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "003-memory-app", 3 | "version": "1.0.0", 4 | "description": "Memory Health Check sample app", 5 | "license": "MIT", 6 | "scripts": { 7 | "build": "rm -rf dist && tsc -p tsconfig.json", 8 | "e2e": "echo 'No e2e tests implemented yet.'", 9 | "e2e:watch": "jest --watch --config=e2e/jest-e2e.json", 10 | "start": "ts-node src/main.ts", 11 | "prestart:prod": "tsc", 12 | "start:prod": "node dist/main.js", 13 | "test": "jest", 14 | "test:coverage": "jest --config=jest.json --coverage --coverageDirectory=coverage", 15 | "test:watch": "jest --watch --config=jest.json" 16 | }, 17 | "dependencies": { 18 | "@nestjs/common": "11.0.11", 19 | "@nestjs/core": "11.0.11", 20 | "@nestjs/platform-express": "11.0.11", 21 | "@nestjs/terminus": "11.0.0", 22 | "class-transformer": "0.5.1", 23 | "class-validator": "0.14.1", 24 | "lodash": "4.17.21", 25 | "reflect-metadata": "0.2.2", 26 | "rxjs": "7.8.2" 27 | }, 28 | "devDependencies": { 29 | "@types/jest": "29.5.14", 30 | "@types/node": "22.13.10", 31 | "@typescript-eslint/eslint-plugin": "6.21.0", 32 | "@typescript-eslint/parser": "6.21.0", 33 | "eslint": "8.57.1", 34 | "eslint-config-prettier": "10.0.1", 35 | "eslint-plugin-import": "2.31.0", 36 | "jest": "29.7.0", 37 | "supertest": "7.0.0", 38 | "ts-jest": "29.2.6", 39 | "ts-node": "10.9.2", 40 | "typescript": "5.7.3" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /sample/003-memory-app/src/app.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { HealthModule } from './health/health.module'; 3 | 4 | @Module({ 5 | imports: [HealthModule], 6 | }) 7 | export class AppModule {} 8 | -------------------------------------------------------------------------------- /sample/003-memory-app/src/health/health.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Get } from '@nestjs/common'; 2 | import { 3 | HealthCheck, 4 | HealthCheckService, 5 | MemoryHealthIndicator, 6 | } from '@nestjs/terminus'; 7 | 8 | @Controller('health') 9 | export class HealthController { 10 | constructor( 11 | private health: HealthCheckService, 12 | private memory: MemoryHealthIndicator, 13 | ) {} 14 | 15 | @Get() 16 | @HealthCheck() 17 | healthCheck() { 18 | return this.health.check([ 19 | async () => this.memory.checkHeap('memory_heap', 200 * 1024 * 1024), 20 | async () => this.memory.checkRSS('memory_rss', 3000 * 1024 * 1024), 21 | ]); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /sample/003-memory-app/src/health/health.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { TerminusModule } from '@nestjs/terminus'; 3 | import { HealthController } from './health.controller'; 4 | 5 | @Module({ 6 | imports: [TerminusModule], 7 | controllers: [HealthController], 8 | }) 9 | export class HealthModule {} 10 | -------------------------------------------------------------------------------- /sample/003-memory-app/src/main.ts: -------------------------------------------------------------------------------- 1 | import { NestFactory } from '@nestjs/core'; 2 | import { AppModule } from './app.module'; 3 | 4 | async function bootstrap() { 5 | const app = await NestFactory.create(AppModule); 6 | await app.listen(3000); 7 | } 8 | 9 | bootstrap(); 10 | -------------------------------------------------------------------------------- /sample/003-memory-app/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "declaration": false, 5 | "noImplicitAny": false, 6 | "removeComments": true, 7 | "noLib": false, 8 | "emitDecoratorMetadata": true, 9 | "experimentalDecorators": true, 10 | "target": "es6", 11 | "sourceMap": true, 12 | "allowJs": true, 13 | "outDir": "./dist", 14 | "skipLibCheck": true, 15 | }, 16 | "include": [ 17 | "src/**/*" 18 | ], 19 | "exclude": [ 20 | "node_modules", 21 | "**/*.spec.ts" 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /sample/004-grpc-app/.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 | -------------------------------------------------------------------------------- /sample/004-grpc-app/.gitignore: -------------------------------------------------------------------------------- 1 | # File created using '.gitignore Generator' for Visual Studio Code: https://bit.ly/vscode-gig 2 | 3 | # Created by https://www.gitignore.io/api/linux,visualstudiocode,node 4 | 5 | ### Linux ### 6 | *~ 7 | 8 | # temporary files which can be created if a process still has a handle open of a deleted file 9 | .fuse_hidden* 10 | 11 | # KDE directory preferences 12 | .directory 13 | 14 | # Linux trash folder which might appear on any partition or disk 15 | .Trash-* 16 | 17 | # .nfs files are created when an open file is removed but is still being accessed 18 | .nfs* 19 | 20 | ### Node ### 21 | # Logs 22 | logs 23 | *.log 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # Runtime data 29 | pids 30 | *.pid 31 | *.seed 32 | *.pid.lock 33 | 34 | # Directory for instrumented libs generated by jscoverage/JSCover 35 | lib-cov 36 | 37 | # Coverage directory used by tools like istanbul 38 | coverage 39 | 40 | # nyc test coverage 41 | .nyc_output 42 | 43 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 44 | .grunt 45 | 46 | # Bower dependency directory (https://bower.io/) 47 | bower_components 48 | 49 | # node-waf configuration 50 | .lock-wscript 51 | 52 | # Compiled binary addons (https://nodejs.org/api/addons.html) 53 | build/Release 54 | 55 | # Dependency directories 56 | node_modules/ 57 | jspm_packages/ 58 | 59 | # TypeScript v1 declaration files 60 | typings/ 61 | 62 | # Optional npm cache directory 63 | .npm 64 | 65 | # Optional eslint cache 66 | .eslintcache 67 | 68 | # Optional REPL history 69 | .node_repl_history 70 | 71 | # Output of 'npm pack' 72 | *.tgz 73 | 74 | # Yarn Integrity file 75 | .yarn-integrity 76 | 77 | # dotenv environment variables file 78 | .env 79 | 80 | # parcel-bundler cache (https://parceljs.org/) 81 | .cache 82 | 83 | # next.js build output 84 | .next 85 | 86 | # nuxt.js build output 87 | .nuxt 88 | 89 | # vuepress build output 90 | .vuepress/dist 91 | 92 | # Serverless directories 93 | .serverless 94 | 95 | ### VisualStudioCode ### 96 | .vscode/* 97 | !.vscode/settings.json 98 | !.vscode/tasks.json 99 | !.vscode/launch.json 100 | !.vscode/extensions.json 101 | 102 | 103 | # End of https://www.gitignore.io/api/linux,visualstudiocode,node 104 | 105 | # Custom rules (everything added below won't be overriden by 'Generate .gitignore File' if you use 'Update' option) 106 | 107 | -------------------------------------------------------------------------------- /sample/004-grpc-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "004-grpc-app", 3 | "version": "1.0.0", 4 | "description": "GRPC ping check app", 5 | "license": "MIT", 6 | "scripts": { 7 | "build": "rm -rf dist && tsc -p tsconfig.json", 8 | "e2e:watch": "jest --watch --config=e2e/jest-e2e.json", 9 | "start": "concurrently -n client,server \"npm:start:client\" \"npm:start:server\" --prefix-colors blue,yellow", 10 | "start:client": "ts-node src/client/main.ts", 11 | "prestart:prod": "tsc", 12 | "start:prod": "node dist/main.js", 13 | "start:server": "ts-node src/server/main.ts", 14 | "test": "jest", 15 | "test:coverage": "jest --config=jest.json --coverage --coverageDirectory=coverage", 16 | "e2e": "echo 'No e2e tests implemented yet.'", 17 | "test:watch": "jest --watch --config=jest.json" 18 | }, 19 | "dependencies": { 20 | "@grpc/grpc-js": "1.12.6", 21 | "@grpc/proto-loader": "0.7.13", 22 | "@nestjs/common": "11.0.11", 23 | "@nestjs/core": "11.0.11", 24 | "@nestjs/microservices": "11.0.11", 25 | "@nestjs/platform-express": "11.0.11", 26 | "@nestjs/terminus": "11.0.0", 27 | "class-transformer": "0.5.1", 28 | "class-validator": "0.14.1", 29 | "lodash": "4.17.21", 30 | "redis": "4.7.0", 31 | "reflect-metadata": "0.2.2", 32 | "rxjs": "7.8.2" 33 | }, 34 | "devDependencies": { 35 | "@types/jest": "29.5.14", 36 | "@types/node": "22.13.10", 37 | "@typescript-eslint/eslint-plugin": "6.21.0", 38 | "@typescript-eslint/parser": "6.21.0", 39 | "concurrently": "9.1.2", 40 | "eslint": "8.57.1", 41 | "eslint-config-prettier": "10.0.1", 42 | "eslint-plugin-import": "2.31.0", 43 | "jest": "29.7.0", 44 | "supertest": "7.0.0", 45 | "ts-jest": "29.2.6", 46 | "ts-node": "10.9.2", 47 | "typescript": "5.7.3" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /sample/004-grpc-app/src/client/app.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { HealthModule } from './health/health.module'; 3 | 4 | @Module({ 5 | imports: [HealthModule], 6 | }) 7 | export class AppModule {} 8 | -------------------------------------------------------------------------------- /sample/004-grpc-app/src/client/health/health.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Get } from '@nestjs/common'; 2 | import { GrpcOptions } from '@nestjs/microservices'; 3 | import { 4 | HealthCheck, 5 | HealthCheckService, 6 | GRPCHealthIndicator, 7 | } from '@nestjs/terminus'; 8 | 9 | @Controller('health') 10 | export class HealthController { 11 | constructor( 12 | private health: HealthCheckService, 13 | private grpc: GRPCHealthIndicator, 14 | ) {} 15 | 16 | @Get() 17 | @HealthCheck() 18 | check() { 19 | return this.health.check([ 20 | async () => 21 | this.grpc.checkService('hero_service', 'hero.health.v1', { 22 | timeout: 2000, 23 | }), 24 | ]); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /sample/004-grpc-app/src/client/health/health.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { TerminusModule } from '@nestjs/terminus'; 3 | import { HealthController } from './health.controller'; 4 | 5 | @Module({ 6 | imports: [TerminusModule], 7 | controllers: [HealthController], 8 | }) 9 | export class HealthModule {} 10 | -------------------------------------------------------------------------------- /sample/004-grpc-app/src/client/main.ts: -------------------------------------------------------------------------------- 1 | import { NestFactory } from '@nestjs/core'; 2 | import { AppModule } from './app.module'; 3 | 4 | async function bootstrap() { 5 | const app = await NestFactory.create(AppModule); 6 | await app.listen(3000); 7 | } 8 | bootstrap(); 9 | -------------------------------------------------------------------------------- /sample/004-grpc-app/src/protos/health.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package grpc.health.v1; 4 | 5 | message HealthCheckRequest { 6 | string service = 1; 7 | } 8 | 9 | message HealthCheckResponse { 10 | enum ServingStatus { 11 | UNKNOWN = 0; 12 | SERVING = 1; 13 | NOT_SERVING = 2; 14 | } 15 | ServingStatus status = 1; 16 | } 17 | 18 | service Health { 19 | rpc Check(HealthCheckRequest) returns (HealthCheckResponse); 20 | } -------------------------------------------------------------------------------- /sample/004-grpc-app/src/server/app.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { HealthController } from './health/health.controller'; 3 | 4 | @Module({ 5 | controllers: [HealthController], 6 | }) 7 | export class AppModule {} 8 | -------------------------------------------------------------------------------- /sample/004-grpc-app/src/server/health/health.controller.ts: -------------------------------------------------------------------------------- 1 | import { GrpcMethod } from '@nestjs/microservices'; 2 | import { Controller } from '@nestjs/common'; 3 | 4 | enum ServingStatus { 5 | UNKNOWN = 0, 6 | SERVING = 1, 7 | NOT_SERVING = 2, 8 | } 9 | 10 | interface HealthCheckRequest { 11 | service: string; 12 | } 13 | 14 | interface HealthCheckResposne { 15 | status: ServingStatus; 16 | } 17 | 18 | @Controller() 19 | export class HealthController { 20 | @GrpcMethod('Health', 'Check') 21 | check(data: HealthCheckRequest, metadata: any): HealthCheckResposne { 22 | // TODO: Implement 23 | return { status: ServingStatus.SERVING }; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /sample/004-grpc-app/src/server/main.ts: -------------------------------------------------------------------------------- 1 | import { NestFactory } from '@nestjs/core'; 2 | import { AppModule } from './app.module'; 3 | import { join } from 'path'; 4 | import { Transport } from '@nestjs/microservices'; 5 | 6 | async function bootstrap() { 7 | const app = await NestFactory.createMicroservice(AppModule, { 8 | transport: Transport.GRPC, 9 | options: { 10 | package: 'grpc.health.v1', 11 | protoPath: join(__dirname, '../protos/health.proto'), 12 | }, 13 | }); 14 | app.listen(); 15 | console.log('Microservice is listening'); 16 | } 17 | bootstrap(); 18 | -------------------------------------------------------------------------------- /sample/004-grpc-app/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "declaration": false, 5 | "noImplicitAny": false, 6 | "removeComments": true, 7 | "noLib": false, 8 | "emitDecoratorMetadata": true, 9 | "experimentalDecorators": true, 10 | "target": "es6", 11 | "sourceMap": true, 12 | "allowJs": true, 13 | "outDir": "./dist", 14 | "skipLibCheck": true 15 | }, 16 | "include": [ 17 | "src/**/*" 18 | ], 19 | "exclude": [ 20 | "node_modules", 21 | "**/*.spec.ts" 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /sample/005-typeorm-app/.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 | -------------------------------------------------------------------------------- /sample/005-typeorm-app/.gitignore: -------------------------------------------------------------------------------- 1 | # File created using '.gitignore Generator' for Visual Studio Code: https://bit.ly/vscode-gig 2 | 3 | # Created by https://www.gitignore.io/api/linux,visualstudiocode,node 4 | 5 | ### Linux ### 6 | *~ 7 | 8 | # temporary files which can be created if a process still has a handle open of a deleted file 9 | .fuse_hidden* 10 | 11 | # KDE directory preferences 12 | .directory 13 | 14 | # Linux trash folder which might appear on any partition or disk 15 | .Trash-* 16 | 17 | # .nfs files are created when an open file is removed but is still being accessed 18 | .nfs* 19 | 20 | ### Node ### 21 | # Logs 22 | logs 23 | *.log 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # Runtime data 29 | pids 30 | *.pid 31 | *.seed 32 | *.pid.lock 33 | 34 | # Directory for instrumented libs generated by jscoverage/JSCover 35 | lib-cov 36 | 37 | # Coverage directory used by tools like istanbul 38 | coverage 39 | 40 | # nyc test coverage 41 | .nyc_output 42 | 43 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 44 | .grunt 45 | 46 | # Bower dependency directory (https://bower.io/) 47 | bower_components 48 | 49 | # node-waf configuration 50 | .lock-wscript 51 | 52 | # Compiled binary addons (https://nodejs.org/api/addons.html) 53 | build/Release 54 | 55 | # Dependency directories 56 | node_modules/ 57 | jspm_packages/ 58 | 59 | # TypeScript v1 declaration files 60 | typings/ 61 | 62 | # Optional npm cache directory 63 | .npm 64 | 65 | # Optional eslint cache 66 | .eslintcache 67 | 68 | # Optional REPL history 69 | .node_repl_history 70 | 71 | # Output of 'npm pack' 72 | *.tgz 73 | 74 | # Yarn Integrity file 75 | .yarn-integrity 76 | 77 | # dotenv environment variables file 78 | .env 79 | 80 | # parcel-bundler cache (https://parceljs.org/) 81 | .cache 82 | 83 | # next.js build output 84 | .next 85 | 86 | # nuxt.js build output 87 | .nuxt 88 | 89 | # vuepress build output 90 | .vuepress/dist 91 | 92 | # Serverless directories 93 | .serverless 94 | 95 | ### VisualStudioCode ### 96 | .vscode/* 97 | !.vscode/settings.json 98 | !.vscode/tasks.json 99 | !.vscode/launch.json 100 | !.vscode/extensions.json 101 | 102 | 103 | # End of https://www.gitignore.io/api/linux,visualstudiocode,node 104 | 105 | # Custom rules (everything added below won't be overriden by 'Generate .gitignore File' if you use 'Update' option) 106 | 107 | -------------------------------------------------------------------------------- /sample/005-typeorm-app/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | services: 4 | mysql: 5 | image: mysql:9 6 | restart: always 7 | environment: 8 | MYSQL_ROOT_PASSWORD: root 9 | MYSQL_DATABASE: test 10 | ports: 11 | - "3306:3306" 12 | -------------------------------------------------------------------------------- /sample/005-typeorm-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "005-typeorm-app", 3 | "version": "1.0.0", 4 | "description": "Typeorm Health Check sample app", 5 | "license": "MIT", 6 | "scripts": { 7 | "start": "ts-node src/main.ts", 8 | "prestart:prod": "tsc", 9 | "start:prod": "node dist/main.js", 10 | "build": "rm -rf dist && tsc -p tsconfig.json", 11 | "test": "jest", 12 | "test:watch": "jest --watch --config=jest.json", 13 | "test:coverage": "jest --config=jest.json --coverage --coverageDirectory=coverage", 14 | "e2e": "echo 'No e2e tests implemented yet.'", 15 | "e2e:watch": "jest --watch --config=e2e/jest-e2e.json" 16 | }, 17 | "dependencies": { 18 | "@nestjs/common": "11.0.11", 19 | "@nestjs/core": "11.0.11", 20 | "@nestjs/platform-express": "11.0.11", 21 | "@nestjs/terminus": "11.0.0", 22 | "@nestjs/typeorm": "11.0.0", 23 | "mysql": "2.18.1", 24 | "reflect-metadata": "0.2.2", 25 | "rxjs": "7.8.2", 26 | "typeorm": "0.3.22" 27 | }, 28 | "devDependencies": { 29 | "@nestjs/testing": "11.0.11", 30 | "@types/express": "5.0.0", 31 | "@types/jest": "29.5.14", 32 | "@types/node": "22.13.10", 33 | "@types/supertest": "6.0.2", 34 | "@typescript-eslint/eslint-plugin": "6.21.0", 35 | "@typescript-eslint/parser": "6.21.0", 36 | "eslint": "8.57.1", 37 | "eslint-config-prettier": "10.0.1", 38 | "eslint-plugin-import": "2.31.0", 39 | "jest": "29.7.0", 40 | "nodemon": "3.1.9", 41 | "prettier": "3.5.3", 42 | "supertest": "7.0.0", 43 | "ts-jest": "29.2.6", 44 | "ts-loader": "9.5.2", 45 | "ts-node": "10.9.2", 46 | "tsconfig-paths": "4.2.0", 47 | "typescript": "5.7.3" 48 | }, 49 | "jest": { 50 | "moduleFileExtensions": [ 51 | "js", 52 | "json", 53 | "ts" 54 | ], 55 | "rootDir": "src", 56 | "testRegex": ".spec.ts$", 57 | "transform": { 58 | "^.+\\.(t|j)s$": "ts-jest" 59 | }, 60 | "coverageDirectory": "../coverage", 61 | "testEnvironment": "node" 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /sample/005-typeorm-app/src/app.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { HealthModule } from './health/health.module'; 3 | 4 | @Module({ 5 | imports: [HealthModule], 6 | }) 7 | export class AppModule {} 8 | -------------------------------------------------------------------------------- /sample/005-typeorm-app/src/health/health.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Get } from '@nestjs/common'; 2 | import { 3 | HealthCheck, 4 | HealthCheckService, 5 | TypeOrmHealthIndicator, 6 | } from '@nestjs/terminus'; 7 | 8 | @Controller('health') 9 | export class HealthController { 10 | constructor( 11 | private health: HealthCheckService, 12 | private db: TypeOrmHealthIndicator, 13 | ) {} 14 | 15 | @Get() 16 | @HealthCheck() 17 | check() { 18 | return this.health.check([async () => this.db.pingCheck('typeorm')]); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /sample/005-typeorm-app/src/health/health.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { TypeOrmModule } from '@nestjs/typeorm'; 3 | import { TerminusModule } from '@nestjs/terminus'; 4 | import { HealthController } from './health.controller'; 5 | 6 | @Module({ 7 | imports: [ 8 | TypeOrmModule.forRoot({ 9 | type: 'mysql', 10 | host: 'localhost', 11 | port: 3306, 12 | username: 'root', 13 | password: 'root', 14 | database: 'test', 15 | autoLoadEntities: true, 16 | synchronize: true, 17 | }), 18 | TerminusModule, 19 | ], 20 | controllers: [HealthController], 21 | }) 22 | export class HealthModule {} 23 | -------------------------------------------------------------------------------- /sample/005-typeorm-app/src/main.ts: -------------------------------------------------------------------------------- 1 | import { NestFactory } from '@nestjs/core'; 2 | import { AppModule } from './app.module'; 3 | 4 | async function bootstrap() { 5 | const app = await NestFactory.create(AppModule); 6 | await app.listen(3000); 7 | } 8 | 9 | bootstrap(); 10 | -------------------------------------------------------------------------------- /sample/005-typeorm-app/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "declaration": true, 5 | "noImplicitAny": false, 6 | "removeComments": true, 7 | "noLib": false, 8 | "allowSyntheticDefaultImports": true, 9 | "emitDecoratorMetadata": true, 10 | "experimentalDecorators": true, 11 | "target": "es6", 12 | "sourceMap": true, 13 | "outDir": "./dist", 14 | "baseUrl": "./", 15 | "skipLibCheck": true, 16 | }, 17 | "include": [ 18 | "src/**/*" 19 | ], 20 | "exclude": [ 21 | "node_modules", 22 | "**/*.spec.ts" 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /sample/006-multi-db-typeorm-app/.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 | -------------------------------------------------------------------------------- /sample/006-multi-db-typeorm-app/.gitignore: -------------------------------------------------------------------------------- 1 | # File created using '.gitignore Generator' for Visual Studio Code: https://bit.ly/vscode-gig 2 | 3 | # Created by https://www.gitignore.io/api/linux,visualstudiocode,node 4 | 5 | ### Linux ### 6 | *~ 7 | 8 | # temporary files which can be created if a process still has a handle open of a deleted file 9 | .fuse_hidden* 10 | 11 | # KDE directory preferences 12 | .directory 13 | 14 | # Linux trash folder which might appear on any partition or disk 15 | .Trash-* 16 | 17 | # .nfs files are created when an open file is removed but is still being accessed 18 | .nfs* 19 | 20 | ### Node ### 21 | # Logs 22 | logs 23 | *.log 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # Runtime data 29 | pids 30 | *.pid 31 | *.seed 32 | *.pid.lock 33 | 34 | # Directory for instrumented libs generated by jscoverage/JSCover 35 | lib-cov 36 | 37 | # Coverage directory used by tools like istanbul 38 | coverage 39 | 40 | # nyc test coverage 41 | .nyc_output 42 | 43 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 44 | .grunt 45 | 46 | # Bower dependency directory (https://bower.io/) 47 | bower_components 48 | 49 | # node-waf configuration 50 | .lock-wscript 51 | 52 | # Compiled binary addons (https://nodejs.org/api/addons.html) 53 | build/Release 54 | 55 | # Dependency directories 56 | node_modules/ 57 | jspm_packages/ 58 | 59 | # TypeScript v1 declaration files 60 | typings/ 61 | 62 | # Optional npm cache directory 63 | .npm 64 | 65 | # Optional eslint cache 66 | .eslintcache 67 | 68 | # Optional REPL history 69 | .node_repl_history 70 | 71 | # Output of 'npm pack' 72 | *.tgz 73 | 74 | # Yarn Integrity file 75 | .yarn-integrity 76 | 77 | # dotenv environment variables file 78 | .env 79 | 80 | # parcel-bundler cache (https://parceljs.org/) 81 | .cache 82 | 83 | # next.js build output 84 | .next 85 | 86 | # nuxt.js build output 87 | .nuxt 88 | 89 | # vuepress build output 90 | .vuepress/dist 91 | 92 | # Serverless directories 93 | .serverless 94 | 95 | ### VisualStudioCode ### 96 | .vscode/* 97 | !.vscode/settings.json 98 | !.vscode/tasks.json 99 | !.vscode/launch.json 100 | !.vscode/extensions.json 101 | 102 | 103 | # End of https://www.gitignore.io/api/linux,visualstudiocode,node 104 | 105 | # Custom rules (everything added below won't be overriden by 'Generate .gitignore File' if you use 'Update' option) 106 | 107 | -------------------------------------------------------------------------------- /sample/006-multi-db-typeorm-app/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | services: 4 | db1: 5 | image: mysql:9 6 | restart: always 7 | environment: 8 | MYSQL_ROOT_PASSWORD: root 9 | MYSQL_DATABASE: test1 10 | ports: 11 | - "3306:3306" 12 | db2: 13 | image: mysql:9 14 | restart: always 15 | environment: 16 | MYSQL_ROOT_PASSWORD: root 17 | MYSQL_DATABASE: test2 18 | ports: 19 | - "3307:3306" -------------------------------------------------------------------------------- /sample/006-multi-db-typeorm-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "006-multi-db-typeorm-app", 3 | "version": "1.0.0", 4 | "description": "Multi Database Typeorm Health Check sample app", 5 | "license": "MIT", 6 | "scripts": { 7 | "start": "ts-node src/main.ts", 8 | "prestart:prod": "tsc", 9 | "start:prod": "node dist/main.js", 10 | "build": "rm -rf dist && tsc -p tsconfig.json", 11 | "test": "jest", 12 | "test:watch": "jest --watch --config=jest.json", 13 | "test:coverage": "jest --config=jest.json --coverage --coverageDirectory=coverage", 14 | "e2e": "echo 'No e2e tests implemented yet.'", 15 | "e2e:watch": "jest --watch --config=e2e/jest-e2e.json" 16 | }, 17 | "dependencies": { 18 | "@nestjs/common": "11.0.11", 19 | "@nestjs/core": "11.0.11", 20 | "@nestjs/platform-express": "11.0.11", 21 | "@nestjs/terminus": "11.0.0", 22 | "@nestjs/typeorm": "11.0.0", 23 | "mysql": "2.18.1", 24 | "reflect-metadata": "0.2.2", 25 | "rxjs": "7.8.2", 26 | "typeorm": "0.3.22" 27 | }, 28 | "devDependencies": { 29 | "@nestjs/testing": "11.0.11", 30 | "@types/express": "5.0.0", 31 | "@types/jest": "29.5.14", 32 | "@types/node": "22.13.10", 33 | "@types/supertest": "6.0.2", 34 | "@typescript-eslint/eslint-plugin": "6.21.0", 35 | "@typescript-eslint/parser": "6.21.0", 36 | "eslint": "8.57.1", 37 | "eslint-config-prettier": "10.0.1", 38 | "eslint-plugin-import": "2.31.0", 39 | "jest": "29.7.0", 40 | "nodemon": "3.1.9", 41 | "prettier": "3.5.3", 42 | "supertest": "7.0.0", 43 | "ts-jest": "29.2.6", 44 | "ts-loader": "9.5.2", 45 | "ts-node": "10.9.2", 46 | "tsconfig-paths": "4.2.0", 47 | "typescript": "5.7.3" 48 | }, 49 | "jest": { 50 | "moduleFileExtensions": [ 51 | "js", 52 | "json", 53 | "ts" 54 | ], 55 | "rootDir": "src", 56 | "testRegex": ".spec.ts$", 57 | "transform": { 58 | "^.+\\.(t|j)s$": "ts-jest" 59 | }, 60 | "coverageDirectory": "../coverage", 61 | "testEnvironment": "node" 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /sample/006-multi-db-typeorm-app/src/app.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { HealthModule } from './health/health.module'; 3 | 4 | @Module({ 5 | imports: [HealthModule], 6 | }) 7 | export class AppModule {} 8 | -------------------------------------------------------------------------------- /sample/006-multi-db-typeorm-app/src/health/health.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Get } from '@nestjs/common'; 2 | import { 3 | HealthCheck, 4 | HealthCheckService, 5 | TypeOrmHealthIndicator, 6 | } from '@nestjs/terminus'; 7 | import { InjectConnection } from '@nestjs/typeorm'; 8 | import { Connection } from 'typeorm'; 9 | 10 | @Controller('health') 11 | export class HealthController { 12 | constructor( 13 | @InjectConnection('db1Connection') 14 | private db1Connection: Connection, 15 | @InjectConnection('db2Connection') 16 | private db2Connection: Connection, 17 | private health: HealthCheckService, 18 | private db: TypeOrmHealthIndicator, 19 | ) {} 20 | 21 | @Get() 22 | @HealthCheck() 23 | check() { 24 | return this.health.check([ 25 | async () => 26 | this.db.pingCheck('db1Connection', { connection: this.db1Connection }), 27 | async () => 28 | this.db.pingCheck('db2Connection', { connection: this.db2Connection }), 29 | ]); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /sample/006-multi-db-typeorm-app/src/health/health.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { TypeOrmModule } from '@nestjs/typeorm'; 3 | import { TerminusModule } from '@nestjs/terminus'; 4 | import { HealthController } from './health.controller'; 5 | 6 | @Module({ 7 | imports: [ 8 | TypeOrmModule.forRootAsync({ 9 | name: 'db1Connection', 10 | useFactory: () => ({ 11 | type: 'mysql', 12 | host: 'localhost', 13 | port: 3306, 14 | username: 'root', 15 | password: 'root', 16 | database: 'test1', 17 | synchronize: true, 18 | }), 19 | }), 20 | TypeOrmModule.forRootAsync({ 21 | name: 'db2Connection', 22 | useFactory: () => ({ 23 | type: 'mysql', 24 | host: 'localhost', 25 | port: 3307, 26 | username: 'root', 27 | password: 'root', 28 | database: 'test2', 29 | synchronize: true, 30 | }), 31 | }), 32 | TerminusModule, 33 | ], 34 | controllers: [HealthController], 35 | }) 36 | export class HealthModule {} 37 | -------------------------------------------------------------------------------- /sample/006-multi-db-typeorm-app/src/main.ts: -------------------------------------------------------------------------------- 1 | import { NestFactory } from '@nestjs/core'; 2 | import { AppModule } from './app.module'; 3 | 4 | async function bootstrap() { 5 | const app = await NestFactory.create(AppModule); 6 | await app.listen(3000); 7 | } 8 | 9 | bootstrap(); 10 | -------------------------------------------------------------------------------- /sample/006-multi-db-typeorm-app/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "declaration": true, 5 | "noImplicitAny": false, 6 | "removeComments": true, 7 | "noLib": false, 8 | "allowSyntheticDefaultImports": true, 9 | "emitDecoratorMetadata": true, 10 | "experimentalDecorators": true, 11 | "target": "es6", 12 | "sourceMap": true, 13 | "outDir": "./dist", 14 | "baseUrl": "./", 15 | "skipLibCheck": true, 16 | }, 17 | "include": [ 18 | "src/**/*" 19 | ], 20 | "exclude": [ 21 | "node_modules", 22 | "**/*.spec.ts" 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /sample/007-sequelize-app/.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 | -------------------------------------------------------------------------------- /sample/007-sequelize-app/.gitignore: -------------------------------------------------------------------------------- 1 | # File created using '.gitignore Generator' for Visual Studio Code: https://bit.ly/vscode-gig 2 | 3 | # Created by https://www.gitignore.io/api/linux,visualstudiocode,node 4 | 5 | ### Linux ### 6 | *~ 7 | 8 | # temporary files which can be created if a process still has a handle open of a deleted file 9 | .fuse_hidden* 10 | 11 | # KDE directory preferences 12 | .directory 13 | 14 | # Linux trash folder which might appear on any partition or disk 15 | .Trash-* 16 | 17 | # .nfs files are created when an open file is removed but is still being accessed 18 | .nfs* 19 | 20 | ### Node ### 21 | # Logs 22 | logs 23 | *.log 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # Runtime data 29 | pids 30 | *.pid 31 | *.seed 32 | *.pid.lock 33 | 34 | # Directory for instrumented libs generated by jscoverage/JSCover 35 | lib-cov 36 | 37 | # Coverage directory used by tools like istanbul 38 | coverage 39 | 40 | # nyc test coverage 41 | .nyc_output 42 | 43 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 44 | .grunt 45 | 46 | # Bower dependency directory (https://bower.io/) 47 | bower_components 48 | 49 | # node-waf configuration 50 | .lock-wscript 51 | 52 | # Compiled binary addons (https://nodejs.org/api/addons.html) 53 | build/Release 54 | 55 | # Dependency directories 56 | node_modules/ 57 | jspm_packages/ 58 | 59 | # TypeScript v1 declaration files 60 | typings/ 61 | 62 | # Optional npm cache directory 63 | .npm 64 | 65 | # Optional eslint cache 66 | .eslintcache 67 | 68 | # Optional REPL history 69 | .node_repl_history 70 | 71 | # Output of 'npm pack' 72 | *.tgz 73 | 74 | # Yarn Integrity file 75 | .yarn-integrity 76 | 77 | # dotenv environment variables file 78 | .env 79 | 80 | # parcel-bundler cache (https://parceljs.org/) 81 | .cache 82 | 83 | # next.js build output 84 | .next 85 | 86 | # nuxt.js build output 87 | .nuxt 88 | 89 | # vuepress build output 90 | .vuepress/dist 91 | 92 | # Serverless directories 93 | .serverless 94 | 95 | ### VisualStudioCode ### 96 | .vscode/* 97 | !.vscode/settings.json 98 | !.vscode/tasks.json 99 | !.vscode/launch.json 100 | !.vscode/extensions.json 101 | 102 | 103 | # End of https://www.gitignore.io/api/linux,visualstudiocode,node 104 | 105 | # Custom rules (everything added below won't be overriden by 'Generate .gitignore File' if you use 'Update' option) 106 | 107 | -------------------------------------------------------------------------------- /sample/007-sequelize-app/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | services: 4 | mysql: 5 | image: mysql:9 6 | restart: always 7 | environment: 8 | MYSQL_ROOT_PASSWORD: root 9 | MYSQL_DATABASE: test 10 | ports: 11 | - "3306:3306" 12 | -------------------------------------------------------------------------------- /sample/007-sequelize-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "007-sequelize-app", 3 | "version": "1.0.0", 4 | "description": "Sequelize Health Check sample app", 5 | "license": "MIT", 6 | "scripts": { 7 | "start": "ts-node src/main.ts", 8 | "prestart:prod": "tsc", 9 | "start:prod": "node dist/main.js", 10 | "build": "rm -rf dist && tsc -p tsconfig.json", 11 | "test": "jest", 12 | "test:watch": "jest --watch --config=jest.json", 13 | "test:coverage": "jest --config=jest.json --coverage --coverageDirectory=coverage", 14 | "e2e": "echo 'No e2e tests implemented yet.'", 15 | "e2e:watch": "jest --watch --config=e2e/jest-e2e.json" 16 | }, 17 | "dependencies": { 18 | "@nestjs/common": "11.0.11", 19 | "@nestjs/core": "11.0.11", 20 | "@nestjs/platform-express": "11.0.11", 21 | "@nestjs/sequelize": "11.0.0", 22 | "@nestjs/terminus": "11.0.0", 23 | "mysql2": "3.12.0", 24 | "reflect-metadata": "0.2.2", 25 | "rxjs": "7.8.2", 26 | "sequelize": "6.37.5", 27 | "sequelize-typescript": "2.1.6" 28 | }, 29 | "devDependencies": { 30 | "@nestjs/testing": "11.0.11", 31 | "@types/express": "5.0.0", 32 | "@types/jest": "29.5.14", 33 | "@types/node": "22.13.10", 34 | "@types/sequelize": "4.28.20", 35 | "@types/supertest": "6.0.2", 36 | "@typescript-eslint/eslint-plugin": "6.21.0", 37 | "@typescript-eslint/parser": "6.21.0", 38 | "eslint": "8.57.1", 39 | "eslint-config-prettier": "10.0.1", 40 | "eslint-plugin-import": "2.31.0", 41 | "jest": "29.7.0", 42 | "nodemon": "3.1.9", 43 | "prettier": "3.5.3", 44 | "supertest": "7.0.0", 45 | "ts-jest": "29.2.6", 46 | "ts-loader": "9.5.2", 47 | "ts-node": "10.9.2", 48 | "tsconfig-paths": "4.2.0", 49 | "typescript": "5.7.3" 50 | }, 51 | "jest": { 52 | "moduleFileExtensions": [ 53 | "js", 54 | "json", 55 | "ts" 56 | ], 57 | "rootDir": "src", 58 | "testRegex": ".spec.ts$", 59 | "transform": { 60 | "^.+\\.(t|j)s$": "ts-jest" 61 | }, 62 | "coverageDirectory": "../coverage", 63 | "testEnvironment": "node" 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /sample/007-sequelize-app/src/app.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { HealthModule } from './health/health.module'; 3 | 4 | @Module({ 5 | imports: [HealthModule], 6 | }) 7 | export class AppModule {} 8 | -------------------------------------------------------------------------------- /sample/007-sequelize-app/src/health/health.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Get } from '@nestjs/common'; 2 | import { 3 | HealthCheck, 4 | HealthCheckService, 5 | SequelizeHealthIndicator, 6 | } from '@nestjs/terminus'; 7 | 8 | @Controller('health') 9 | export class HealthController { 10 | constructor( 11 | private health: HealthCheckService, 12 | private db: SequelizeHealthIndicator, 13 | ) {} 14 | 15 | @Get() 16 | @HealthCheck() 17 | check() { 18 | return this.health.check([async () => this.db.pingCheck('sequelize')]); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /sample/007-sequelize-app/src/health/health.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { SequelizeModule } from '@nestjs/sequelize'; 3 | import { TerminusModule } from '@nestjs/terminus'; 4 | import { HealthController } from './health.controller'; 5 | 6 | @Module({ 7 | imports: [ 8 | SequelizeModule.forRoot({ 9 | dialect: 'mysql', 10 | host: 'localhost', 11 | port: 3306, 12 | username: 'root', 13 | password: 'root', 14 | database: 'test', 15 | autoLoadModels: true, 16 | synchronize: true, 17 | }), 18 | TerminusModule, 19 | ], 20 | controllers: [HealthController], 21 | }) 22 | export class HealthModule {} 23 | -------------------------------------------------------------------------------- /sample/007-sequelize-app/src/main.ts: -------------------------------------------------------------------------------- 1 | import { NestFactory } from '@nestjs/core'; 2 | import { AppModule } from './app.module'; 3 | 4 | async function bootstrap() { 5 | const app = await NestFactory.create(AppModule); 6 | await app.listen(3000); 7 | } 8 | 9 | bootstrap(); 10 | -------------------------------------------------------------------------------- /sample/007-sequelize-app/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "declaration": true, 5 | "noImplicitAny": false, 6 | "removeComments": true, 7 | "noLib": false, 8 | "allowSyntheticDefaultImports": true, 9 | "emitDecoratorMetadata": true, 10 | "experimentalDecorators": true, 11 | "target": "es6", 12 | "sourceMap": true, 13 | "outDir": "./dist", 14 | "baseUrl": "./", 15 | "skipLibCheck": true, 16 | }, 17 | "include": [ 18 | "src/**/*" 19 | ], 20 | "exclude": [ 21 | "node_modules", 22 | "**/*.spec.ts" 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /sample/008-multi-database-mongoose-app/.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 | -------------------------------------------------------------------------------- /sample/008-multi-database-mongoose-app/.gitignore: -------------------------------------------------------------------------------- 1 | # File created using '.gitignore Generator' for Visual Studio Code: https://bit.ly/vscode-gig 2 | 3 | # Created by https://www.gitignore.io/api/linux,visualstudiocode,node 4 | 5 | ### Linux ### 6 | *~ 7 | 8 | # temporary files which can be created if a process still has a handle open of a deleted file 9 | .fuse_hidden* 10 | 11 | # KDE directory preferences 12 | .directory 13 | 14 | # Linux trash folder which might appear on any partition or disk 15 | .Trash-* 16 | 17 | # .nfs files are created when an open file is removed but is still being accessed 18 | .nfs* 19 | 20 | ### Node ### 21 | # Logs 22 | logs 23 | *.log 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # Runtime data 29 | pids 30 | *.pid 31 | *.seed 32 | *.pid.lock 33 | 34 | # Directory for instrumented libs generated by jscoverage/JSCover 35 | lib-cov 36 | 37 | # Coverage directory used by tools like istanbul 38 | coverage 39 | 40 | # nyc test coverage 41 | .nyc_output 42 | 43 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 44 | .grunt 45 | 46 | # Bower dependency directory (https://bower.io/) 47 | bower_components 48 | 49 | # node-waf configuration 50 | .lock-wscript 51 | 52 | # Compiled binary addons (https://nodejs.org/api/addons.html) 53 | build/Release 54 | 55 | # Dependency directories 56 | node_modules/ 57 | jspm_packages/ 58 | 59 | # TypeScript v1 declaration files 60 | typings/ 61 | 62 | # Optional npm cache directory 63 | .npm 64 | 65 | # Optional eslint cache 66 | .eslintcache 67 | 68 | # Optional REPL history 69 | .node_repl_history 70 | 71 | # Output of 'npm pack' 72 | *.tgz 73 | 74 | # Yarn Integrity file 75 | .yarn-integrity 76 | 77 | # dotenv environment variables file 78 | .env 79 | 80 | # parcel-bundler cache (https://parceljs.org/) 81 | .cache 82 | 83 | # next.js build output 84 | .next 85 | 86 | # nuxt.js build output 87 | .nuxt 88 | 89 | # vuepress build output 90 | .vuepress/dist 91 | 92 | # Serverless directories 93 | .serverless 94 | 95 | ### VisualStudioCode ### 96 | .vscode/* 97 | !.vscode/settings.json 98 | !.vscode/tasks.json 99 | !.vscode/launch.json 100 | !.vscode/extensions.json 101 | 102 | 103 | # End of https://www.gitignore.io/api/linux,visualstudiocode,node 104 | 105 | # Custom rules (everything added below won't be overriden by 'Generate .gitignore File' if you use 'Update' option) 106 | 107 | -------------------------------------------------------------------------------- /sample/008-multi-database-mongoose-app/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | services: 4 | db1: 5 | image: mongo:latest 6 | environment: 7 | - MONGODB_DATABASE="test1" 8 | ports: 9 | - 27017:27017 10 | db2: 11 | image: mongo:latest 12 | environment: 13 | - MONGODB_DATABASE="test2" 14 | ports: 15 | - 27018:27017 16 | -------------------------------------------------------------------------------- /sample/008-multi-database-mongoose-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "008-multi-database-mongoose-app", 3 | "version": "1.0.0", 4 | "description": "Multi Database Mongoose Health Check sample app", 5 | "license": "MIT", 6 | "scripts": { 7 | "start": "ts-node src/main.ts", 8 | "prestart:prod": "tsc", 9 | "start:prod": "node dist/main.js", 10 | "build": "rm -rf dist && tsc -p tsconfig.json", 11 | "test": "jest", 12 | "test:watch": "jest --watch --config=jest.json", 13 | "test:coverage": "jest --config=jest.json --coverage --coverageDirectory=coverage", 14 | "e2e": "echo 'No e2e tests implemented yet.'", 15 | "e2e:watch": "jest --watch --config=e2e/jest-e2e.json" 16 | }, 17 | "dependencies": { 18 | "@nestjs/common": "11.0.11", 19 | "@nestjs/core": "11.0.11", 20 | "@nestjs/mongoose": "11.0.1", 21 | "@nestjs/platform-express": "11.0.11", 22 | "@nestjs/terminus": "11.0.0", 23 | "mongoose": "8.10.1", 24 | "reflect-metadata": "0.2.2", 25 | "rxjs": "7.8.2" 26 | }, 27 | "devDependencies": { 28 | "@nestjs/testing": "11.0.11", 29 | "@types/express": "5.0.0", 30 | "@types/jest": "29.5.14", 31 | "@types/node": "22.13.10", 32 | "@types/supertest": "6.0.2", 33 | "@typescript-eslint/eslint-plugin": "6.21.0", 34 | "@typescript-eslint/parser": "6.21.0", 35 | "eslint": "8.57.1", 36 | "eslint-config-prettier": "10.0.1", 37 | "eslint-plugin-import": "2.31.0", 38 | "jest": "29.7.0", 39 | "nodemon": "3.1.9", 40 | "prettier": "3.5.3", 41 | "supertest": "7.0.0", 42 | "ts-jest": "29.2.6", 43 | "ts-loader": "9.5.2", 44 | "ts-node": "10.9.2", 45 | "tsconfig-paths": "4.2.0", 46 | "typescript": "5.7.3" 47 | }, 48 | "jest": { 49 | "moduleFileExtensions": [ 50 | "js", 51 | "json", 52 | "ts" 53 | ], 54 | "rootDir": "src", 55 | "testRegex": ".spec.ts$", 56 | "transform": { 57 | "^.+\\.(t|j)s$": "ts-jest" 58 | }, 59 | "coverageDirectory": "../coverage", 60 | "testEnvironment": "node" 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /sample/008-multi-database-mongoose-app/src/app.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { HealthModule } from './health/health.module'; 3 | 4 | @Module({ 5 | imports: [HealthModule], 6 | }) 7 | export class AppModule {} 8 | -------------------------------------------------------------------------------- /sample/008-multi-database-mongoose-app/src/health/health.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Get } from '@nestjs/common'; 2 | import { InjectConnection } from '@nestjs/mongoose'; 3 | import { 4 | HealthCheck, 5 | HealthCheckService, 6 | MongooseHealthIndicator, 7 | } from '@nestjs/terminus'; 8 | import { Connection } from 'mongoose'; 9 | 10 | @Controller('health') 11 | export class HealthController { 12 | constructor( 13 | @InjectConnection('mongodb1') 14 | private mongodb1: Connection, 15 | @InjectConnection('mongodb2') 16 | private mongodb2: Connection, 17 | private health: HealthCheckService, 18 | private mongoose: MongooseHealthIndicator, 19 | ) {} 20 | 21 | @Get() 22 | @HealthCheck() 23 | check() { 24 | return this.health.check([ 25 | async () => 26 | this.mongoose.pingCheck('mongodb1', { connection: this.mongodb1 }), 27 | async () => 28 | this.mongoose.pingCheck('mongodb2', { connection: this.mongodb2 }), 29 | ]); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /sample/008-multi-database-mongoose-app/src/health/health.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { MongooseModule } from '@nestjs/mongoose'; 3 | import { TerminusModule } from '@nestjs/terminus'; 4 | import { HealthController } from './health.controller'; 5 | 6 | @Module({ 7 | imports: [ 8 | MongooseModule.forRoot('mongodb://localhost:27017/test1', { 9 | connectionName: 'mongodb1', 10 | }), 11 | MongooseModule.forRoot('mongodb://localhost:27018/test2', { 12 | connectionName: 'mongodb2', 13 | }), 14 | TerminusModule, 15 | ], 16 | controllers: [HealthController], 17 | }) 18 | export class HealthModule {} 19 | -------------------------------------------------------------------------------- /sample/008-multi-database-mongoose-app/src/main.ts: -------------------------------------------------------------------------------- 1 | import { NestFactory } from '@nestjs/core'; 2 | import { AppModule } from './app.module'; 3 | 4 | async function bootstrap() { 5 | const app = await NestFactory.create(AppModule); 6 | await app.listen(3000); 7 | } 8 | 9 | bootstrap(); 10 | -------------------------------------------------------------------------------- /sample/008-multi-database-mongoose-app/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "declaration": true, 5 | "noImplicitAny": false, 6 | "removeComments": true, 7 | "noLib": false, 8 | "allowSyntheticDefaultImports": true, 9 | "emitDecoratorMetadata": true, 10 | "experimentalDecorators": true, 11 | "target": "es6", 12 | "sourceMap": true, 13 | "outDir": "./dist", 14 | "baseUrl": "./", 15 | "skipLibCheck": true, 16 | }, 17 | "include": [ 18 | "src/**/*" 19 | ], 20 | "exclude": [ 21 | "node_modules", 22 | "**/*.spec.ts" 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /sample/009-http-app/.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 | -------------------------------------------------------------------------------- /sample/009-http-app/.gitignore: -------------------------------------------------------------------------------- 1 | # File created using '.gitignore Generator' for Visual Studio Code: https://bit.ly/vscode-gig 2 | 3 | # Created by https://www.gitignore.io/api/linux,visualstudiocode,node 4 | 5 | ### Linux ### 6 | *~ 7 | 8 | # temporary files which can be created if a process still has a handle open of a deleted file 9 | .fuse_hidden* 10 | 11 | # KDE directory preferences 12 | .directory 13 | 14 | # Linux trash folder which might appear on any partition or disk 15 | .Trash-* 16 | 17 | # .nfs files are created when an open file is removed but is still being accessed 18 | .nfs* 19 | 20 | ### Node ### 21 | # Logs 22 | logs 23 | *.log 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # Runtime data 29 | pids 30 | *.pid 31 | *.seed 32 | *.pid.lock 33 | 34 | # Directory for instrumented libs generated by jscoverage/JSCover 35 | lib-cov 36 | 37 | # Coverage directory used by tools like istanbul 38 | coverage 39 | 40 | # nyc test coverage 41 | .nyc_output 42 | 43 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 44 | .grunt 45 | 46 | # Bower dependency directory (https://bower.io/) 47 | bower_components 48 | 49 | # node-waf configuration 50 | .lock-wscript 51 | 52 | # Compiled binary addons (https://nodejs.org/api/addons.html) 53 | build/Release 54 | 55 | # Dependency directories 56 | node_modules/ 57 | jspm_packages/ 58 | 59 | # TypeScript v1 declaration files 60 | typings/ 61 | 62 | # Optional npm cache directory 63 | .npm 64 | 65 | # Optional eslint cache 66 | .eslintcache 67 | 68 | # Optional REPL history 69 | .node_repl_history 70 | 71 | # Output of 'npm pack' 72 | *.tgz 73 | 74 | # Yarn Integrity file 75 | .yarn-integrity 76 | 77 | # dotenv environment variables file 78 | .env 79 | 80 | # parcel-bundler cache (https://parceljs.org/) 81 | .cache 82 | 83 | # next.js build output 84 | .next 85 | 86 | # nuxt.js build output 87 | .nuxt 88 | 89 | # vuepress build output 90 | .vuepress/dist 91 | 92 | # Serverless directories 93 | .serverless 94 | 95 | ### VisualStudioCode ### 96 | .vscode/* 97 | !.vscode/settings.json 98 | !.vscode/tasks.json 99 | !.vscode/launch.json 100 | !.vscode/extensions.json 101 | 102 | 103 | # End of https://www.gitignore.io/api/linux,visualstudiocode,node 104 | 105 | # Custom rules (everything added below won't be overriden by 'Generate .gitignore File' if you use 'Update' option) 106 | 107 | -------------------------------------------------------------------------------- /sample/009-http-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "009-http-app", 3 | "version": "1.0.0", 4 | "description": "Http Health Check sample app", 5 | "license": "MIT", 6 | "scripts": { 7 | "start": "ts-node src/main.ts", 8 | "prestart:prod": "tsc", 9 | "start:prod": "node dist/main.js", 10 | "build": "rm -rf dist && tsc -p tsconfig.json", 11 | "test": "jest", 12 | "test:watch": "jest --watch --config=jest.json", 13 | "test:coverage": "jest --config=jest.json --coverage --coverageDirectory=coverage", 14 | "e2e": "echo 'No e2e tests implemented yet.'", 15 | "e2e:watch": "jest --watch --config=e2e/jest-e2e.json" 16 | }, 17 | "dependencies": { 18 | "@nestjs/axios": "4.0.0", 19 | "@nestjs/common": "11.0.11", 20 | "@nestjs/core": "11.0.11", 21 | "@nestjs/platform-express": "11.0.11", 22 | "@nestjs/terminus": "11.0.0", 23 | "reflect-metadata": "0.2.2", 24 | "rxjs": "7.8.2" 25 | }, 26 | "devDependencies": { 27 | "@nestjs/testing": "11.0.11", 28 | "@types/express": "5.0.0", 29 | "@types/jest": "29.5.14", 30 | "@types/node": "22.13.10", 31 | "@types/supertest": "6.0.2", 32 | "@typescript-eslint/eslint-plugin": "6.21.0", 33 | "@typescript-eslint/parser": "6.21.0", 34 | "eslint": "8.57.1", 35 | "eslint-config-prettier": "10.0.1", 36 | "eslint-plugin-import": "2.31.0", 37 | "jest": "29.7.0", 38 | "nodemon": "3.1.9", 39 | "prettier": "3.5.3", 40 | "supertest": "7.0.0", 41 | "ts-jest": "29.2.6", 42 | "ts-loader": "9.5.2", 43 | "ts-node": "10.9.2", 44 | "tsconfig-paths": "4.2.0", 45 | "typescript": "5.7.3" 46 | }, 47 | "jest": { 48 | "moduleFileExtensions": [ 49 | "js", 50 | "json", 51 | "ts" 52 | ], 53 | "rootDir": "src", 54 | "testRegex": ".spec.ts$", 55 | "transform": { 56 | "^.+\\.(t|j)s$": "ts-jest" 57 | }, 58 | "coverageDirectory": "../coverage", 59 | "testEnvironment": "node" 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /sample/009-http-app/src/app.module.ts: -------------------------------------------------------------------------------- 1 | import { HttpModule } from '@nestjs/axios'; 2 | import { Module } from '@nestjs/common'; 3 | import { HealthModule } from './health/health.module'; 4 | 5 | @Module({ 6 | imports: [HealthModule, HttpModule], 7 | }) 8 | export class AppModule {} 9 | -------------------------------------------------------------------------------- /sample/009-http-app/src/health/health.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Get } from '@nestjs/common'; 2 | import { 3 | HealthCheckService, 4 | HttpHealthIndicator, 5 | HealthCheck, 6 | } from '@nestjs/terminus'; 7 | 8 | @Controller('health') 9 | export class HealthController { 10 | constructor( 11 | private health: HealthCheckService, 12 | private http: HttpHealthIndicator, 13 | ) {} 14 | 15 | @Get() 16 | @HealthCheck() 17 | check() { 18 | return this.health.check([ 19 | () => this.http.pingCheck('nestjs-docs', 'https://docs.nestjs.com'), 20 | ]); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /sample/009-http-app/src/health/health.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { TerminusModule } from '@nestjs/terminus'; 3 | import { HealthController } from './health.controller'; 4 | 5 | @Module({ 6 | imports: [TerminusModule], 7 | controllers: [HealthController], 8 | }) 9 | export class HealthModule {} 10 | -------------------------------------------------------------------------------- /sample/009-http-app/src/main.ts: -------------------------------------------------------------------------------- 1 | import { NestFactory } from '@nestjs/core'; 2 | import { AppModule } from './app.module'; 3 | 4 | async function bootstrap() { 5 | const app = await NestFactory.create(AppModule); 6 | await app.listen(3000); 7 | } 8 | 9 | bootstrap(); 10 | -------------------------------------------------------------------------------- /sample/009-http-app/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "declaration": true, 5 | "noImplicitAny": false, 6 | "removeComments": true, 7 | "noLib": false, 8 | "allowSyntheticDefaultImports": true, 9 | "emitDecoratorMetadata": true, 10 | "experimentalDecorators": true, 11 | "target": "es6", 12 | "sourceMap": true, 13 | "outDir": "./dist", 14 | "baseUrl": "./", 15 | "skipLibCheck": true, 16 | }, 17 | "include": [ 18 | "src/**/*" 19 | ], 20 | "exclude": [ 21 | "node_modules", 22 | "**/*.spec.ts" 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /sample/010-disk-app/.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 | -------------------------------------------------------------------------------- /sample/010-disk-app/.gitignore: -------------------------------------------------------------------------------- 1 | # File created using '.gitignore Generator' for Visual Studio Code: https://bit.ly/vscode-gig 2 | 3 | # Created by https://www.gitignore.io/api/linux,visualstudiocode,node 4 | 5 | ### Linux ### 6 | *~ 7 | 8 | # temporary files which can be created if a process still has a handle open of a deleted file 9 | .fuse_hidden* 10 | 11 | # KDE directory preferences 12 | .directory 13 | 14 | # Linux trash folder which might appear on any partition or disk 15 | .Trash-* 16 | 17 | # .nfs files are created when an open file is removed but is still being accessed 18 | .nfs* 19 | 20 | ### Node ### 21 | # Logs 22 | logs 23 | *.log 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # Runtime data 29 | pids 30 | *.pid 31 | *.seed 32 | *.pid.lock 33 | 34 | # Directory for instrumented libs generated by jscoverage/JSCover 35 | lib-cov 36 | 37 | # Coverage directory used by tools like istanbul 38 | coverage 39 | 40 | # nyc test coverage 41 | .nyc_output 42 | 43 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 44 | .grunt 45 | 46 | # Bower dependency directory (https://bower.io/) 47 | bower_components 48 | 49 | # node-waf configuration 50 | .lock-wscript 51 | 52 | # Compiled binary addons (https://nodejs.org/api/addons.html) 53 | build/Release 54 | 55 | # Dependency directories 56 | node_modules/ 57 | jspm_packages/ 58 | 59 | # TypeScript v1 declaration files 60 | typings/ 61 | 62 | # Optional npm cache directory 63 | .npm 64 | 65 | # Optional eslint cache 66 | .eslintcache 67 | 68 | # Optional REPL history 69 | .node_repl_history 70 | 71 | # Output of 'npm pack' 72 | *.tgz 73 | 74 | # Yarn Integrity file 75 | .yarn-integrity 76 | 77 | # dotenv environment variables file 78 | .env 79 | 80 | # parcel-bundler cache (https://parceljs.org/) 81 | .cache 82 | 83 | # next.js build output 84 | .next 85 | 86 | # nuxt.js build output 87 | .nuxt 88 | 89 | # vuepress build output 90 | .vuepress/dist 91 | 92 | # Serverless directories 93 | .serverless 94 | 95 | ### VisualStudioCode ### 96 | .vscode/* 97 | !.vscode/settings.json 98 | !.vscode/tasks.json 99 | !.vscode/launch.json 100 | !.vscode/extensions.json 101 | 102 | 103 | # End of https://www.gitignore.io/api/linux,visualstudiocode,node 104 | 105 | # Custom rules (everything added below won't be overriden by 'Generate .gitignore File' if you use 'Update' option) 106 | 107 | -------------------------------------------------------------------------------- /sample/010-disk-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "010-disk-app", 3 | "version": "1.0.0", 4 | "description": "Disk Health Check sample app", 5 | "license": "MIT", 6 | "scripts": { 7 | "start": "ts-node src/main.ts", 8 | "prestart:prod": "tsc", 9 | "start:prod": "node dist/main.js", 10 | "build": "rm -rf dist && tsc -p tsconfig.json", 11 | "test": "jest", 12 | "test:watch": "jest --watch --config=jest.json", 13 | "test:coverage": "jest --config=jest.json --coverage --coverageDirectory=coverage", 14 | "e2e": "echo 'No e2e tests implemented yet.'", 15 | "e2e:watch": "jest --watch --config=e2e/jest-e2e.json" 16 | }, 17 | "dependencies": { 18 | "@nestjs/axios": "4.0.0", 19 | "@nestjs/common": "11.0.11", 20 | "@nestjs/core": "11.0.11", 21 | "@nestjs/platform-express": "11.0.11", 22 | "@nestjs/terminus": "11.0.0", 23 | "reflect-metadata": "0.2.2", 24 | "rxjs": "7.8.2" 25 | }, 26 | "devDependencies": { 27 | "@nestjs/testing": "11.0.11", 28 | "@types/express": "5.0.0", 29 | "@types/jest": "29.5.14", 30 | "@types/node": "22.13.10", 31 | "@types/supertest": "6.0.2", 32 | "@typescript-eslint/eslint-plugin": "6.21.0", 33 | "@typescript-eslint/parser": "6.21.0", 34 | "eslint": "8.57.1", 35 | "eslint-config-prettier": "10.0.1", 36 | "eslint-plugin-import": "2.31.0", 37 | "jest": "29.7.0", 38 | "nodemon": "3.1.9", 39 | "prettier": "3.5.3", 40 | "supertest": "7.0.0", 41 | "ts-jest": "29.2.6", 42 | "ts-loader": "9.5.2", 43 | "ts-node": "10.9.2", 44 | "tsconfig-paths": "4.2.0", 45 | "typescript": "5.7.3" 46 | }, 47 | "jest": { 48 | "moduleFileExtensions": [ 49 | "js", 50 | "json", 51 | "ts" 52 | ], 53 | "rootDir": "src", 54 | "testRegex": ".spec.ts$", 55 | "transform": { 56 | "^.+\\.(t|j)s$": "ts-jest" 57 | }, 58 | "coverageDirectory": "../coverage", 59 | "testEnvironment": "node" 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /sample/010-disk-app/src/app.module.ts: -------------------------------------------------------------------------------- 1 | import { HttpModule } from '@nestjs/axios'; 2 | import { Module } from '@nestjs/common'; 3 | import { HealthModule } from './health/health.module'; 4 | 5 | @Module({ 6 | imports: [HealthModule, HttpModule], 7 | }) 8 | export class AppModule {} 9 | -------------------------------------------------------------------------------- /sample/010-disk-app/src/health/health.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Get } from '@nestjs/common'; 2 | import { 3 | HealthCheckService, 4 | DiskHealthIndicator, 5 | HealthCheck, 6 | } from '@nestjs/terminus'; 7 | 8 | @Controller('health') 9 | export class HealthController { 10 | constructor( 11 | private health: HealthCheckService, 12 | private disk: DiskHealthIndicator, 13 | ) {} 14 | 15 | @Get() 16 | @HealthCheck() 17 | check() { 18 | return this.health.check([ 19 | // The used disk storage should not exceed 50% of the full disk size 20 | () => 21 | this.disk.checkStorage('disk health', { 22 | thresholdPercent: 0.5, 23 | path: '/', 24 | }), 25 | // The used disk storage should not exceed 250 GB 26 | () => 27 | this.disk.checkStorage('disk health', { 28 | threshold: 250 * 1024 * 1024 * 1024, 29 | path: '/', 30 | }), 31 | ]); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /sample/010-disk-app/src/health/health.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { TerminusModule } from '@nestjs/terminus'; 3 | import { HealthController } from './health.controller'; 4 | 5 | @Module({ 6 | imports: [TerminusModule], 7 | controllers: [HealthController], 8 | }) 9 | export class HealthModule {} 10 | -------------------------------------------------------------------------------- /sample/010-disk-app/src/main.ts: -------------------------------------------------------------------------------- 1 | import { NestFactory } from '@nestjs/core'; 2 | import { AppModule } from './app.module'; 3 | 4 | async function bootstrap() { 5 | const app = await NestFactory.create(AppModule); 6 | await app.listen(3000); 7 | } 8 | 9 | bootstrap(); 10 | -------------------------------------------------------------------------------- /sample/010-disk-app/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "declaration": true, 5 | "noImplicitAny": false, 6 | "removeComments": true, 7 | "noLib": false, 8 | "allowSyntheticDefaultImports": true, 9 | "emitDecoratorMetadata": true, 10 | "experimentalDecorators": true, 11 | "target": "es6", 12 | "sourceMap": true, 13 | "outDir": "./dist", 14 | "baseUrl": "./", 15 | "skipLibCheck": true, 16 | }, 17 | "include": [ 18 | "src/**/*" 19 | ], 20 | "exclude": [ 21 | "node_modules", 22 | "**/*.spec.ts" 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /sample/011-mirkoorm-app/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | parser: '@typescript-eslint/parser', 3 | parserOptions: { 4 | project: 'tsconfig.json', 5 | tsconfigRootDir: __dirname, 6 | sourceType: 'module', 7 | }, 8 | plugins: ['@typescript-eslint/eslint-plugin'], 9 | extends: [ 10 | 'plugin:@typescript-eslint/recommended', 11 | 'plugin:prettier/recommended', 12 | ], 13 | root: true, 14 | env: { 15 | node: true, 16 | jest: true, 17 | }, 18 | ignorePatterns: ['.eslintrc.js'], 19 | rules: { 20 | '@typescript-eslint/interface-name-prefix': 'off', 21 | '@typescript-eslint/explicit-function-return-type': 'off', 22 | '@typescript-eslint/explicit-module-boundary-types': 'off', 23 | '@typescript-eslint/no-explicit-any': 'off', 24 | }, 25 | }; 26 | -------------------------------------------------------------------------------- /sample/011-mirkoorm-app/.gitignore: -------------------------------------------------------------------------------- 1 | # compiled output 2 | /dist 3 | /node_modules 4 | 5 | # Logs 6 | logs 7 | *.log 8 | npm-debug.log* 9 | pnpm-debug.log* 10 | yarn-debug.log* 11 | yarn-error.log* 12 | lerna-debug.log* 13 | 14 | # OS 15 | .DS_Store 16 | 17 | # Tests 18 | /coverage 19 | /.nyc_output 20 | 21 | # IDEs and editors 22 | /.idea 23 | .project 24 | .classpath 25 | .c9/ 26 | *.launch 27 | .settings/ 28 | *.sublime-workspace 29 | 30 | # IDE - VSCode 31 | .vscode/* 32 | !.vscode/settings.json 33 | !.vscode/tasks.json 34 | !.vscode/launch.json 35 | !.vscode/extensions.json -------------------------------------------------------------------------------- /sample/011-mirkoorm-app/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | services: 4 | mysql: 5 | image: mysql:9 6 | restart: always 7 | environment: 8 | MYSQL_ROOT_PASSWORD: root 9 | MYSQL_DATABASE: test 10 | ports: 11 | - "3306:3306" 12 | -------------------------------------------------------------------------------- /sample/011-mirkoorm-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "011-mirkoorm-app", 3 | "version": "1.0.0", 4 | "description": "Mirkoorm Health Check sample app", 5 | "license": "MIT", 6 | "scripts": { 7 | "build": "rm -rf dist && tsc -p tsconfig.json", 8 | "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", 9 | "start": "ts-node src/main.ts", 10 | "start:dev": "nest start --watch", 11 | "start:debug": "nest start --debug --watch", 12 | "start:prod": "node dist/main", 13 | "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", 14 | "test": "jest", 15 | "test:watch": "jest --watch", 16 | "test:cov": "jest --coverage", 17 | "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", 18 | "e2e": "echo 'No e2e tests implemented yet.'" 19 | }, 20 | "dependencies": { 21 | "@mikro-orm/core": "6.4.5", 22 | "@mikro-orm/mysql": "6.4.5", 23 | "@mikro-orm/nestjs": "6.1.1", 24 | "@nestjs/common": "11.0.11", 25 | "@nestjs/core": "11.0.11", 26 | "@nestjs/platform-express": "11.0.11", 27 | "@nestjs/terminus": "11.0.0", 28 | "reflect-metadata": "0.2.2", 29 | "rxjs": "7.8.2" 30 | }, 31 | "devDependencies": { 32 | "@nestjs/testing": "11.0.11", 33 | "@types/express": "5.0.0", 34 | "@types/jest": "29.5.14", 35 | "@types/node": "22.13.10", 36 | "@types/supertest": "6.0.2", 37 | "@typescript-eslint/eslint-plugin": "6.21.0", 38 | "@typescript-eslint/parser": "6.21.0", 39 | "eslint": "8.57.1", 40 | "eslint-config-prettier": "10.0.1", 41 | "eslint-plugin-prettier": "5.2.3", 42 | "jest": "29.7.0", 43 | "prettier": "3.5.3", 44 | "source-map-support": "0.5.21", 45 | "supertest": "7.0.0", 46 | "ts-jest": "29.2.6", 47 | "ts-loader": "9.5.2", 48 | "ts-node": "10.9.2", 49 | "tsconfig-paths": "4.2.0", 50 | "typescript": "5.7.3" 51 | }, 52 | "jest": { 53 | "moduleFileExtensions": [ 54 | "js", 55 | "json", 56 | "ts" 57 | ], 58 | "rootDir": "src", 59 | "testRegex": ".*\\.spec\\.ts$", 60 | "transform": { 61 | "^.+\\.(t|j)s$": "ts-jest" 62 | }, 63 | "collectCoverageFrom": [ 64 | "**/*.(t|j)s" 65 | ], 66 | "coverageDirectory": "../coverage", 67 | "testEnvironment": "node" 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /sample/011-mirkoorm-app/src/app.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { HealthModule } from './health/health.module'; 3 | 4 | @Module({ 5 | imports: [HealthModule], 6 | }) 7 | export class AppModule {} 8 | -------------------------------------------------------------------------------- /sample/011-mirkoorm-app/src/health/health.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Get } from '@nestjs/common'; 2 | import { 3 | HealthCheck, 4 | HealthCheckService, 5 | MikroOrmHealthIndicator, 6 | } from '@nestjs/terminus'; 7 | 8 | @Controller('health') 9 | export class HealthController { 10 | constructor( 11 | private health: HealthCheckService, 12 | private mikroOrm: MikroOrmHealthIndicator, 13 | ) {} 14 | 15 | @Get() 16 | @HealthCheck() 17 | check() { 18 | return this.health.check([async () => this.mikroOrm.pingCheck('mikroOrm')]); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /sample/011-mirkoorm-app/src/health/health.module.ts: -------------------------------------------------------------------------------- 1 | import { MikroOrmModule } from '@mikro-orm/nestjs'; 2 | import { Module } from '@nestjs/common'; 3 | import { HealthController } from './health.controller'; 4 | import { TerminusModule } from '@nestjs/terminus'; 5 | import { MySqlDriver } from '@mikro-orm/mysql'; 6 | 7 | @Module({ 8 | imports: [ 9 | MikroOrmModule.forRoot({ 10 | driver: MySqlDriver, 11 | dbName: 'test', 12 | user: 'root', 13 | password: 'root', 14 | host: '0.0.0.0', 15 | port: 3306, 16 | discovery: { warnWhenNoEntities: false }, // disable validation entities 17 | strict: true, 18 | }), 19 | TerminusModule, 20 | ], 21 | controllers: [HealthController], 22 | }) 23 | export class HealthModule {} 24 | -------------------------------------------------------------------------------- /sample/011-mirkoorm-app/src/main.ts: -------------------------------------------------------------------------------- 1 | import { NestFactory } from '@nestjs/core'; 2 | import { AppModule } from './app.module'; 3 | 4 | async function bootstrap() { 5 | const app = await NestFactory.create(AppModule); 6 | await app.listen(3000); 7 | } 8 | bootstrap(); 9 | -------------------------------------------------------------------------------- /sample/011-mirkoorm-app/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "declaration": true, 5 | "removeComments": true, 6 | "emitDecoratorMetadata": true, 7 | "experimentalDecorators": true, 8 | "allowSyntheticDefaultImports": true, 9 | "target": "es2017", 10 | "sourceMap": true, 11 | "outDir": "./dist", 12 | "baseUrl": "./", 13 | "incremental": true, 14 | "skipLibCheck": true, 15 | "strictNullChecks": false, 16 | "noImplicitAny": false, 17 | "strictBindCallApply": false, 18 | "forceConsistentCasingInFileNames": false, 19 | "noFallthroughCasesInSwitch": false 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /sample/012-prisma-app/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | parser: '@typescript-eslint/parser', 3 | parserOptions: { 4 | project: 'tsconfig.json', 5 | tsconfigRootDir: __dirname, 6 | sourceType: 'module', 7 | }, 8 | plugins: ['@typescript-eslint/eslint-plugin'], 9 | extends: [ 10 | 'plugin:@typescript-eslint/recommended', 11 | 'plugin:prettier/recommended', 12 | ], 13 | root: true, 14 | env: { 15 | node: true, 16 | jest: true, 17 | }, 18 | ignorePatterns: ['.eslintrc.js'], 19 | rules: { 20 | '@typescript-eslint/interface-name-prefix': 'off', 21 | '@typescript-eslint/explicit-function-return-type': 'off', 22 | '@typescript-eslint/explicit-module-boundary-types': 'off', 23 | '@typescript-eslint/no-explicit-any': 'off', 24 | }, 25 | }; 26 | -------------------------------------------------------------------------------- /sample/012-prisma-app/.gitignore: -------------------------------------------------------------------------------- 1 | # compiled output 2 | /dist 3 | /node_modules 4 | 5 | # Logs 6 | logs 7 | *.log 8 | npm-debug.log* 9 | pnpm-debug.log* 10 | yarn-debug.log* 11 | yarn-error.log* 12 | lerna-debug.log* 13 | 14 | # OS 15 | .DS_Store 16 | 17 | # Tests 18 | /coverage 19 | /.nyc_output 20 | 21 | # IDEs and editors 22 | /.idea 23 | .project 24 | .classpath 25 | .c9/ 26 | *.launch 27 | .settings/ 28 | *.sublime-workspace 29 | 30 | # IDE - VSCode 31 | .vscode/* 32 | !.vscode/settings.json 33 | !.vscode/tasks.json 34 | !.vscode/launch.json 35 | !.vscode/extensions.json -------------------------------------------------------------------------------- /sample/012-prisma-app/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "all" 4 | } -------------------------------------------------------------------------------- /sample/012-prisma-app/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | services: 4 | postgres: 5 | image: postgres:16 6 | restart: always 7 | environment: 8 | POSTGRES_USER: docker 9 | POSTGRES_PASSWORD: default 10 | PGDATA: /data/postgres 11 | ports: 12 | - "5432:5432" -------------------------------------------------------------------------------- /sample/012-prisma-app/nest-cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/nest-cli", 3 | "collection": "@nestjs/schematics", 4 | "sourceRoot": "src", 5 | "compilerOptions": { 6 | "deleteOutDir": true 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /sample/012-prisma-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "012-prisma-app", 3 | "version": "1.0.0", 4 | "description": "Prisma Health Check sample app", 5 | "license": "MIT", 6 | "scripts": { 7 | "build": "prisma generate && tsc -p tsconfig.json", 8 | "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", 9 | "start": "nest start", 10 | "start:dev": "nest start --watch", 11 | "start:debug": "nest start --debug --watch", 12 | "start:prod": "node dist/main", 13 | "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", 14 | "test": "jest", 15 | "test:watch": "jest --watch", 16 | "test:cov": "jest --coverage", 17 | "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", 18 | "e2e": "echo 'No e2e tests implemented yet.'" 19 | }, 20 | "dependencies": { 21 | "@nestjs/common": "11.0.11", 22 | "@nestjs/core": "11.0.11", 23 | "@nestjs/platform-express": "11.0.11", 24 | "@nestjs/terminus": "11.0.0", 25 | "@prisma/client": "6.3.1", 26 | "prisma": "^6.2.1", 27 | "reflect-metadata": "0.2.2", 28 | "rxjs": "7.8.2" 29 | }, 30 | "devDependencies": { 31 | "@nestjs/testing": "11.0.11", 32 | "@types/express": "5.0.0", 33 | "@types/jest": "29.5.14", 34 | "@types/node": "22.13.10", 35 | "@types/supertest": "6.0.2", 36 | "@typescript-eslint/eslint-plugin": "6.21.0", 37 | "@typescript-eslint/parser": "6.21.0", 38 | "eslint": "8.57.1", 39 | "eslint-config-prettier": "10.0.1", 40 | "eslint-plugin-prettier": "5.2.3", 41 | "jest": "29.7.0", 42 | "prettier": "3.5.3", 43 | "source-map-support": "0.5.21", 44 | "supertest": "7.0.0", 45 | "ts-jest": "29.2.6", 46 | "ts-loader": "9.5.2", 47 | "ts-node": "10.9.2", 48 | "tsconfig-paths": "4.2.0", 49 | "typescript": "5.7.3" 50 | }, 51 | "jest": { 52 | "moduleFileExtensions": [ 53 | "js", 54 | "json", 55 | "ts" 56 | ], 57 | "rootDir": "src", 58 | "testRegex": ".*\\.spec\\.ts$", 59 | "transform": { 60 | "^.+\\.(t|j)s$": "ts-jest" 61 | }, 62 | "collectCoverageFrom": [ 63 | "**/*.(t|j)s" 64 | ], 65 | "coverageDirectory": "../coverage", 66 | "testEnvironment": "node" 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /sample/012-prisma-app/prisma/schema.prisma: -------------------------------------------------------------------------------- 1 | // This is your Prisma schema file, 2 | // learn more about it in the docs: https://pris.ly/d/prisma-schema 3 | 4 | generator client { 5 | provider = "prisma-client-js" 6 | binaryTargets = ["native", "debian-openssl-1.1.x"] 7 | } 8 | 9 | datasource db { 10 | provider = "postgresql" 11 | url = "postgres://docker:default@localhost:5432/postgres" 12 | } 13 | 14 | model Sample { 15 | id String @id @default(uuid()) 16 | } 17 | -------------------------------------------------------------------------------- /sample/012-prisma-app/src/app.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { HealthModule } from './health/health.module'; 3 | 4 | @Module({ 5 | imports: [HealthModule], 6 | controllers: [], 7 | providers: [], 8 | }) 9 | export class AppModule {} 10 | -------------------------------------------------------------------------------- /sample/012-prisma-app/src/health/health.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Get } from '@nestjs/common'; 2 | import { 3 | HealthCheck, 4 | HealthCheckService, 5 | PrismaHealthIndicator, 6 | } from '@nestjs/terminus'; 7 | import { PrismaService } from 'src/prisma/prisma.service'; 8 | 9 | @Controller('health') 10 | export class HealthController { 11 | constructor( 12 | private health: HealthCheckService, 13 | private prismaHealth: PrismaHealthIndicator, 14 | private prisma: PrismaService, 15 | ) {} 16 | 17 | @Get() 18 | @HealthCheck() 19 | check() { 20 | return this.health.check([ 21 | async () => this.prismaHealth.pingCheck('prisma', this.prisma), 22 | ]); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /sample/012-prisma-app/src/health/health.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { PrismaModule } from 'src/prisma/prisma.module'; 3 | import { HealthController } from './health.controller'; 4 | import { TerminusModule } from '@nestjs/terminus'; 5 | 6 | @Module({ 7 | imports: [PrismaModule, TerminusModule], 8 | controllers: [HealthController], 9 | }) 10 | export class HealthModule {} 11 | -------------------------------------------------------------------------------- /sample/012-prisma-app/src/main.ts: -------------------------------------------------------------------------------- 1 | import { NestFactory } from '@nestjs/core'; 2 | import { AppModule } from './app.module'; 3 | 4 | async function bootstrap() { 5 | const app = await NestFactory.create(AppModule); 6 | 7 | app.enableShutdownHooks(); 8 | 9 | await app.listen(3000); 10 | } 11 | bootstrap(); 12 | -------------------------------------------------------------------------------- /sample/012-prisma-app/src/prisma/prisma.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { PrismaService } from './prisma.service'; 3 | 4 | @Module({ 5 | providers: [PrismaService], 6 | exports: [PrismaService], 7 | }) 8 | export class PrismaModule {} 9 | -------------------------------------------------------------------------------- /sample/012-prisma-app/src/prisma/prisma.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, OnModuleInit } from '@nestjs/common'; 2 | import { PrismaClient } from '@prisma/client'; 3 | 4 | @Injectable() 5 | export class PrismaService extends PrismaClient implements OnModuleInit { 6 | async onModuleInit() { 7 | await this.$connect(); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /sample/012-prisma-app/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "exclude": ["node_modules", "test", "dist", "**/*spec.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /sample/012-prisma-app/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "declaration": true, 5 | "removeComments": true, 6 | "emitDecoratorMetadata": true, 7 | "experimentalDecorators": true, 8 | "allowSyntheticDefaultImports": true, 9 | "target": "es2017", 10 | "sourceMap": true, 11 | "outDir": "./dist", 12 | "baseUrl": "./", 13 | "incremental": true, 14 | "skipLibCheck": true, 15 | "strictNullChecks": false, 16 | "noImplicitAny": false, 17 | "strictBindCallApply": false, 18 | "forceConsistentCasingInFileNames": false, 19 | "noFallthroughCasesInSwitch": false 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /sample/README.md: -------------------------------------------------------------------------------- 1 | # Terminus Samples -------------------------------------------------------------------------------- /tools/import-check.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file exists because no dynamically loaded dependencies's type definitons 3 | * are allowed in the NestJS Terminus type definitions. 4 | * 5 | */ 6 | 7 | const allowList = [ 8 | // Dependencies which are directly required by Terminus or NestJS itself 9 | '@nestjs/core', 10 | '@nestjs/common', 11 | 'rxjs', 12 | 'check-disk-space', 13 | 14 | // NodeJS std 15 | 'assert', 16 | 'buffer', 17 | 'child_process', 18 | 'console', 19 | 'cluster', 20 | 'crypto', 21 | 'dgram', 22 | 'dns', 23 | 'events', 24 | 'fs', 25 | 'http', 26 | 'http2', 27 | 'https', 28 | 'net', 29 | 'os', 30 | 'path', 31 | 'perf_hooks', 32 | 'process', 33 | 'querystring', 34 | 'readline', 35 | 'repl', 36 | 'stream', 37 | 'string_decoder', 38 | 'timers', 39 | 'tls', 40 | 'tty', 41 | 'url', 42 | 'util', 43 | 'v8', 44 | 'vm', 45 | 'wasi', 46 | 'worker', 47 | 'zlib', 48 | ]; 49 | 50 | import { rollup } from 'rollup'; 51 | import dts from 'rollup-plugin-dts'; 52 | 53 | rollup({ 54 | input: './dist/index.d.ts', 55 | output: [{ file: 'index.d.ts', format: 'es' }], 56 | plugins: [dts()], 57 | }) 58 | .then((bundle) => bundle.generate({ format: 'es' })) 59 | .then((a) => a.output[0].imports) 60 | .then((imports) => { 61 | for (const i of imports) { 62 | if (!allowList.includes(i)) { 63 | throw new Error( 64 | `Import "${i}" is not allowed in type definition files. If this is a mistake, update tools/import-check.ts`, 65 | ); 66 | } 67 | } 68 | }) 69 | .catch((err) => { 70 | // eslint-disable-next-line no-console 71 | console.error(err); 72 | process.exit(1); 73 | }); 74 | -------------------------------------------------------------------------------- /tsconfig.base.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "module": "commonjs", 5 | "strict": true, 6 | "declaration": true, 7 | "noImplicitAny": true, 8 | "removeComments": false, 9 | "noLib": false, 10 | "emitDecoratorMetadata": true, 11 | "experimentalDecorators": true, 12 | "target": "es6", 13 | "sourceMap": true, 14 | "outDir": "./dist", 15 | "rootDirs": ["./lib", "./e2e"], 16 | "alwaysStrict": true, 17 | "skipLibCheck": true 18 | }, 19 | } 20 | -------------------------------------------------------------------------------- /tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.base.json", 3 | "include": ["lib/**/*"], 4 | "exclude": ["node_modules", "**/*.spec.ts", "sample/**/*.ts", "e2e/**/*"] 5 | } 6 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.base.json", 3 | "include": ["lib/**/*", "e2e/**/*", "tools/**/*"], 4 | "exclude": ["node_modules", "sample/**/*.ts"] 5 | } 6 | --------------------------------------------------------------------------------