🚀 Graceful Server 🐢
Tiny (~5k), dependency-free Node.JS library to make your API more graceful
Features
✔ It's listening system events to gracefully close your API on interruption.
✔ It facilitates the disconnect of data sources on shutdown.
✔ It facilitates the use of liveness and readiness.
✔ It manages the connections of your API.
Requirements
✔ NodeJS >= 8
Installation
NPM
npm install --save @gquittet/graceful-server
Yarn
yarn add @gquittet/graceful-server
Example
ExpressJS
const express = require('express')
const http = require('http')
const GracefulServer = require('@gquittet/graceful-server')
const { connectToDb, closeDbConnection } = require('./db')
const app = express()
app.get('/test', (_, res) => {
return res.send({ uptime: process.uptime() | 0 })
})
const server = http.createServer(app)
const gracefulServer = GracefulServer(server, { closePromises: [closeDbConnection] })
server.listen(8080, async () => {
await connectToDb()
gracefulServer.setReady()
})
HTTP Server
import http from 'http'
import url from 'url'
import GracefulServer from '@gquittet/graceful-server'
import { connectToDb, closeDbConnection } from './db'
const server = http.createServer((req, res) => {
if (req.url === '/test' && req.method === 'GET') {
res.statusCode = 200
res.setHeader('Content-Type', 'application/json')
return res.end(JSON.stringify({ uptime: process.uptime() | 0 }))
}
res.statusCode = 404
return res.end()
})
const gracefulServer = GracefulServer(server, { closePromises: [closeDbConnection] })
gracefulServer.on(GracefulServer.READY, () => {
console.log('Server is ready')
})
gracefulServer.on(GracefulServer.SHUTTING_DOWN, () => {
console.log('Server is shutting down')
})
gracefulServer.on(GracefulServer.SHUTDOWN, error => {
console.log('Server is down because of', error.message)
})
server.listen(8080, async () => {
await connectToDb()
gracefulServer.setReady()
})
Options
Name | Type | Default | Description |
---|
closePromises | (() => Promise)[] | [] | The functions to run when the API is stopping |
timeout | number | 1000 | The time in milliseconds to wait before shutting down the server |
livenessEndpoint | string | /live | The liveness endpoint |
readinessEndpoint | string | /ready | The readiness endpoint |
Integration with Docker
HEALTH CHECK in Dockerfile
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s CMD ["node healthcheck.js"]
Content of healthcheck.js
const http = require('http')
const options = {
timeout: 2000,
host: 'localhost',
port: 8080,
path: '/live'
}
const request = http.request(options, res => {
console.info('STATUS:', res.statusCode)
process.exitCode = res.statusCode === 200 ? 0 : 1
process.exit()
})
request.on('error', err => {
console.error('ERROR', err)
process.exit(1)
})
request.end()
Example of Dockerfile
FROM node:12-slim as base
ENV NODE_ENV=production
ENV TINI_VERSION=v0.18.0
ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /tini
RUN chmod +x /tini && \
mkdir -p /node_app/app && \
chown -R node:node /node_app
WORKDIR /node_app
USER node
COPY --chown=node:node package.json package-lock*.json ./
RUN npm ci && \
npm cache clean --force
WORKDIR /node_app/app
FROM base as source
COPY --chown=node:node . .
FROM source as dev
ENV NODE_ENV=development
ENV PATH=/node_app/node_modules/.bin:$PATH
RUN npm install --only=development --prefix /node_app
CMD ["nodemon", "--inspect=0.0.0.0:9229"]
FROM source as test
ENV NODE_ENV=development
ENV PATH=/node_app/node_modules/.bin:$PATH
COPY --from=dev /node_app/node_modules /node_app/node_modules
RUN npm run lint
ENV NODE_ENV=test
RUN npm test
CMD ["npm", "test"]
FROM test as audit
RUN npm audit --audit-level critical
USER root
ADD https://get.aquasec.com/microscanner /
RUN chmod +x /microscanner && \
/microscanner your_token --continue-on-failure
FROM source as buildProd
ENV PATH=/node_app/node_modules/.bin:$PATH
COPY --from=dev /node_app/node_modules /node_app/node_modules
RUN npm run build
FROM source as prod
COPY --from=buildProd --chown=node:node /node_app/app/build ./build
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s CMD ["node healthcheck.js"]
ENTRYPOINT ["/tini", "--"]
CMD ["node", "./build/src/main.js"]
Integration with Kubernetes
readinessProbe:
httpGet:
path: /ready
port: 8080
failureThreshold: 1
initialDelaySeconds: 5
periodSeconds: 5
successThreshold: 1
timeoutSeconds: 5
livenessProbe:
httpGet:
path: /live
port: 8080
failureThreshold: 3
initialDelaySeconds: 10
periodSeconds: 30
successThreshold: 1
timeoutSeconds: 5
startupProbe:
httpGet:
path: /live
port: 9000
failureThreshold: 3
initialDelaySeconds: 10
periodSeconds: 30
successThreshold: 1
timeoutSeconds: 5
Thanks
★ Terminus
★ Lightship
★ Bret Fisher
★ Node HTTP documentation
Donate
If you like my job, don't hesite to contribute to this project! ❤️