Security News
Research
Supply Chain Attack on Rspack npm Packages Injects Cryptojacking Malware
A supply chain attack on Rspack's npm packages injected cryptomining malware, potentially impacting thousands of developers.
@gquittet/graceful-server
Advanced tools
Tiny (~5k), dependency-free Node.JS library to make your API more graceful
Tiny (~5k), KISS, dependency-free Node.JS library to make your Rest API graceful.
✔ 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.
✔ NodeJS >= 10.13.0
npm install --save @gquittet/graceful-server
yarn add @gquittet/graceful-server
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).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).
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()
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).
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()
})
;((server: http.Server, options?: IGracefulServerOptions | undefined) => IGracefulServer) & typeof State
where State
is an enum that contains, STARTING
, READY
, SHUTTING_DOWN
and SHUTDOWN
.
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 |
export default interface IGracefulServer {
isReady: () => boolean
setReady: () => void
on: (name: string, callback: (...args: any[]) => void) => EventEmitter
}
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s CMD ["node 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()
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" ]
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"]
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
★ Terminus
★ Bret Fisher for his great articles and videos
If you like my job, don't hesite to contribute to this project! ❤️
FAQs
Tiny (~5k), dependency-free Node.JS library to make your API more graceful
The npm package @gquittet/graceful-server receives a total of 40,874 weekly downloads. As such, @gquittet/graceful-server popularity was classified as popular.
We found that @gquittet/graceful-server demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 0 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Research
A supply chain attack on Rspack's npm packages injected cryptomining malware, potentially impacting thousands of developers.
Research
Security News
Socket researchers discovered a malware campaign on npm delivering the Skuld infostealer via typosquatted packages, exposing sensitive data.
Security News
Sonar’s acquisition of Tidelift highlights a growing industry shift toward sustainable open source funding, addressing maintainer burnout and critical software dependencies.