├── .dockerignore ├── .editorconfig ├── .gitattributes ├── .gitignore ├── .jsdoc.json ├── .npmignore ├── .npmrc ├── .taprc ├── CHANGELOG.md ├── Dockerfile-fail.example ├── Dockerfile.example ├── LICENSE ├── README.md ├── example ├── example-under-pressure-fail.js ├── example.js └── home.html ├── package.json ├── src ├── healthcheck.js └── plugin.js ├── test ├── healthcheck.test.js ├── named-import.test-d.ts └── underpressure.test.js └── types ├── .eslintrc.json ├── index.d.ts ├── index.test-d.ts └── tsconfig.json /.dockerignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | .git 4 | .gitignore 5 | 6 | .settings 7 | .classpath 8 | .project 9 | .cache* 10 | .metadata 11 | .worksheet 12 | RemoteSystemsTempFiles/ 13 | Servers/ 14 | 15 | .gradle 16 | bin/ 17 | build/ 18 | /*/build/ 19 | # dist/ 20 | 21 | *.log 22 | *.log.* 23 | *~* 24 | *.bak 25 | *.tmp 26 | *.temp 27 | Thumbs.db 28 | 29 | _old 30 | /dist 31 | # /*.min.js* 32 | 33 | /.vscode 34 | 35 | out/ 36 | tmp/ 37 | temp/ 38 | /target/ 39 | /target-eclipse/ 40 | 41 | .env 42 | local.properties 43 | 44 | # *.class 45 | 46 | node_modules 47 | npm-debug.log 48 | 49 | .nyc_output 50 | /coverage 51 | 52 | .dockerignore 53 | Dockerfile 54 | Dockerfile*.* 55 | Jenkinsfile 56 | Jenkinsfile*.* 57 | /jenkins 58 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | end_of_line = lf 7 | insert_final_newline = true 8 | indent_style = space 9 | indent_size = 2 10 | trim_trailing_whitespace = true 11 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Set default behavior to automatically convert line endings 2 | * text=auto eol=lf 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # mac files 2 | .DS_Store 3 | 4 | # environment variables file 5 | .env 6 | .envrc 7 | 8 | # IDE/editors 9 | .vscode 10 | .cache 11 | .metadata 12 | .settings 13 | .idea 14 | 15 | # Optional npm cache directory 16 | .npm 17 | 18 | # Optional REPL history 19 | .node_repl_history 20 | 21 | # Optional eslint cache 22 | .eslintcache 23 | 24 | # TypeScript cache 25 | *.tsbuildinfo 26 | 27 | # Yarn Integrity file 28 | .yarn-integrity 29 | 30 | # Clinic analysis/results 31 | profile* 32 | *clinic* 33 | *flamegraph* 34 | 35 | # Logs 36 | logs 37 | *.log 38 | npm-debug.log* 39 | yarn-debug.log* 40 | yarn-error.log* 41 | 42 | # Runtime data 43 | pids 44 | *.pid 45 | *.seed 46 | *.pid.lock 47 | 48 | # Directory for instrumented libs generated by jscoverage/JSCover 49 | lib-cov 50 | 51 | # Coverage directory used by tools like istanbul 52 | coverage 53 | 54 | # nyc test coverage 55 | .nyc_output 56 | 57 | # tap files 58 | .tap/ 59 | 60 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 61 | .grunt 62 | 63 | # Bower dependency directory (https://bower.io/) 64 | bower_components 65 | 66 | # node-waf configuration 67 | .lock-wscript 68 | 69 | # Compiled binary addons (http://nodejs.org/api/addons.html) 70 | build/Release 71 | 72 | # Dependency directories 73 | node_modules/ 74 | jspm_packages/ 75 | 76 | # Snowpack dependency directory 77 | web_modules/ 78 | 79 | # Output of 'npm pack' 80 | *.tgz 81 | 82 | # vim swap files 83 | *.swp 84 | 85 | # lock files 86 | yarn.lock 87 | package-lock.json 88 | 89 | # common files to not commit 90 | *~* 91 | *.bak 92 | Thumbs.db 93 | *.db 94 | *.zip 95 | *.tar 96 | *.tar.gz 97 | 98 | # temporary/work/output folders 99 | _old/ 100 | bin/ 101 | build/ 102 | docs/ 103 | dist/ 104 | out/ 105 | private/ 106 | temp/ 107 | tmp/ 108 | -------------------------------------------------------------------------------- /.jsdoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "source": { 3 | "include": ["./src"], 4 | "exclude": ["./example", "./test", "./lib"], 5 | "includePattern": ".+\\.js(doc|x)?$", 6 | "excludePattern": "(^|\\/|\\\\)_" 7 | }, 8 | "opts": { 9 | "template": "templates/default", 10 | "encoding": "utf8", 11 | "destination": "./docs/", 12 | "recurse": true 13 | }, 14 | "plugins": [ 15 | "plugins/summarize" 16 | ], 17 | "tags": { 18 | "allowUnknownTags": false, 19 | "dictionaries": ["jsdoc"] 20 | }, 21 | "templates": { 22 | "default": { 23 | "includeDate": false 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | .vscode 4 | .cache 5 | .metadata 6 | .settings 7 | .idea 8 | 9 | _old/ 10 | build/ 11 | docs/ 12 | lib/ 13 | logs/ 14 | out/ 15 | private/ 16 | tmp/ 17 | temp/ 18 | 19 | coverage/ 20 | .nyc_output 21 | .tap/ 22 | 23 | scripts/ 24 | src/ 25 | test/ 26 | # example/ 27 | 28 | *.zip 29 | *.tar 30 | *.tar.gz 31 | *.tgz 32 | 33 | .env 34 | .envrc 35 | .babelrc 36 | .clinic 37 | .dependabot 38 | .eslintignore 39 | .eslintrc* 40 | .eslintcache 41 | .esdoc.json 42 | .yarn-integrity 43 | .taprc 44 | 45 | .editorconfig 46 | .gitattributes 47 | .travis.yml 48 | 49 | gulpfile.* 50 | jsdoc-conf.* 51 | .jsdoc.json 52 | 53 | jenkins.sh 54 | tslint.json 55 | 56 | *.log 57 | 58 | TODO.* 59 | BUILD.md 60 | 61 | .dockerignore 62 | # Dockerfile 63 | # Dockerfile*.* 64 | Jenkinsfile 65 | Jenkinsfile*.* 66 | /jenkins 67 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | # package-lock=false 2 | -------------------------------------------------------------------------------- /.taprc: -------------------------------------------------------------------------------- 1 | # vim: set filetype=yaml : 2 | 3 | # node-arg: 4 | # - '--allow-natives-syntax' 5 | 6 | # jobs: 1 7 | comments: true 8 | # disable-coverage: true 9 | timeout: 60 10 | # typecheck: true 11 | 12 | include: 13 | - 'test/*.test.js' 14 | # - 'test/**/*.test.js' 15 | # - 'test/**/*.test.cjs' 16 | # - 'test/**/*.test.mjs' 17 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | ## [5.1.0](https://github.com/smartiniOnGitHub/fastify-favicon/releases/tag/5.1.0) (2024-11-09) 4 | [Full Changelog](https://github.com/smartiniOnGitHub/fastify-favicon/compare/5.0.0...5.1.0) 5 | Summary Changelog: 6 | - Add plugin configOptions argument to allow passing config to route 7 | - Updated all dependencies to latest 8 | 9 | ## [5.0.0](https://github.com/smartiniOnGitHub/fastify-favicon/releases/tag/5.0.0) (2024-09-22) 10 | [Full Changelog](https://github.com/smartiniOnGitHub/fastify-favicon/compare/4.5.0...5.0.0) 11 | Summary Changelog: 12 | - Updated requirements to Fastify '^5.0.0' 13 | - Updated all dependencies to latest (for Node.js 20 LTS) 14 | 15 | ## [4.4.0](https://github.com/smartiniOnGitHub/fastify-healthcheck/releases/tag/4.4.0) (2023-01-06) 16 | [Full Changelog](https://github.com/smartiniOnGitHub/fastify-healthcheck/compare/4.3.0...4.4.0) 17 | Summary Changelog: 18 | - Updated requirements to Fastify '^4.11.0' and all other dependencies to latest 19 | - Adds schema options for health endpoint 20 | 21 | ## [4.3.0](https://github.com/smartiniOnGitHub/fastify-healthcheck/releases/tag/4.3.0) (2022-12-12) 22 | [Full Changelog](https://github.com/smartiniOnGitHub/fastify-healthcheck/compare/4.2.0...4.3.0) 23 | Summary Changelog: 24 | - Updated requirements to Fastify '^4.10.2' and under-pressure '^8.2.0'; 25 | updated all other dependencies to latest 26 | - Compatibility with TypeScript 4.9 and NodeNext / ESNext 27 | 28 | ## [4.2.0](https://github.com/smartiniOnGitHub/fastify-healthcheck/releases/tag/4.2.0) (2022-09-01) 29 | [Full Changelog](https://github.com/smartiniOnGitHub/fastify-healthcheck/compare/4.1.0...4.2.0) 30 | Summary Changelog: 31 | - Updated requirements to Fastify '^4.5.3' and under-pressure '^8.1.0' 32 | - Compatibility with TypeScript 4.8 33 | - Improve plugin source docs, with JSDoc 34 | - Simplified/updated plugin tests 35 | 36 | ## [4.1.0](https://github.com/smartiniOnGitHub/fastify-healthcheck/releases/tag/4.1.0) (2022-07-19) 37 | [Full Changelog](https://github.com/smartiniOnGitHub/fastify-healthcheck/compare/4.0.0...4.1.0) 38 | Summary Changelog: 39 | - Updated requirements to Fastify '^4.0.1' 40 | - Ensure all is good as before 41 | 42 | ## [4.0.0](https://github.com/smartiniOnGitHub/fastify-healthcheck/releases/tag/4.0.0) (2022-06-13) 43 | [Full Changelog](https://github.com/smartiniOnGitHub/fastify-healthcheck/compare/3.2.0...4.0.0) 44 | Summary Changelog: 45 | - Updated requirements to Fastify '^4.0.0'; update code with related changes 46 | - Updated all dependencies to latest (for Node.js 14 LTS) 47 | - Update dependencies from 'under-pressure' to the new (scoped) 48 | package '@fastify/under-pressure' 49 | - Update and simplified example and test code 50 | - Update example Docker files to use Node.js slim image as base 51 | to reduce image size and all is good 52 | - Update documentation from sources with JSDoc 53 | 54 | ## [3.2.0](https://github.com/smartiniOnGitHub/fastify-healthcheck/releases/tag/3.2.0) (2022-06-13) 55 | [Full Changelog](https://github.com/smartiniOnGitHub/fastify-healthcheck/compare/3.1.0...3.2.0) 56 | Summary Changelog: 57 | - Updated requirements to Fastify '3.11.0' or higher (but still 3.x) 58 | - Updated all dependencies to latest (for Node.js 10 LTS) 59 | - Update Copyright year 60 | - Update Tap configuration and small simplifications in tests 61 | - Generate documentation from sources with JSDoc 62 | 63 | ## [3.1.0](https://github.com/smartiniOnGitHub/fastify-healthcheck/releases/tag/3.1.0) (2021-02-09) 64 | [Full Changelog](https://github.com/smartiniOnGitHub/fastify-favicon/compare/3.0.0...3.1.0) 65 | Summary Changelog: 66 | - Updated requirements to Under-pressure '^5.6.0' 67 | - Update all dependencies to latest, and removed 'standardx' (as dev dependency) 68 | - Fix some (now) failing tests 69 | - Fix exposure of uptime after requested the first time 70 | - Ensure compatibility with Under-pressure option to specify a custom route for healthcheck/status 71 | 72 | ## [3.0.0](https://github.com/smartiniOnGitHub/fastify-healthcheck/releases/tag/3.0.0) (2020-07-24) 73 | [Full Changelog](https://github.com/smartiniOnGitHub/fastify-favicon/compare/2.3.1...3.0.0) 74 | Summary Changelog: 75 | - Updated requirements to Fastify '^3.0.0' (as dev dependency) 76 | - Updated all dependencies to latest 77 | - Update TypeScript types 78 | 79 | ## [2.3.1](https://github.com/smartiniOnGitHub/fastify-healthcheck/releases/tag/2.3.1) (2020-06-01) 80 | Summary Changelog: 81 | - Fix TypeScript types 82 | 83 | ## [2.3.0](https://github.com/smartiniOnGitHub/fastify-healthcheck/releases/tag/2.3.0) (2020-05-26) 84 | Summary Changelog: 85 | - Add TypeScript types 86 | - Update requirements to Fastify '^2.14.1' (as dev dependency) 87 | - Update dependency on 'under-pressure' 4.x in this release (latest for Fastify 2.x) 88 | - Update other dev dependencies 89 | 90 | ## [2.2.0](https://github.com/smartiniOnGitHub/fastify-healthcheck/releases/tag/2.2.0) (2020-05-24) 91 | Summary Changelog: 92 | - Add a new plugin flag 'exposeUptime' (by default false, so disabled) 93 | to add even Node.js process uptime in the reply, when enabled 94 | - Updated requirements to Fastify '^2.8.0' (as dev dependency) 95 | - Keep dependency on 'under-pressure' 3.x in this release 96 | - Updated other dev dependencies 97 | 98 | ## [2.1.0](https://github.com/smartiniOnGitHub/fastify-healthcheck/releases/tag/2.1.0) (2019-05-08) 99 | Summary Changelog: 100 | - Updated requirements to Fastify '^2.1.0' 101 | - Updated to latest 'under-pressure', which drops support for Node.js 6 102 | - Updated all dependencies 103 | 104 | ## [2.0.0](https://github.com/smartiniOnGitHub/fastify-healthcheck/releases/tag/2.0.0) (2019-02-26) 105 | Summary Changelog: 106 | - Updated requirements to Fastify 2.x 107 | - Updated all dependencies 108 | - Note that this release number is aligned with 'under-pressure' 2.x, so for Fastify v2 109 | 110 | ## [1.0.0](https://github.com/smartiniOnGitHub/fastify-healthcheck/releases/tag/1.0.0) (2019-02-03) 111 | Summary Changelog: 112 | - Updated all dependencies 113 | - Updated Tap tests 114 | - Note that this release number means that the plugin is stable, 115 | and aligned with 'under-pressure' 1.x, and Fastify v1 116 | 117 | ## [0.3.0](https://github.com/smartiniOnGitHub/fastify-healthcheck/releases/tag/0.3.0) (2019-01-26) 118 | Summary Changelog: 119 | - Update `under-pressure` to '1.x' and update all dev dependencies to latest release 120 | - Note that the minor release change here is to let consumers of this package 121 | to have more evidence of underlying library release changes 122 | 123 | ## [0.2.0](https://github.com/smartiniOnGitHub/fastify-healthcheck/releases/tag/0.2.0) (2018-11-27) 124 | Summary Changelog: 125 | - Delegate to the [under-pressure](https://www.npmjs.com/package/under-pressure) 126 | plugin the logic to report the status of the web application 127 | - To fully encapsulate `under-pressure` features, removed the dependency 128 | on [fastify-plugin](https://github.com/fastify/fastify-plugin); 129 | for more info look [here](https://github.com/fastify/fastify/blob/master/docs/Plugins.md#handle-the-scope), 130 | [here](https://github.com/fastify/fastify/blob/master/docs/Plugins-Guide.md#how-to-handle-encapsulation-and-distribution) 131 | - Update Fastify dependencies to '1.1.0' or higher (but on 1.x), 132 | but without `fastify-plugin` to check it now (see related changes) 133 | - Add another example (`example-under-pressure-fail`) with `under-pressure` 134 | configured to always return a `Service Unavailable` error (HTTP 503), 135 | and related `Dockerfile-fail.example` to simplify its usage (as a sample) 136 | - Update `healthcheck` standalone script to return 0 for success, 137 | or the HTTP error code in case of failure (or 1 in case of other failure) 138 | 139 | ## [0.1.1](https://github.com/smartiniOnGitHub/fastify-healthcheck/releases/tag/0.1.1) (2018-11-13) 140 | Summary Changelog: 141 | - Maintenance release to fix Fastify dependencies to '1.x', 142 | to avoid breaking changes because Fastify '2.x' will be released soon 143 | - Updated dependencies to latest Fastify plugin (1.2.1) 144 | and Fastify 1.x (1.13.0) 145 | 146 | ## [0.1.0](https://github.com/smartiniOnGitHub/fastify-healthcheck/releases/tag/0.1.0) (2018-10-31) 147 | Summary Changelog: 148 | - First release, with basic features: 149 | expose an healthcheck route, 150 | add a script to check the health status by calling that route via HTTP GET 151 | - Ability to override some features, with plugin options 152 | - Add a sample webapp using it 153 | - Add a sample Dockerfile with the HEALTHCHECK directive 154 | - Add sample Docker commands to show and simplify its usage 155 | - Add some documentation 156 | 157 | ---- 158 | -------------------------------------------------------------------------------- /Dockerfile-fail.example: -------------------------------------------------------------------------------- 1 | FROM node:20-slim 2 | 3 | LABEL version="1.0.0" 4 | LABEL description="Example Fastify (Node.js) Docker Image" 5 | LABEL maintainer "Sandro Martini " 6 | 7 | RUN mkdir -p /work 8 | WORKDIR /work 9 | 10 | ARG NODE_ENV 11 | ENV NODE_ENV $NODE_ENV 12 | # ENV NODE_ENV production 13 | # to be able to run tests (for example in CI), do not set production as environment 14 | 15 | ENV NPM_CONFIG_LOGLEVEL warn 16 | 17 | # copy project definition/dependencies files, for better reuse of layers 18 | COPY ./package*.json ./$WORKDIR 19 | 20 | # install dependencies here, for better reuse of layers 21 | RUN npm install 22 | # RUN npm install --only=production 23 | 24 | # copy all sources in the container (exclusions in .dockerignore file) 25 | COPY . $WORKDIR 26 | 27 | EXPOSE 3000 28 | 29 | # add an healthcheck, useful 30 | # healthcheck with curl, but not recommended 31 | # HEALTHCHECK CMD curl --fail http://localhost:3000/health || exit 1 32 | # healthcheck by calling the additional script exposed by the plugin 33 | HEALTHCHECK --interval=30s --timeout=10s --start-period=5s CMD npm start 34 | 35 | CMD [ "npm", "run", "example-under-pressure-fail" ] 36 | -------------------------------------------------------------------------------- /Dockerfile.example: -------------------------------------------------------------------------------- 1 | FROM node:20-slim 2 | 3 | LABEL version="1.0.0" 4 | LABEL description="Example Fastify (Node.js) Docker Image" 5 | LABEL maintainer "Sandro Martini " 6 | 7 | RUN mkdir -p /work 8 | WORKDIR /work 9 | 10 | ARG NODE_ENV 11 | ENV NODE_ENV $NODE_ENV 12 | # ENV NODE_ENV production 13 | # to be able to run tests (for example in CI), do not set production as environment 14 | 15 | ENV NPM_CONFIG_LOGLEVEL warn 16 | 17 | # copy project definition/dependencies files, for better reuse of layers 18 | COPY ./package*.json ./$WORKDIR 19 | 20 | # install dependencies here, for better reuse of layers 21 | RUN npm install 22 | # RUN npm install --only=production 23 | 24 | # copy all sources in the container (exclusions in .dockerignore file) 25 | COPY . $WORKDIR 26 | 27 | EXPOSE 3000 28 | 29 | # add an healthcheck, useful 30 | # healthcheck with curl, but not recommended 31 | # HEALTHCHECK CMD curl --fail http://localhost:3000/health || exit 1 32 | # healthcheck by calling the additional script exposed by the plugin 33 | HEALTHCHECK --interval=30s --timeout=10s --start-period=5s CMD npm start 34 | 35 | CMD [ "npm", "run", "example" ] 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # fastify-healthcheck 2 | 3 | [![NPM Version](https://img.shields.io/npm/v/fastify-healthcheck.svg?style=flat)](https://npmjs.org/package/fastify-healthcheck/) 4 | [![NPM Downloads](https://img.shields.io/npm/dm/fastify-healthcheck.svg?style=flat)](https://npmjs.org/package/fastify-healthcheck/) 5 | [![Code Style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat)](http://standardjs.com/) 6 | 7 | Fastify Plugin to serve responses that report about the web application, 8 | if it's still running and alive (health checks). 9 | 10 | This is very useful with Containers like `Docker` 11 | and orchestrators like `Kubernetes`. 12 | 13 | With this plugin, Fastify by default expose an healthcheck route configured 14 | for `/health` GET requests, and even a script that can be executed to get 15 | content via HTTP GET from that running web application. 16 | 17 | 18 | ## Usage 19 | 20 | The plugin can be used without specifying options, so good default values 21 | will be used, but if needed can be specified: 22 | - `healthcheckUrl`, to set a different uri for the healthcheck route 23 | - `healthcheckUrlDisable`, to not publish the healthcheck route 24 | - `healthcheckUrlAlwaysFail`, to always return failure responses (useful to test failure responses) 25 | - `exposeUptime`, to return even Node.js process uptime (by default disabled) 26 | - `underPressureOptions`, for options to send directly to under-pressure 27 | - `schemaOptions`, for options to use for route schema (no default value provided) 28 | 29 | Under the hood, the healthcheck status is determined by the 30 | [@fastify/under-pressure](https://www.npmjs.com/package/@fastify/under-pressure) plugin, 31 | used here as a dependency; so it's possible to specify all its 32 | configuration options in related option. 33 | 34 | To use all default values for `healthcheck` options, do not set its options 35 | (or set with undefined values); in that way no `under-pressure` specific 36 | options will be overridden by them. 37 | 38 | 39 | Sample usage: 40 | 41 | ```js 42 | const fastify = require('fastify')() 43 | 44 | // example without specifying options, returning a default healthcheck 45 | // route mapped to '/health' that only reply to a GET request 46 | fastify.register(require('fastify-healthcheck')) 47 | // or 48 | // example with custom healthcheck url and response to always fail 49 | // fastify.register(require('fastify-healthcheck'), { healthcheckUrl: '/custom-health', healthcheckUrlAlwaysFail: true }) 50 | // 51 | 52 | fastify.listen({ port: 3000, host: 'localhost' }) 53 | 54 | // To test, for example (in another terminal session) do: 55 | // `npm start`, or 56 | // `curl http://127.0.0.1:3000/health` => returning an HTTP response 200 (OK) 57 | // and a JSON response like: {"statusCode":200,"status":"ok"} 58 | // or run the healthcheck script, for example with: 59 | // `node src/healthcheck http://localhost:3000/health` 60 | // and get the same HTTP response seen before 61 | ``` 62 | 63 | In the [example](./example/) folder there is a simple server scripts that 64 | uses the plugin (inline but it's the same using it from npm registry). 65 | 66 | The file `Dockerfile.example` is a sample container definition for 67 | the example webapp (using the plugin) to show Docker HEALTHCHECK directive 68 | both using 'curl' (but commented) and calling the healthcheck script 69 | available by the plugin. 70 | For convenience, all Docker commands have been defined in `package.json`, 71 | to run many of them in a simple way (with `npm run custom-command`), 72 | like in the following sequence: 73 | - `docker:build`, to build the image, where the entry point is the example 74 | - `docker:build:fail`, to build the image, but as entry point the example 75 | that is triggering the `Service Unavailable` error (HTTP 503) in the 76 | healthcheck route 77 | - `docker:run`, to start the container from generated image, 78 | in detached mode 79 | - `docker:healthcheck-manual`, to run the healthcheck script in the 80 | container but manually 81 | - `docker:status`, to get the health status of the container 82 | - and others like: `docker:inspect` (interactive), `docker:log` 83 | (C to close), `docker:process`, etc ... 84 | - `docker:stop`, to stop running container 85 | - `docker:clean`, to remove generated image 86 | 87 | 88 | ## Requirements 89 | 90 | Fastify ^5.0.0 , Node.js 20 LTS (20.9.0) or later. 91 | Note that plugin releases 4.x are for Fastify 4.x, 5.x for Fastify 5.x, etc. 92 | 93 | 94 | ## Sources 95 | 96 | Source code is all inside main repo: 97 | [fastify-healthcheck](https://github.com/smartiniOnGitHub/fastify-healthcheck). 98 | 99 | Documentation generated from source code (library API): 100 | [here](https://smartiniongithub.github.io/fastify-healthcheck/). 101 | 102 | 103 | ## Note 104 | 105 | To fully encapsulate `under-pressure` features inside the scope 106 | of this plugin, the plugin is not exposed by [fastify-plugin](https://github.com/fastify/fastify-plugin); 107 | for more info look [here](https://github.com/fastify/fastify/blob/master/docs/Plugins.md#handle-the-scope), [here](https://github.com/fastify/fastify/blob/master/docs/Plugins-Guide.md#how-to-handle-encapsulation-and-distribution). 108 | 109 | The plugin map a default endpoint on the URI `/health` to be 110 | called via GET, but it's possible to change it with the setting 'url' 111 | in plugin options. 112 | 113 | The plugin exposes even another script that tries to get some content 114 | (via HTTP GET) from the current web application where it's running. 115 | In a container environment this could be useful to let containers runtime 116 | do the healthcheck without the need to use other tools 117 | like `curl` or `wget` that must be available in the container. 118 | 119 | Both approaches could be useful in most common cases, like 120 | Kubernetes HTTP GET, or Kubernetes EXEC or Docker HEALTHCHECK, 121 | or others similar. 122 | 123 | Note that the healthcheck script gets the URL to call from the command-line, 124 | but if not specified it will use a default value of 125 | [http://localhost:3000/health](http://localhost:3000/health). 126 | 127 | To execute the healthcheck script from another Node.js project/package, 128 | you need to run something like: 129 | `node node_modules/fastify-healthcheck/src/healthcheck http://localhost:8000/health`, 130 | with the webapp exposed to the port `8000` in this case. 131 | 132 | 133 | ## License 134 | 135 | Licensed under [Apache-2.0](./LICENSE). 136 | 137 | ---- 138 | -------------------------------------------------------------------------------- /example/example-under-pressure-fail.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-2024 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 'use strict' 17 | 18 | // example web application, but with under-pressure configured 19 | // to always return a `Service Unavailable` error (HTTP 503) 20 | 21 | const fastify = require('fastify')() 22 | 23 | fastify.register(require('../src/plugin'), { 24 | underPressureOptions: { 25 | maxEventLoopDelay: 100, 26 | maxHeapUsedBytes: 1, 27 | // maxRssBytes: 2, 28 | message: 'Under pressure!', // sample custom message 29 | retryAfter: 50 30 | } // set some under-pressure specific options set here 31 | }) // configure this plugin with some custom options 32 | 33 | // example to handle a sample home request to serve a static page, optional here 34 | fastify.get('/', function (req, reply) { 35 | const path = require('node:path') 36 | const scriptRelativeFolder = path.join(__dirname, path.sep) 37 | const fs = require('node:fs') 38 | const stream = fs.createReadStream(path.join(scriptRelativeFolder, 'home.html')) 39 | reply.type('text/html; charset=utf-8').send(stream) 40 | }) 41 | 42 | fastify.listen({ port: 3000, host: '0.0.0.0' }, (err, address) => { 43 | if (err) throw err 44 | console.log(`Server listening on ${address}`) 45 | }) 46 | 47 | fastify.ready(() => { 48 | const routes = fastify.printRoutes() 49 | console.log(`Available Routes:\n${routes}`) 50 | }) 51 | -------------------------------------------------------------------------------- /example/example.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-2024 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 'use strict' 17 | 18 | const fastify = require('fastify')() 19 | 20 | // register plugin with all its options shown but commented (as a sample) 21 | fastify.register(require('../src/plugin'), { 22 | // healthcheckUrl: '/custom-health', 23 | // healthcheckUrlDisable: true, 24 | // healthcheckUrlAlwaysFail: true, 25 | // exposeUptime: true, 26 | // underPressureOptions: { } // no under-pressure specific options set here 27 | exposeUptime: true // enable, as a sample 28 | }) 29 | 30 | // example to handle a sample home request to serve a static page, optional here 31 | fastify.get('/', function (req, reply) { 32 | const path = require('node:path') 33 | const scriptRelativeFolder = path.join(__dirname, path.sep) 34 | const fs = require('node:fs') 35 | const stream = fs.createReadStream(path.join(scriptRelativeFolder, 'home.html')) 36 | reply.type('text/html; charset=utf-8').send(stream) 37 | }) 38 | 39 | fastify.listen({ port: 3000, host: '0.0.0.0' }, (err, address) => { 40 | if (err) throw err 41 | console.log(`Server listening on ${address}`) 42 | }) 43 | 44 | fastify.ready(() => { 45 | const routes = fastify.printRoutes() 46 | console.log(`Available Routes:\n${routes}`) 47 | }) 48 | -------------------------------------------------------------------------------- /example/home.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Home - Example pages 6 | 7 | 8 | 9 |

