All Projects → gquittet → Graceful Server

gquittet / Graceful Server

Licence: other
Tiny (~5k), KISS, dependency-free Node.JS library to make your API more graceful

Programming Languages

typescript
32286 projects

Projects that are alternatives of or similar to Graceful Server

Express Status Monitor
🚀 Realtime Monitoring solution for Node.js/Express.js apps, inspired by status.github.com, sponsored by https://dynobase.dev
Stars: ✭ 3,302 (+1808.67%)
Mutual labels:  health-check, expressjs
Swagger Express Middleware
Swagger 2.0 middlware and mocks for Express.js
Stars: ✭ 543 (+213.87%)
Mutual labels:  expressjs, server
Iris
The fastest HTTP/2 Go Web Framework. AWS Lambda, gRPC, MVC, Unique Router, Websockets, Sessions, Test suite, Dependency Injection and more. A true successor of expressjs and laravel | 谢谢 https://github.com/kataras/iris/issues/1329 |
Stars: ✭ 21,587 (+12378.03%)
Mutual labels:  expressjs, server
Express Env Example
A sample express environment that is well architected for scale. Read about it here:
Stars: ✭ 130 (-24.86%)
Mutual labels:  expressjs, server
New Website
🖥 cdnjs.com website
Stars: ✭ 449 (+159.54%)
Mutual labels:  expressjs, server
Pure Http
✨ The simple web framework for Node.js with zero dependencies.
Stars: ✭ 139 (-19.65%)
Mutual labels:  expressjs, server
Lear
Linux Engine for Asset Retrieval - speed-profiled C HTTP server
Stars: ✭ 165 (-4.62%)
Mutual labels:  server
Bahunya
10KB classless CSS framework with responsive typography, navbar, syntax highlighting, etc.
Stars: ✭ 170 (-1.73%)
Mutual labels:  tiny
Unity Fastpacedmultiplayer
Features a Networking Framework to be used on top of Unity Networking, in order to implement an Authoritative Server with Lag Compensation, Client-Side Prediction/Server Reconciliation and Entity Interpolation
Stars: ✭ 162 (-6.36%)
Mutual labels:  server
Newbe.claptrap
This is a frameworks with reactive, event sourcing and Actor pattern as basic theories. On top of this, developers can create "distributed", "scale out", and "easy to test" application more simply. Claptrap and it`s Minions is on the way.
Stars: ✭ 163 (-5.78%)
Mutual labels:  server
Mitol
Lightweight NodeJS http server
Stars: ✭ 172 (-0.58%)
Mutual labels:  server
Tigase Server
Highly optimized, extremely modular and very flexible XMPP/Jabber server
Stars: ✭ 170 (-1.73%)
Mutual labels:  server
Psiphon
A multi-functional version of a popular network circumvention tool
Stars: ✭ 169 (-2.31%)
Mutual labels:  server
Cognito Express
Authenticates API requests on a Node application by verifying the JWT signature of AccessToken or IDToken generated by Amazon Cognito.
Stars: ✭ 165 (-4.62%)
Mutual labels:  expressjs
Rayo.js
Micro framework for Node.js
Stars: ✭ 170 (-1.73%)
Mutual labels:  server
Node Git Server
🎡 A configurable git server written in Node.js
Stars: ✭ 163 (-5.78%)
Mutual labels:  server
Casbin Server
Casbin as a Service (CaaS)
Stars: ✭ 171 (-1.16%)
Mutual labels:  server
Simplenet
An easy-to-use, event-driven, asynchronous network application framework compiled with Java 11.
Stars: ✭ 164 (-5.2%)
Mutual labels:  server
Socketio
Socket.IO Java Server based on Netty. was created to meet gaming performance requirements. battle tested and in use by Playtech Microservices API Gateway.
Stars: ✭ 166 (-4.05%)
Mutual labels:  server
Fiber
⚡️ Express inspired web framework written in Go
Stars: ✭ 17,334 (+9919.65%)
Mutual labels:  expressjs

🚀 Graceful Server 🐢

GitHub package.json version JavaScript Style Guide npm type definitions License npmjs download Donate with PayPal

Tiny (~5k), KISS, dependency-free Node.JS library to make your Rest API 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.

✔ It avoid boilerplate codes.

✔ Dependency-free.

✔ KISS code base.

Requirements

✔ NodeJS >= 10.13.0

Installation

NPM

npm install --save @gquittet/graceful-server

Yarn

yarn add @gquittet/graceful-server

Endpoint

Below you can find the default endpoint but you can setup or disable them. To do that, check out the Options part.

/live

The endpoint responds:

  • 200 status code with the uptime of the server in second.
{ "uptime": 42 }

Used to configure liveness probe.

/ready

The endpoint responds:

  • 200 status code if the server is ready.
{ "status": "ready" }
  • 503 status code with an empty response if the server is not ready (started, shutting down, etc).

Example

ExpressJS

The library works with the default HTTP NodeJS object. So, when you're using Express you can't pass directly the app object from Express. But, you can easily generate an HTTP NodeJS object from the app object.

Just follow the bottom example:

const express = require('express')
const helmet = require('helmet')
const http = require('http')
const GracefulServer = require('@gquittet/graceful-server')
const { connectToDb, closeDbConnection } = require('./db')

const app = express()
const server = http.createServer(app)
const gracefulServer = GracefulServer(server, { closePromises: [closeDbConnection] })

app.use(helmet())

app.get('/test', (_, res) => {
  return res.send({ uptime: process.uptime() | 0 })
})

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()
})

As you can see, we're using the app object from Express to set up the endpoints and middleware. But it can't listen (you can do it but app hasn't any liveness or readiness). The listening of HTTP calls need to be done by the default NodeJS HTTP object (aka server).

Fastify

const fastify = require('fastify')({ logger: true })
const GracefulServer = require('@gquittet/graceful-server')

const gracefulServer = GracefulServer(fastify.server)

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)
})

// Declare a route
fastify.get('/', async (request, reply) => {
  return { hello: 'world' }
})

// Run the server!
const start = async () => {
  try {
    await fastify.listen(3000)
    fastify.log.info(`server listening on ${fastify.server.address().port}`)
    gracefulServer.setReady()
  } catch (err) {
    fastify.log.error(err)
    process.exit(1)
  }
}
start()

Koa

const GracefulServer = require('@gquittet/graceful-server')
const Koa = require('koa')
const http = require('http')
const Router = require('koa-router')

const app = new Koa()
const router = new Router()

const server = http.createServer(app.callback())
gracefulServer = GracefulServer(server)

router.get('/test')
app.use(router.routes())

// response
app.use(ctx => {
  ctx.body = 'Hello Koa'
})

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()
})

As you can see, we're using the app object from Express to set up the endpoints and middleware. But it can't listen (you can do it but app hasn't any liveness or readiness). The listening of HTTP calls need to be done by the default NodeJS HTTP object (aka server).

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()
})

API

GracefulServer

;((server: http.Server, options?: IGracefulServerOptions | undefined) => IGracefulServer) & typeof State

where State is an enum that contains, STARTING, READY, SHUTTING_DOWN and SHUTDOWN.

IGracefulServerOptions

All of the below options are optional.

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
healthCheck boolean true Enable/Disable the default endpoints (liveness and readiness)
livenessEndpoint string /live The liveness endpoint
readinessEndpoint string /ready The readiness endpoint

GracefulServer Instance

export default interface IGracefulServer {
  isReady: () => boolean
  setReady: () => void
  on: (name: string, callback: (...args: any[]) => void) => EventEmitter
}

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

POC level

FROM node:12-slim

WORKDIR /usr/src/app

COPY package*.json ./

RUN npm ci --only=production

COPY . .

EXPOSE 8080
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s CMD ["node healthcheck.js"]
CMD [ "node", "server.js" ]

Company level

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
  # Allow sufficient amount of time (90 seconds = periodSeconds * failureThreshold)
  # for the registered shutdown handlers to run to completion.
  periodSeconds: 30
  successThreshold: 1
  # Setting a very low timeout value (e.g. 1 second) can cause false-positive
  # checks and service interruption.
  timeoutSeconds: 5

# As per Kubernetes documentation (https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#when-should-you-use-a-startup-probe),
# startup probe should point to the same endpoint as the liveness probe.
#
# Startup probe is only needed when container is taking longer to start than
# `initialDelaySeconds + failureThreshold × periodSeconds` of the liveness probe.
startupProbe:
  httpGet:
    path: /live
    port: 8080
  failureThreshold: 3
  initialDelaySeconds: 10
  periodSeconds: 30
  successThreshold: 1
  timeoutSeconds: 5

Thanks

Terminus

Lightship

Stoppable

Bret Fisher for his great articles and videos

IBM documentation

Node HTTP documentation

Cloud Health

Cloud Health Connect

Sponsors

JetBrains Logo

Donate

Donate

If you like my job, don't hesite to contribute to this project! ❤️

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].