UNPKG

10.7 kBMarkdownView Raw
1<h1 align="center">
2 Node.js Service Tools
3</h1>
4
5<p align="center">
6 <strong>Prepare your Node.js application for production!</strong>
7</p>
8
9<div align="center">
10 <a target="_blank" rel="noopener noreferrer" href="https://circleci.com/gh/banzaicloud/node-service-tools">
11 <img alt="CircleCI" title="CircleCI" src="https://circleci.com/gh/banzaicloud/node-service-tools.svg?style=svg&circle-token=002bb942365281c0834e18a1cb83e1930c3ce0fa" height="20">
12 </a>
13 <a target="_blank" rel="noopener noreferrer" href="https://npmjs.com/package/@banzaicloud/service-tools">
14 <img alt="npm version" title="npm version" src="https://badge.fury.io/js/%40banzaicloud%2Fservice-tools.svg" height="20">
15 </a>
16</div>
17
18This library provides common functionalities, like graceful error handling & shutdown, structured JSON logging and several HTTP middleware to make your application truly ready for modern containerised environments, like [Kubernetes](http://kubernetes.io/).
19
20<!-- TOC -->
21
22- [Installation](#installation)
23- [Usage](#usage)
24 - [Main exports](#main-exports)
25 - [`catchErrors(options)`](#catcherrorsoptions)
26 - [`gracefulShutdown(handlers, options)`](#gracefulshutdownhandlers-options)
27 - [`logger`](#logger)
28 - [Use provided logger instead of `console`](#use-provided-logger-instead-of-console)
29 - [Config](#config)
30 - [`config.environment`](#configenvironment)
31 - [`config.pino`](#configpino)
32 - [Middleware (Koa)](#middleware-koa)
33 - [`errorHandler(options)`](#errorhandleroptions)
34 - [`healthCheck(checks, options)`](#healthcheckchecks-options)
35 - [`prometheusMetrics(options)`](#prometheusmetricsoptions)
36 - [`requestValidator(options)`](#requestvalidatoroptions)
37 - [`requestLogger(options)`](#requestloggeroptions)
38 - [Middleware (Express)](#middleware-express)
39 - [`errorHandler(options)`](#errorhandleroptions-1)
40 - [`healthCheck(checks, options)`](#healthcheckchecks-options-1)
41 - [`prometheusMetrics(options)`](#prometheusmetricsoptions-1)
42 - [`requestValidator(options)`](#requestvalidatoroptions-1)
43
44<!-- /TOC -->
45
46## Installation
47
48```sh
49npm i @banzaicloud/service-tools
50# or
51yarn add @banzaicloud/service-tools
52```
53
54## Usage & Examples
55
56This library is written in TypeScript, refer to the published types or the source code for argument and return types.
57
58Examples are available for [Express](https://expressjs.com/) and [Koa](https://koajs.com/) frameworks. Check out the _[examples](/examples)_ folder!
59
60### Main exports
61
62#### `catchErrors(options)`
63
64Catch 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).
65
66```js
67const { catchErrors } = require('@banzaicloud/service-tools')
68
69// ...
70
71// the handlers return a Promise
72// the handlers are called in order
73catchErrors([closeServer, closeDB])
74
75// the error will be caught and the handlers will be called before exiting
76throw new Error()
77```
78
79#### `gracefulShutdown(handlers, options)`
80
81Graceful 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`.
82
83```js
84const { gracefulShutdown } = require('@banzaicloud/service-tools')
85
86// ...
87
88// the handlers return a Promise
89// the handlers are called in order
90gracefulShutdown([closeServer, closeDB])
91```
92
93#### `logger`
94
95A [pino](https://github.com/pinojs/pino) structured JSON logger instance configured with [`config.pino`](#config).
96
97```js
98const { logger } = require('@banzaicloud/service-tools')
99
100logger.info({ metadata: true }, 'log message')
101// > {"level":30,"time":<ts>,"msg":"log message","pid":0,"hostname":"local","metadata":true,"v":1}
102```
103
104##### Use provided logger instead of `console`
105
106Globally overwrite the `console` and use the logger provided by the library to print out messages.
107
108```js
109const { logger } = require('@banzaicloud/service-tools')
110
111console.log('log message')
112// > log message
113
114logger.interceptConsole()
115
116console.log('log message')
117// > {"level":30,"time":<ts>,"msg":"log message","pid":0,"hostname":"local","v":1}
118```
119
120### Config
121
122Load configurations dynamically.
123
124#### `config.environment`
125
126Dynamically load the environment config. It will become available on the `.environment` field. It exports the runtime environment and as a side-effect, it loads `.env` in development. Uses the `NODE_ENV` environment variable, with accepted values of: production, development, test.
127
128```js
129const { config } = require('@banzaicloud/service-tools')
130// validates NODE_ENV environment variables when referenced first and load .env when it's "development"
131console.log(config.environment)
132// > { nodeEnv: 'production' }
133```
134
135#### `config.pino`
136
137Used 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'`).
138
139```js
140const pino = require('pino')
141const { config } = require('@banzaicloud/service-tools')
142
143const logger = pino(config.pino)
144
145logger.info({ metadata: true, password: 'secret' }, 'log message')
146// > {"level":30,"time":<ts>,"msg":"log message","pid":0,"hostname":"local","metadata":true,"password":"[REDACTED]","v":1}
147```
148
149### Middleware (Koa)
150
151Several middleware for the [Koa](https://koajs.com/) web framework.
152
153#### `errorHandler(options)`
154
155Koa error handler middleware.
156
157```js
158const Koa = require('koa')
159const { koa: middleware } = require('@banzaicloud/service-tools').middleware
160
161const app = new Koa()
162
163// this should be the first middleware
164app.use(middleware.errorHandler())
165```
166
167#### `healthCheck(checks, options)`
168
169Koa health check endpoint handler.
170
171```js
172const Koa = require('koa')
173const Router = require('koa-router')
174const { koa: middleware } = require('@banzaicloud/service-tools').middleware
175
176// ...
177
178const app = new Koa()
179const router = new Router()
180
181// the checks return a Promise
182router.get('/health', middleware.healthCheck([checkDB]))
183
184app.use(router.routes())
185app.use(router.allowedMethods())
186```
187
188#### `prometheusMetrics(options)`
189
190Koa [Prometheus](https://prometheus.io/) metrics endpoint handler. By default it collects some [default metrics](https://github.com/siimon/prom-client#default-metrics).
191
192```js
193const Koa = require('koa')
194const Router = require('koa-router')
195const { koa: middleware } = require('@banzaicloud/service-tools').middleware
196
197// ...
198
199const app = new Koa()
200const router = new Router()
201
202router.get('/metrics', middleware.prometheusMetrics())
203
204app.use(router.routes())
205app.use(router.allowedMethods())
206```
207
208#### `requestValidator(options)`
209
210Koa 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`.
211
212```js
213const joi = require('joi')
214const qs = require('qs')
215const Koa = require('koa')
216const Router = require('koa-router')
217const bodyParser = require('koa-bodyparser')
218const { koa: middleware } = require('@banzaicloud/service-tools').middleware
219
220// ...
221
222const app = new Koa()
223const router = new Router()
224
225const paramsSchema = joi
226 .object({
227 id: joi
228 .string()
229 .hex()
230 .length(64)
231 .required(),
232 })
233 .required()
234
235const bodySchema = joi.object({ name: joi.string().required() }).required()
236
237const querySchema = joi.object({ include: joi.array().default([]) }).required()
238
239router.get(
240 '/',
241 middleware.requestValidator({ params: paramsSchema, body: bodySchema, query: querySchema }),
242 async function routeHandler(ctx) {
243 const { params, body, query } = ctx.state.validated
244 // ...
245 }
246)
247
248app.use(bodyParser())
249// query parser
250app.use(async function parseQuery(ctx, next) {
251 ctx.query = qs.parse(ctx.querystring, options)
252 ctx.request.query = ctx.query
253 await next()
254})
255app.use(router.routes())
256app.use(router.allowedMethods())
257```
258
259#### `requestLogger(options)`
260
261Koa request logger middleware. Useful for local development and debugging.
262
263```js
264const Koa = require('koa')
265const { koa: middleware } = require('@banzaicloud/service-tools').middleware
266
267// ...
268
269const app = new Koa()
270
271// this should be the second middleware after the error handler
272// ...
273app.use(middleware.requestLogger())
274```
275
276### Middleware (Express)
277
278Several middleware for the [Express](https://expressjs.com/) web framework.
279
280#### `errorHandler(options)`
281
282Express error handler middleware.
283
284```js
285const express = require('express')
286const { express: middleware } = require('@banzaicloud/service-tools').middleware
287
288const app = express()
289
290// this should be the last middleware
291app.use(middleware.errorHandler())
292```
293
294#### `healthCheck(checks, options)`
295
296Express health check endpoint handler.
297
298```js
299const express = require('express')
300const { express: middleware } = require('@banzaicloud/service-tools').middleware
301
302// ...
303
304const app = express()
305
306// the checks return a Promise
307app.get('/health', middleware.healthCheck([checkDB]))
308```
309
310#### `prometheusMetrics(options)`
311
312Express [Prometheus](https://prometheus.io/) metrics endpoint handler. By default it collects some [default metrics](https://github.com/siimon/prom-client#default-metrics).
313
314```js
315const express = require('express')
316const { express: middleware } = require('@banzaicloud/service-tools').middleware
317
318// ...
319
320const app = express()
321
322app.get('/metrics', middleware.prometheusMetrics())
323```
324
325#### `requestValidator(options)`
326
327Express 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`.
328
329```js
330const joi = require('joi')
331const express = require('express')
332const { express: middleware } = require('@banzaicloud/service-tools').middleware
333
334// ...
335
336const app = express()
337
338const paramsSchema = joi
339 .object({
340 id: joi
341 .string()
342 .hex()
343 .length(64)
344 .required(),
345 })
346 .required()
347
348const bodySchema = joi.object({ name: joi.string().required() }).required()
349
350const querySchema = joi.object({ include: joi.array().default([]) }).required()
351
352app.use(express.json())
353app.get(
354 '/',
355 middleware.requestValidator({ params: paramsSchema, body: bodySchema, query: querySchema }),
356 function routeHandler(req, res) {
357 const { params, body, query } = req
358 // ...
359 }
360)
361```