{"id":13550262,"url":"https://github.com/banzaicloud/service-tools","last_synced_at":"2025-04-03T00:33:24.545Z","repository":{"id":35848290,"uuid":"142031136","full_name":"banzaicloud/service-tools","owner":"banzaicloud","description":"Prepare your Node.js application for production","archived":true,"fork":false,"pushed_at":"2022-06-23T05:22:10.000Z","size":1151,"stargazers_count":99,"open_issues_count":7,"forks_count":10,"subscribers_count":20,"default_branch":"master","last_synced_at":"2025-03-05T13:02:36.225Z","etag":null,"topics":["express","koa","kubernetes","middleware","prometheus","shutdown"],"latest_commit_sha":null,"homepage":"https://banzaicloud.com/blog/nodejs-in-production/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/banzaicloud.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":".github/CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null}},"created_at":"2018-07-23T15:10:28.000Z","updated_at":"2024-06-23T13:32:05.000Z","dependencies_parsed_at":"2022-09-06T03:30:52.387Z","dependency_job_id":null,"html_url":"https://github.com/banzaicloud/service-tools","commit_stats":null,"previous_names":[],"tags_count":51,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/banzaicloud%2Fservice-tools","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/banzaicloud%2Fservice-tools/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/banzaicloud%2Fservice-tools/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/banzaicloud%2Fservice-tools/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/banzaicloud","download_url":"https://codeload.github.com/banzaicloud/service-tools/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246916733,"owners_count":20854511,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["express","koa","kubernetes","middleware","prometheus","shutdown"],"created_at":"2024-08-01T12:01:30.822Z","updated_at":"2025-04-03T00:33:24.015Z","avatar_url":"https://github.com/banzaicloud.png","language":"TypeScript","funding_links":[],"categories":["TypeScript"],"sub_categories":[],"readme":"\u003ch1 align=\"center\"\u003e\n  Node.js Service Tools\n\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cstrong\u003ePrepare your Node.js application for production!\u003c/strong\u003e\n\u003c/p\u003e\n\n\u003cdiv align=\"center\"\u003e\n  \u003ca target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://circleci.com/gh/banzaicloud/service-tools\"\u003e\n    \u003cimg alt=\"CircleCI\" title=\"CircleCI\" src=\"https://circleci.com/gh/banzaicloud/service-tools.svg?style=svg\u0026circle-token=002bb942365281c0834e18a1cb83e1930c3ce0fa\" height=\"20\"\u003e\n  \u003c/a\u003e\n  \u003ca target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://npmjs.com/package/@banzaicloud/service-tools\"\u003e\n    \u003cimg alt=\"npm version\" title=\"npm version\" src=\"https://badge.fury.io/js/%40banzaicloud%2Fservice-tools.svg\" height=\"20\"\u003e\n  \u003c/a\u003e\n\u003c/div\u003e\n\nThis library provides common functionalities, like graceful error handling \u0026 shutdown, structured JSON logging and several HTTP middleware to make your application truly ready for modern containerised environments, like [Kubernetes](http://kubernetes.io/).\n\n\u003c!-- TOC --\u003e\n\n- [Installation](#installation)\n- [Usage](#usage)\n  - [Main exports](#main-exports)\n    - [`catchErrors(options)`](#catcherrorsoptions)\n    - [`gracefulShutdown(handlers, options)`](#gracefulshutdownhandlers-options)\n    - [`logger`](#logger)\n      - [Use provided logger instead of `console`](#use-provided-logger-instead-of-console)\n  - [Config](#config)\n    - [`config.environment`](#configenvironment)\n    - [`config.pino`](#configpino)\n  - [Middleware (Koa)](#middleware-koa)\n    - [`errorHandler(options)`](#errorhandleroptions)\n    - [`healthCheck(checks, options)`](#healthcheckchecks-options)\n    - [`prometheusMetrics(options)`](#prometheusmetricsoptions)\n    - [`requestValidator(options)`](#requestvalidatoroptions)\n    - [`requestLogger(options)`](#requestloggeroptions)\n  - [Middleware (Express)](#middleware-express)\n    - [`errorHandler(options)`](#errorhandleroptions-1)\n    - [`healthCheck(checks, options)`](#healthcheckchecks-options-1)\n    - [`prometheusMetrics(options)`](#prometheusmetricsoptions-1)\n    - [`requestValidator(options)`](#requestvalidatoroptions-1)\n\n\u003c!-- /TOC --\u003e\n\n## Installation\n\n```sh\nnpm i @banzaicloud/service-tools\n# or\nyarn add @banzaicloud/service-tools\n```\n\n## Usage \u0026 Examples\n\nThis library is written in TypeScript, refer to the published types or the source code for argument and return types.\n\nExamples are available for [Express](https://expressjs.com/) and [Koa](https://koajs.com/) frameworks. Check out the _[examples](/examples)_ folder!\n\n### Main exports\n\n#### `catchErrors(options)`\n\nCatch uncaught exceptions and unhandled Promise rejections. It is not safe to resume normal operation after ['uncaughtException'](https://nodejs.org/api/process.html#process_event_uncaughtexception).\n\n```js\nconst { catchErrors } = require('@banzaicloud/service-tools')\n\n// ...\n\n// the handlers return a Promise\n// the handlers are called in order\ncatchErrors([closeServer, closeDB])\n\n// the error will be caught and the handlers will be called before exiting\nthrow new Error()\n```\n\n#### `gracefulShutdown(handlers, options)`\n\nGraceful shutdown: release resources (databases, HTTP connections, ...) before exiting. When the application receives `SIGTERM` or `SIGINT` signals, the close handlers will be called. The handlers should return a `Promise`.\n\n```js\nconst { gracefulShutdown } = require('@banzaicloud/service-tools')\n\n// ...\n\n// the handlers return a Promise\n// the handlers are called in order\ngracefulShutdown([closeServer, closeDB])\n```\n\n#### `logger`\n\nA [pino](https://github.com/pinojs/pino) structured JSON logger instance configured with [`config.pino`](#config).\n\n```js\nconst { logger } = require('@banzaicloud/service-tools')\n\nlogger.info({ metadata: true }, 'log message')\n// \u003e {\"level\":30,\"time\":\u003cts\u003e,\"msg\":\"log message\",\"pid\":0,\"hostname\":\"local\",\"metadata\":true,\"v\":1}\n```\n\n##### Use provided logger instead of `console`\n\nGlobally overwrite the `console` and use the logger provided by the library to print out messages.\n\n```js\nconst { logger } = require('@banzaicloud/service-tools')\n\nconsole.log('log message')\n// \u003e log message\n\nlogger.interceptConsole()\n\nconsole.log('log message')\n// \u003e {\"level\":30,\"time\":\u003cts\u003e,\"msg\":\"log message\",\"pid\":0,\"hostname\":\"local\",\"v\":1}\n```\n\n### Config\n\nLoad configurations dynamically.\n\n#### `config.environment`\n\nUses the `NODE_ENV` environment variable, with accepted values of: production, development, test.\n\n```js\nconst { config } = require('@banzaicloud/service-tools')\n// validates the NODE_ENV environment variable\nconsole.log(config.environment)\n// \u003e { nodeEnv: 'production' }\n```\n\n#### `config.pino`\n\nUsed by the provided [logger](#logger). Uses the `LOGGER_LEVEL` and `LOGGER_REDACT_FIELDS` environment variables. The `LOGGER_LEVEL` can be one of the following: fatal, error, warn, info, debug, trace. `LOGGER_REDACT_FIELDS` is a comma separated list of field names to mask out in the output (defaults to: `'password, pass, authorization, auth, cookie, _object'`).\n\n```js\nconst pino = require('pino')\nconst { config } = require('@banzaicloud/service-tools')\n\nconst logger = pino(config.pino)\n\nlogger.info({ metadata: true, password: 'secret' }, 'log message')\n// \u003e {\"level\":30,\"time\":\u003cts\u003e,\"msg\":\"log message\",\"pid\":0,\"hostname\":\"local\",\"metadata\":true,\"password\":\"[REDACTED]\",\"v\":1}\n```\n\n### Middleware (Koa)\n\nSeveral middleware for the [Koa](https://koajs.com/) web framework.\n\n#### `errorHandler(options)`\n\nKoa error handler middleware.\n\n```js\nconst Koa = require('koa')\nconst { koa: middleware } = require('@banzaicloud/service-tools').middleware\n\nconst app = new Koa()\n\n// this should be the first middleware\napp.use(middleware.errorHandler())\n```\n\n#### `healthCheck(checks, options)`\n\nKoa health check endpoint handler.\n\n```js\nconst Koa = require('koa')\nconst Router = require('koa-router')\nconst { koa: middleware } = require('@banzaicloud/service-tools').middleware\n\n// ...\n\nconst app = new Koa()\nconst router = new Router()\n\n// the checks return a Promise\nrouter.get('/health', middleware.healthCheck([checkDB]))\n\napp.use(router.routes())\napp.use(router.allowedMethods())\n```\n\n#### `prometheusMetrics(options)`\n\nKoa [Prometheus](https://prometheus.io/) metrics endpoint handler. By default it collects some [default metrics](https://github.com/siimon/prom-client#default-metrics).\n\n```js\nconst Koa = require('koa')\nconst Router = require('koa-router')\nconst { koa: middleware } = require('@banzaicloud/service-tools').middleware\n\n// ...\n\nconst app = new Koa()\nconst router = new Router()\n\nrouter.get('/metrics', middleware.prometheusMetrics())\n\napp.use(router.routes())\napp.use(router.allowedMethods())\n```\n\n#### `requestValidator(options)`\n\nKoa request validator middleware. Accepts [Joi](https://github.com/hapijs/joi) schemas for `body` (body parser required), `params` and `query` (query parser required). Returns with `400` if the request is not valid. Assigns validated values to `ctx.state.validated`.\n\n```js\nconst joi = require('joi')\nconst qs = require('qs')\nconst Koa = require('koa')\nconst Router = require('koa-router')\nconst bodyParser = require('koa-bodyparser')\nconst { koa: middleware } = require('@banzaicloud/service-tools').middleware\n\n// ...\n\nconst app = new Koa()\nconst router = new Router()\n\nconst paramsSchema = joi\n  .object({\n    id: joi\n      .string()\n      .hex()\n      .length(64)\n      .required(),\n  })\n  .required()\n\nconst bodySchema = joi.object({ name: joi.string().required() }).required()\n\nconst querySchema = joi.object({ include: joi.array().default([]) }).required()\n\nrouter.get(\n  '/',\n  middleware.requestValidator({ params: paramsSchema, body: bodySchema, query: querySchema }),\n  async function routeHandler(ctx) {\n    const { params, body, query } = ctx.state.validated\n    // ...\n  }\n)\n\napp.use(bodyParser())\n// query parser\napp.use(async function parseQuery(ctx, next) {\n  ctx.query = qs.parse(ctx.querystring, options)\n  ctx.request.query = ctx.query\n  await next()\n})\napp.use(router.routes())\napp.use(router.allowedMethods())\n```\n\n#### `requestLogger(options)`\n\nKoa request logger middleware. Useful for local development and debugging.\n\n```js\nconst Koa = require('koa')\nconst { koa: middleware } = require('@banzaicloud/service-tools').middleware\n\n// ...\n\nconst app = new Koa()\n\n// this should be the second middleware after the error handler\n// ...\napp.use(middleware.requestLogger())\n```\n\n### Middleware (Express)\n\nSeveral middleware for the [Express](https://expressjs.com/) web framework.\n\n#### `errorHandler(options)`\n\nExpress error handler middleware.\n\n```js\nconst express = require('express')\nconst { express: middleware } = require('@banzaicloud/service-tools').middleware\n\nconst app = express()\n\n// this should be the last middleware\napp.use(middleware.errorHandler())\n```\n\n#### `healthCheck(checks, options)`\n\nExpress health check endpoint handler.\n\n```js\nconst express = require('express')\nconst { express: middleware } = require('@banzaicloud/service-tools').middleware\n\n// ...\n\nconst app = express()\n\n// the checks return a Promise\napp.get('/health', middleware.healthCheck([checkDB]))\n```\n\n#### `prometheusMetrics(options)`\n\nExpress [Prometheus](https://prometheus.io/) metrics endpoint handler. By default it collects some [default metrics](https://github.com/siimon/prom-client#default-metrics).\n\n```js\nconst express = require('express')\nconst { express: middleware } = require('@banzaicloud/service-tools').middleware\n\n// ...\n\nconst app = express()\n\napp.get('/metrics', middleware.prometheusMetrics())\n```\n\n#### `requestValidator(options)`\n\nExpress request validator middleware. Accepts [Joi](https://github.com/hapijs/joi) schemas for `body` (body parser required), `params` and `query`. Returns with `400` if the request is not valid. Assigns validated values to `req`.\n\n```js\nconst joi = require('joi')\nconst express = require('express')\nconst { express: middleware } = require('@banzaicloud/service-tools').middleware\n\n// ...\n\nconst app = express()\n\nconst paramsSchema = joi\n  .object({\n    id: joi\n      .string()\n      .hex()\n      .length(64)\n      .required(),\n  })\n  .required()\n\nconst bodySchema = joi.object({ name: joi.string().required() }).required()\n\nconst querySchema = joi.object({ include: joi.array().default([]) }).required()\n\napp.use(express.json())\napp.get(\n  '/',\n  middleware.requestValidator({ params: paramsSchema, body: bodySchema, query: querySchema }),\n  function routeHandler(req, res) {\n    const { params, body, query } = req\n    // ...\n  }\n)\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbanzaicloud%2Fservice-tools","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbanzaicloud%2Fservice-tools","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbanzaicloud%2Fservice-tools/lists"}