Welcome to the Home page.

10 |
11 | 12 | Plugin routes to call: 13 | 16 |
17 | 18 |
19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fastify-healthcheck", 3 | "version": "5.1.0", 4 | "description": "Fastify Plugin to serve responses for health checks", 5 | "main": "src/plugin", 6 | "types": "types/index.d.ts", 7 | "scripts": { 8 | "audit:log": "npm audit > ./temp/audit.log", 9 | "clean:install": "rm -rf ./package-lock.json ./node_modules/", 10 | "dependency:log": "npm list > ./temp/dependencies.log", 11 | "docker:build:fail": "docker build -t fastify-healthcheck-example -f Dockerfile-fail.example .", 12 | "docker:build": "docker build -t fastify-healthcheck-example -f Dockerfile.example .", 13 | "docker:clean": "docker rmi fastify-healthcheck-example", 14 | "docker:healthcheck-manual": "docker exec -it fastify-healthcheck-example node src/healthcheck", 15 | "docker:inspect": "docker exec -it fastify-healthcheck-example bash", 16 | "docker:log": "docker logs --follow --tail=1000 fastify-healthcheck-example", 17 | "docker:process": "docker ps --filter name=fastify-healthcheck-example", 18 | "docker:run": "docker run --rm --name fastify-healthcheck-example -d -p 3000:3000 -t fastify-healthcheck-example", 19 | "docker:status": "docker inspect --format '{{ json .State.Health }}' fastify-healthcheck-example", 20 | "docker:stop": "docker kill fastify-healthcheck-example", 21 | "docs:clean": "rm -rf ./docs/*", 22 | "docs:generate": "npx jsdoc -c .jsdoc.json -R README.md", 23 | "docs": "npm run docs:clean && npm run docs:generate", 24 | "example-under-pressure-fail": "node example/example-under-pressure-fail", 25 | "example": "node example/example", 26 | "lint:fix": "standard --fix", 27 | "lint:standard": "standard --verbose", 28 | "lint:typescript": "standard --parser @typescript-eslint/parser --plugin @typescript-eslint/eslint-plugin test/*.ts", 29 | "lint:typescript:eslint": "eslint -c types/.eslintrc.json types/*.d.ts types/*.test-d.ts", 30 | "lint:typescript:eslint:fix": "eslint -c types/.eslintrc.json types/*.d.ts types/*.test-d.ts --fix", 31 | "lint": "npm run lint:standard && npm run lint:typescript", 32 | "start": "node src/healthcheck", 33 | "test:clean": "rm -rf .nyc_output/* .tap/* ./coverage/*", 34 | "test:coverage:all": "npm run test:unit -- --coverage-report=text", 35 | "test:coverage": "npm run test:unit -- --coverage-report=html", 36 | "test:types": "tsd", 37 | "test:unit:debug": "tap -T --node-arg=--inspect-brk", 38 | "test:unit:dev": "tap --watch --coverage-report=none", 39 | "test:unit": "tap --allow-incomplete-coverage", 40 | "test": "npm run lint:standard && npm run lint:typescript && npm run test:types && npm run test:unit" 41 | }, 42 | "dependencies": { 43 | "@fastify/under-pressure": "^9.0.1" 44 | }, 45 | "devDependencies": { 46 | "@types/node": "^22.9.0", 47 | "@typescript-eslint/eslint-plugin": "^8.13.0", 48 | "@typescript-eslint/parser": "^8.13.0", 49 | "fastify": "^5.0.0", 50 | "jsdoc": "^4.0.4", 51 | "simple-get": "^4.0.1", 52 | "standard": "^17.1.2", 53 | "tap": "^21.0.1", 54 | "tsd": "^0.31.2" 55 | }, 56 | "standard": { 57 | "ignore": [ 58 | "types/*" 59 | ] 60 | }, 61 | "tsd": { 62 | "directory": "types", 63 | "compilerOptions": { 64 | "esModuleInterop": true, 65 | "strict": true 66 | } 67 | }, 68 | "peerDependencies": {}, 69 | "engines": { 70 | "node": ">=20.9.0" 71 | }, 72 | "homepage": "https://github.com/smartiniOnGitHub/fastify-healthcheck#readme", 73 | "repository": { 74 | "type": "git", 75 | "url": "git+https://github.com/smartiniOnGitHub/fastify-healthcheck.git" 76 | }, 77 | "bugs": { 78 | "url": "https://github.com/smartiniOnGitHub/fastify-healthcheck/issues" 79 | }, 80 | "keywords": [ 81 | "fastify", 82 | "plugin", 83 | "healthcheck" 84 | ], 85 | "author": "Sandro Martini ", 86 | "license": "Apache-2.0" 87 | } 88 | -------------------------------------------------------------------------------- /src/healthcheck.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-2024 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 'use strict' 17 | 18 | // standalone script, to get content from the running web application 19 | // for example it can be called by container health checks 20 | // return code will be 0 for success, or the HTTP error code 21 | 22 | // use Node.js 'http' integrated module, 23 | // even to avoid dependencies clash 24 | const http = require('node:http') 25 | 26 | const options = { 27 | timeout: 5000, // 5 sec 28 | log: true // if enabled, write log to console 29 | } 30 | const url = process.argv[2] || 'http://localhost:3000/health' 31 | if (options.log === true) { 32 | console.log(`GET call for healthcheck at: ${url} ...`) 33 | } 34 | 35 | const request = http.get(url, (res) => { 36 | if (options.log === true) { 37 | console.log(`statusCode: ${res.statusCode}`) 38 | if (res.statusMessage) { 39 | console.log(`statusMessage: '${res.statusMessage}'`) 40 | } 41 | console.log('----------------') 42 | } 43 | if (res.statusCode === 200) { 44 | process.exit(0) 45 | } else { 46 | process.exit(res.statusCode || 1) 47 | } 48 | }) 49 | request.setTimeout(options.timeout) 50 | 51 | request.on('error', (err) => { 52 | if (options.log === true) { 53 | console.log(`error: ${err.message}`) 54 | } 55 | process.exit(err.statusCode || 1) 56 | }) 57 | 58 | request.end() 59 | -------------------------------------------------------------------------------- /src/plugin.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-2024 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 'use strict' 17 | 18 | /** 19 | * Plugin: 20 | * this module exports the plugin as a function. 21 | * @module plugin 22 | */ 23 | 24 | const payloadOK = { statusCode: 200, status: 'ok' } 25 | const payloadKO = { statusCode: 500, status: 'ko' } 26 | 27 | /** 28 | * Plugin implementation. 29 | * 30 | * @param {!object} fastify Fastify instance 31 | * @param {!object} options plugin configuration options 32 | *
    33 | *
  • healthcheckUrl (string, default `/health`) for the route of the health check,
  • 34 | *
  • healthcheckUrlDisable (boolean, default false) flag to disable health check route,
  • 35 | *
  • healthcheckUrlAlwaysFail (boolean, default false) flag to always return failure from health check route (mainly for testing purposes),
  • 36 | *
  • exposeUptime (boolean, default false) flag to show even process uptime in health check results,
  • 37 | *
  • underPressureOptions (object, default empty) for options to send directly to under-pressure,
  • 38 | *
  • schemaOptions (object, default empty) for options to use for route schema,
  • 39 | *
  • configOptions (object, default empty) for options to use for route config,
  • 40 | *
41 | * @param {!function} done callback, to call as last step 42 | * 43 | * @namespace 44 | */ 45 | function fastifyHealthcheck (fastify, options, done) { 46 | const { 47 | healthcheckUrl = '/health', 48 | healthcheckUrlDisable = false, 49 | healthcheckUrlAlwaysFail = false, 50 | exposeUptime = false, 51 | underPressureOptions = { }, 52 | schemaOptions, 53 | configOptions 54 | } = options 55 | 56 | ensureIsString(healthcheckUrl, 'healthcheckUrl') 57 | ensureIsBoolean(healthcheckUrlDisable, 'healthcheckUrlDisable') 58 | ensureIsBoolean(healthcheckUrlAlwaysFail, 'healthcheckUrlAlwaysFail') 59 | ensureIsBoolean(exposeUptime, 'exposeUptime') 60 | ensureIsObject(underPressureOptions, 'underPressureOptions') 61 | if (schemaOptions) { 62 | ensureIsObject(schemaOptions, 'schemaOptions') 63 | } 64 | if (configOptions) { 65 | ensureIsObject(configOptions, 'configOptions') 66 | } 67 | 68 | // execute plugin code 69 | 70 | // register under-pressure plugin 71 | // note that it will trigger automatic failure responses 72 | // in all routes defined here when current values are higher 73 | // that threshold values set 74 | fastify.register(require('@fastify/under-pressure'), underPressureOptions) 75 | 76 | let healthcheckHandler = normalHandler 77 | if (exposeUptime === true) { 78 | healthcheckHandler = normalHandlerWithUptime 79 | } 80 | if (healthcheckUrlAlwaysFail !== null && healthcheckUrlAlwaysFail === true) { 81 | healthcheckHandler = failHandler 82 | } 83 | 84 | if (healthcheckUrlDisable === null || healthcheckUrlDisable === false) { 85 | fastify.route({ 86 | method: 'GET', 87 | url: healthcheckUrl, 88 | handler: healthcheckHandler, 89 | schema: schemaOptions, 90 | config: configOptions 91 | }) 92 | } 93 | 94 | done() 95 | } 96 | 97 | /** 98 | * Handler that generates a failure response. 99 | * 100 | * @param {!req} req the request 101 | * @param {!reply} reply the response 102 | * 103 | * @private 104 | */ 105 | function failHandler (req, reply) { 106 | reply.code(500).send(payloadKO) 107 | } 108 | 109 | /** 110 | * Handler that generates a success response. 111 | * 112 | * @param {!req} req the request 113 | * @param {!reply} reply the response 114 | * 115 | * @private 116 | */ 117 | function normalHandler (req, reply) { 118 | reply.code(200).send(payloadOK) 119 | } 120 | 121 | /** 122 | * Handler that generates a success response, and show even process uptime. 123 | * 124 | * @param {!req} req the request 125 | * @param {!reply} reply the response 126 | * 127 | * @private 128 | */ 129 | function normalHandlerWithUptime (req, reply) { 130 | reply.code(200).send({ ...payloadOK, uptime: process.uptime() }) 131 | } 132 | 133 | // utility functions 134 | 135 | function ensureIsString (arg, name) { 136 | if (arg !== null && typeof arg !== 'string') { 137 | throw new TypeError(`The argument '${name}' must be a string, instead got a '${typeof arg}'`) 138 | } 139 | } 140 | 141 | function ensureIsBoolean (arg, name) { 142 | if (arg !== null && typeof arg !== 'boolean') { 143 | throw new TypeError(`The argument '${name}' must be a boolean, instead got a '${typeof arg}'`) 144 | } 145 | } 146 | 147 | function ensureIsObject (arg, name) { 148 | if (arg !== null && typeof arg !== 'object') { 149 | throw new TypeError(`The argument '${name}' must be a object, instead got a '${typeof arg}'`) 150 | } 151 | } 152 | 153 | // not using fastify-plugin, to fully encapsulate under-pressure plugin 154 | module.exports = fastifyHealthcheck 155 | -------------------------------------------------------------------------------- /test/healthcheck.test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-2024 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 'use strict' 17 | 18 | const { monitorEventLoopDelay } = require('node:perf_hooks') 19 | const process = require('node:process') 20 | 21 | const test = require('tap').test 22 | const sget = require('simple-get').concat 23 | const Fastify = require('fastify') 24 | const healthcheckPlugin = require('..') 25 | 26 | function sleep (ms) { 27 | return new Promise((resolve) => { 28 | setTimeout(resolve, ms) 29 | }) 30 | } 31 | 32 | function block (msec) { 33 | const start = Date.now() 34 | /* eslint-disable no-empty */ 35 | while (Date.now() - start < msec) { } 36 | } 37 | 38 | const jsonMimeType = 'application/json; charset=utf-8' 39 | const successResponse = { statusCode: 200, status: 'ok' } 40 | const failureResponse = { statusCode: 500, status: 'ko' } 41 | 42 | test('healthcheck with all defaults: does not return an error, but a good response (200) and some content', async (t) => { 43 | // t.plan(3) 44 | const fastify = Fastify() 45 | t.after(() => { fastify.close() }) 46 | await fastify.register(healthcheckPlugin) // configure this plugin with its default options 47 | await fastify.listen({ port: 0 }) 48 | 49 | // process.nextTick(() => sleep(500)) // not really needed, but could be useful to have 50 | const response = await fastify.inject({ 51 | method: 'GET', 52 | timeout: 2000, 53 | url: '/health' 54 | }) 55 | t.equal(response.statusCode, 200) 56 | t.equal(response.headers['content-type'], jsonMimeType) 57 | t.same(JSON.parse(response.body), successResponse) 58 | t.end() 59 | }) 60 | 61 | test('healthcheck on a custom route: does not return an error, but a good response (200) and some content', async (t) => { 62 | const fastify = Fastify() 63 | t.after(() => { fastify.close() }) 64 | await fastify.register(healthcheckPlugin, { 65 | healthcheckUrl: '/custom-health' 66 | }) // configure this plugin with some custom options 67 | await fastify.listen({ port: 0 }) 68 | 69 | { 70 | const response = await fastify.inject({ 71 | method: 'GET', 72 | timeout: 2000, 73 | url: '/custom-health' 74 | }) 75 | t.equal(response.statusCode, 200) 76 | t.equal(response.headers['content-type'], jsonMimeType) 77 | t.same(JSON.parse(response.body), successResponse) 78 | } 79 | 80 | { 81 | // ensure default url is not exposed 82 | const response = await fastify.inject({ 83 | method: 'GET', 84 | timeout: 2000, 85 | url: '/health' 86 | }) 87 | t.equal(response.statusCode, 404) 88 | t.equal(response.headers['content-type'], jsonMimeType) 89 | t.same(JSON.parse(response.body), { message: 'Route GET:/health not found', error: 'Not Found', statusCode: 404 }) 90 | } 91 | 92 | t.end() 93 | }) 94 | 95 | test('healthcheck on a disabled route (default or custom): return a not found error (404) and some content', async (t) => { 96 | const fastify = Fastify() 97 | t.after(() => { fastify.close() }) 98 | await fastify.register(healthcheckPlugin, { 99 | healthcheckUrl: '/custom-health', 100 | healthcheckUrlDisable: true 101 | }) // configure this plugin with some custom options 102 | 103 | await fastify.listen({ port: 0 }) 104 | 105 | { 106 | const response = await fastify.inject({ 107 | method: 'GET', 108 | timeout: 2000, 109 | url: '/custom-health' 110 | }) 111 | t.equal(response.statusCode, 404) 112 | t.equal(response.headers['content-type'], jsonMimeType) 113 | t.equal(JSON.parse(response.body).statusCode, 404) 114 | t.equal(JSON.parse(response.body).error, 'Not Found') 115 | t.equal(JSON.parse(response.body).message, 'Route GET:/custom-health not found') 116 | } 117 | 118 | { 119 | // ensure default url is not exposed 120 | const response = await fastify.inject({ 121 | method: 'GET', 122 | timeout: 2000, 123 | url: '/health' 124 | }) 125 | t.equal(response.statusCode, 404) 126 | t.equal(response.headers['content-type'], jsonMimeType) 127 | t.equal(JSON.parse(response.body).statusCode, 404) 128 | t.equal(JSON.parse(response.body).error, 'Not Found') 129 | t.equal(JSON.parse(response.body).message, 'Route GET:/health not found') 130 | } 131 | 132 | t.end() 133 | }) 134 | 135 | test('healthcheck with always failure flag: always return an error, (500) and some content', async (t) => { 136 | const fastify = Fastify() 137 | t.after(() => { fastify.close() }) 138 | await fastify.register(healthcheckPlugin, { 139 | // 'healthcheckUrl': '/custom-health', 140 | healthcheckUrlAlwaysFail: true 141 | }) // configure this plugin with some custom options 142 | 143 | await fastify.listen({ port: 0 }) 144 | 145 | const response = await fastify.inject({ 146 | method: 'GET', 147 | timeout: 2000, 148 | url: '/health' 149 | }) 150 | t.equal(response.statusCode, 500) 151 | t.equal(response.headers['content-type'], jsonMimeType) 152 | t.same(JSON.parse(response.body), failureResponse) 153 | 154 | t.end() 155 | }) 156 | 157 | test('healthcheck with healthcheck option enabled to return even process uptime: ensure a good response (200) will be returned', async (t) => { 158 | const fastify = Fastify() 159 | t.after(() => { fastify.close() }) 160 | await fastify.register(healthcheckPlugin, { 161 | exposeUptime: true 162 | }) // configure this plugin with some custom options 163 | 164 | await fastify.listen({ port: 0 }) 165 | 166 | const response = await fastify.inject({ 167 | method: 'GET', 168 | timeout: 2000, 169 | url: '/health' 170 | }) 171 | t.equal(response.statusCode, 200) 172 | t.equal(response.headers['content-type'], jsonMimeType) 173 | const payload = JSON.parse(response.body) 174 | t.equal(payload.statusCode, 200) 175 | t.equal(payload.status, 'ok') 176 | t.ok(payload.uptime > 0.0) 177 | t.end() 178 | }) 179 | 180 | test('healthcheck with all healthcheck specific options undefined: does not return an error, but a good response (200) and under-pressure defaults', async (t) => { 181 | const fastify = Fastify() 182 | t.after(() => { fastify.close() }) 183 | await fastify.register(healthcheckPlugin, { 184 | healthcheckUrl: undefined, 185 | healthcheckUrlDisable: undefined, 186 | healthcheckUrlAlwaysFail: undefined, 187 | exposeUptime: undefined, 188 | underPressureOptions: { } // no under-pressure specific options set here 189 | }) // configure this plugin with some custom options 190 | 191 | await fastify.listen({ port: 0 }) 192 | 193 | const response = await fastify.inject({ 194 | method: 'GET', 195 | timeout: 2000, 196 | url: '/health' 197 | }) 198 | t.equal(response.statusCode, 200) 199 | t.equal(response.headers['content-type'], jsonMimeType) 200 | const payload = JSON.parse(response.body) 201 | t.equal(payload.statusCode, 200) 202 | t.equal(payload.status, 'ok') 203 | t.notOk(payload.uptime) // not present in this reply payload 204 | t.same(payload, successResponse) 205 | t.end() 206 | }) 207 | 208 | test('healthcheck with only some under-pressure options defined: does not return an error, but a good response (200) and some content', async (t) => { 209 | const fastify = Fastify() 210 | t.after(() => { fastify.close() }) 211 | await fastify.register(healthcheckPlugin, { 212 | underPressureOptions: { 213 | // exposeStatusRoute: true, // no effect here 214 | maxEventLoopDelay: 1000, 215 | maxHeapUsedBytes: 100000000, 216 | maxRssBytes: 100000000, 217 | // message: 'Under pressure!', // custom message 218 | retryAfter: 50 219 | } // set some under-pressure specific options set here 220 | }) // configure this plugin with some custom options 221 | 222 | await fastify.listen({ port: 0 }) 223 | 224 | t.ok(!fastify.memoryUsage) // ensure is not exposed 225 | // process.nextTick(() => sleep(500)) 226 | const response = await fastify.inject({ 227 | method: 'GET', 228 | timeout: 2000, 229 | url: '/health' 230 | }) 231 | t.equal(response.statusCode, 200) 232 | t.equal(response.headers['content-type'], jsonMimeType) 233 | t.same(JSON.parse(response.body), successResponse) 234 | 235 | t.end() 236 | }) 237 | 238 | test('healthcheck with some under-pressure options defined for a custom response on healthcheck: does not return an error, but a good response (200) and some content', async (t) => { 239 | const fastify = Fastify() 240 | t.after(() => { fastify.close() }) 241 | await fastify.register(healthcheckPlugin, { 242 | underPressureOptions: { 243 | // set a custom healthcheck route, for example async 244 | healthCheck: async () => { 245 | t.pass('healthcheck called') 246 | return { 247 | some: 'value', 248 | anotherValue: 'another', 249 | status: 'override healthcheck response' 250 | } 251 | }, 252 | // set the schema for the custom healthcheck route, optional but reccomended 253 | exposeStatusRoute: { 254 | routeResponseSchemaOpts: { 255 | some: { type: 'string' }, 256 | anotherValue: { type: 'string' } 257 | } 258 | } 259 | } // set some under-pressure specific options set here 260 | }) // configure this plugin with some custom options 261 | 262 | await fastify.listen({ port: 0 }) 263 | 264 | { 265 | // test usual plugin healthcheck route 266 | process.nextTick(() => sleep(500)) 267 | const response = await fastify.inject({ 268 | method: 'GET', 269 | timeout: 2000, 270 | url: '/health' 271 | }) 272 | t.equal(response.statusCode, 200) 273 | t.equal(response.headers['content-type'], jsonMimeType) 274 | const payload = JSON.parse(response.body) 275 | t.notOk(payload.uptime) // not present in this reply payload 276 | t.same(JSON.parse(response.body), successResponse) 277 | } 278 | 279 | { 280 | // test under-pressure healthcheck route 281 | process.nextTick(() => sleep(500)) 282 | const response = await fastify.inject({ 283 | method: 'GET', 284 | timeout: 2000, 285 | url: '/status' 286 | }) 287 | t.equal(response.statusCode, 200) 288 | t.equal(response.headers['content-type'], jsonMimeType) 289 | const payload = JSON.parse(response.body) 290 | t.notOk(payload.uptime) // not present in this reply payload 291 | t.same(payload, { 292 | some: 'value', 293 | anotherValue: 'another', 294 | status: 'override healthcheck response' 295 | }) 296 | } 297 | 298 | t.end() 299 | }) 300 | 301 | test('healthcheck with only some under-pressure options defined to always fail: return an error (503) and some content', (t) => { 302 | const fastify = Fastify() 303 | t.after(() => { fastify.close() }) 304 | fastify.register(healthcheckPlugin, { 305 | underPressureOptions: { 306 | // exposeStatusRoute: true, // no effect here 307 | maxEventLoopDelay: 100, 308 | maxHeapUsedBytes: 1, 309 | // maxRssBytes: 2, 310 | message: 'Under pressure!', // sample custom message 311 | retryAfter: 50 312 | } // set some under-pressure specific options set here 313 | }) // configure this plugin with some custom options 314 | 315 | fastify.listen({ port: 0 }, (err, address) => { 316 | if (err) t.assert.ifError(err) 317 | t.ok(!fastify.memoryUsage) // ensure is not exposed 318 | 319 | // process.nextTick(() => sleep(500)) // does not work anymore here 320 | process.nextTick(() => block(monitorEventLoopDelay ? 1500 : 500)) 321 | 322 | // can't use fastify inject here ... 323 | sget({ 324 | method: 'GET', 325 | timeout: 2000, 326 | url: `${address}/health` 327 | }, (err, response, body) => { 328 | if (err) t.assert.ifError(err) 329 | t.equal(response.statusCode, 503) 330 | t.equal(response.headers['content-type'], jsonMimeType) 331 | t.equal(response.headers['retry-after'], '50') 332 | const payload = JSON.parse(body) 333 | t.notOk(payload.uptime) // not present in this reply payload 334 | t.same(payload, { 335 | code: 'FST_UNDER_PRESSURE', 336 | error: 'Service Unavailable', 337 | // message: 'Service Unavailable', // default message 338 | message: 'Under pressure!', 339 | statusCode: 503 340 | }) 341 | t.end() 342 | }) 343 | }) 344 | }) 345 | -------------------------------------------------------------------------------- /test/named-import.test-d.ts: -------------------------------------------------------------------------------- 1 | import fastify from 'fastify' 2 | // import fastifyHealthcheck, { FastifyHealthcheckOptions } from '../types' 3 | import fastifyHealthcheck from '../types' 4 | 5 | const app = fastify() 6 | 7 | app.register(fastifyHealthcheck) 8 | -------------------------------------------------------------------------------- /test/underpressure.test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-2024 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 'use strict' 17 | 18 | // const { promisify } = require('node:util') 19 | const { monitorEventLoopDelay } = require('node:perf_hooks') 20 | 21 | const test = require('tap').test 22 | const sget = require('simple-get').concat 23 | 24 | const Fastify = require('fastify') 25 | 26 | // const wait = promisify(setTimeout) 27 | 28 | function block (msec) { 29 | const start = Date.now() 30 | /* eslint-disable no-empty */ 31 | while (Date.now() - start < msec) { } 32 | } 33 | 34 | // test the following features even directly with original under-pressure plugin 35 | const underPressure = require('@fastify/under-pressure') 36 | 37 | test('Should return 503 on maxHeapUsedBytes', (t) => { 38 | // t.plan(5) 39 | 40 | const fastify = Fastify() 41 | t.after(() => { fastify.close() }) 42 | fastify.register(underPressure, { 43 | maxHeapUsedBytes: 1 44 | }) 45 | 46 | fastify.get('/', (req, reply) => { 47 | reply.send({ hello: 'world' }) 48 | }) 49 | 50 | fastify.listen({ port: 0 }, (err, address) => { 51 | if (err) t.assert.ifError(err) 52 | 53 | process.nextTick(() => block(monitorEventLoopDelay ? 1500 : 500)) 54 | 55 | // can't use fastify inject here ... 56 | sget({ 57 | method: 'GET', 58 | url: address 59 | }, (err, response, body) => { 60 | if (err) t.assert.ifError(err) 61 | t.equal(response.statusCode, 503) 62 | t.equal(response.headers['retry-after'], '10') 63 | t.same(JSON.parse(body), { 64 | code: 'FST_UNDER_PRESSURE', 65 | error: 'Service Unavailable', 66 | message: 'Service Unavailable', 67 | statusCode: 503 68 | }) 69 | t.end() 70 | }) 71 | }) 72 | }) 73 | -------------------------------------------------------------------------------- /types/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "eslint:recommended", 4 | "plugin:@typescript-eslint/eslint-recommended", 5 | "plugin:@typescript-eslint/recommended", 6 | "standard" 7 | ], 8 | "parser": "@typescript-eslint/parser", 9 | "plugins": ["@typescript-eslint"], 10 | "env": { "node": true }, 11 | "parserOptions": { 12 | "ecmaVersion": 6, 13 | "sourceType": "module", 14 | "project": "./types/tsconfig.json", 15 | "createDefaultProgram": true 16 | }, 17 | "rules": { 18 | "no-console": "off", 19 | "@typescript-eslint/indent": ["error", 2], 20 | "semi": ["error", "never"], 21 | "import/export": "off" // this errors on multiple exports (overload interfaces) 22 | }, 23 | "overrides": [ 24 | { 25 | "files": ["*.d.ts","*.test-d.ts"], 26 | "rules": { 27 | "@typescript-eslint/no-explicit-any": "off", 28 | "no-redeclare": "off", 29 | "no-use-before-define": "off" 30 | } 31 | }, 32 | { 33 | "files": ["*.test-d.ts"], 34 | "rules": { 35 | "@typescript-eslint/explicit-function-return-type": "off", 36 | "@typescript-eslint/no-empty-function": "off", 37 | "@typescript-eslint/no-non-null-assertion": "off", 38 | "@typescript-eslint/no-unused-vars": "off", 39 | "no-unused-vars": "off", 40 | "node/handle-callback-err": "off" 41 | }, 42 | "globals": { 43 | "NodeJS": "readonly" 44 | } 45 | } 46 | ] 47 | } 48 | -------------------------------------------------------------------------------- /types/index.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | import { FastifyUnderPressureOptions } from '@fastify/under-pressure' 4 | // import { FastifyInstance, FastifyPluginCallback, FastifyPluginOptions, FastifyRequest } from 'fastify' 5 | import { FastifyPluginCallback } from 'fastify' 6 | 7 | // note that this plugin does not use FastifyPlugin, but it's good the same here for types 8 | 9 | type FastifyHealthcheckPlugin = FastifyPluginCallback> 10 | 11 | declare namespace fastifyHealthcheck { 12 | export interface FastifyHealthcheckOptions { 13 | /** 14 | * Override the uri for the healthcheck route (default: '/health'). 15 | */ 16 | healthcheckUrl?: string 17 | 18 | /** 19 | * Disable the healthcheck route (default: false). 20 | */ 21 | healthcheckUrlDisable?: boolean 22 | 23 | /** 24 | * Override to always return failure responses, useful to test failure responses (default: false). 25 | */ 26 | healthcheckUrlAlwaysFail?: boolean 27 | 28 | /** 29 | * Override to return Node.js process uptime (default: false). 30 | */ 31 | exposeUptime?: boolean 32 | 33 | /** 34 | * Override options for under-pressure (default: {}). 35 | */ 36 | underPressureOptions?: FastifyUnderPressureOptions 37 | 38 | /** 39 | * Fastify schema for the healthcheck route (default: undefined). 40 | */ 41 | schemaOptions?: unknown 42 | 43 | /** 44 | * Fastify route config for the healthcheck route (default: undefined). See https://fastify.dev/docs/latest/Reference/Routes/#config 45 | */ 46 | configOptions?: unknown 47 | } 48 | 49 | export const fastifyHealthcheck: FastifyHealthcheckPlugin 50 | export { fastifyHealthcheck as default } 51 | } 52 | 53 | declare function fastifyHealthcheck( 54 | ...params: Parameters 55 | ): ReturnType 56 | 57 | export = fastifyHealthcheck 58 | -------------------------------------------------------------------------------- /types/index.test-d.ts: -------------------------------------------------------------------------------- 1 | import fastify from 'fastify' 2 | import healthcheckPlugin, { FastifyHealthcheckOptions } from '..' 3 | import { expectAssignable } from 'tsd' 4 | 5 | const app = fastify() 6 | const options: FastifyHealthcheckOptions = { 7 | healthcheckUrl: '/health', 8 | healthcheckUrlDisable: false, 9 | healthcheckUrlAlwaysFail: false, 10 | exposeUptime: false, 11 | underPressureOptions: { } 12 | } 13 | app.register(healthcheckPlugin, options) 14 | 15 | app.listen({ port: 3000 }, (err, address) => { 16 | if (err) throw err 17 | console.log(`Server listening on '${address}' ...`) 18 | }) 19 | 20 | expectAssignable({}) 21 | expectAssignable({ 22 | healthcheckUrl: '/health', 23 | healthcheckUrlDisable: true, 24 | healthcheckUrlAlwaysFail: true, 25 | exposeUptime: true, 26 | schemaOptions: { 27 | operationId: 'getHealth', 28 | description: 'Serve responses for health checks', 29 | response: { 30 | default: { 31 | description: 'Default Response', 32 | type: 'object', 33 | properties: { 34 | statusCode: { 35 | type: 'number' 36 | }, 37 | status: { 38 | type: 'string' 39 | }, 40 | uptime: { 41 | type: 'number', 42 | optional: true 43 | } 44 | } 45 | } 46 | } 47 | }, 48 | configOptions: { 49 | hello: 'world' 50 | } 51 | }) 52 | -------------------------------------------------------------------------------- /types/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "checkJs": false, 4 | "esModuleInterop": true, 5 | "lib": [ "ES2023", "DOM" ], 6 | "module": "nodenext", 7 | "moduleResolution": "nodenext", 8 | "noEmit": true, 9 | "noImplicitAny": true, 10 | "preserveConstEnums": true, 11 | "sourceMap": false, 12 | "strict": true, 13 | "strictNullChecks": true, 14 | "target": "ES2023" 15 | }, 16 | "include": [ 17 | "./*.d.ts", 18 | "./*.test-d.ts", 19 | "/types/*.d.ts", 20 | "/types/*.test-d.ts", 21 | "../test/*.test-d.ts" 22 | ] 23 | } 24 | --------------------------------------------------------------------------------