npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

exiting

v7.0.0

Published

Gracefully stop hapi.js servers

Downloads

4,873

Readme

Exiting

Safely shutdown hapi.js servers whenever the process exits.

Node.js CI

Details

While it is simple to start and stop a server, ensuring proper shutdown on external, or internal, triggers can be cumbersome to handle properly. exiting makes this easy by managing your Hapi servers, taking care of starting and stopping them as appropriate.

Depending on the exit trigger, the hapi servers will either be gracefully stopped or aborted (by only triggering onPreStop hooks). The exit triggers are handled as detailed:

  • Graceful exit with code 0:
    • process.exit() with exit code 0.
    • Unhandled SIGINT kill signal, through eg. ctrl-c.
    • Unhandled SIGTERM kill signal.
    • Unhandled SIGQUIT kill signal.
  • Aborted exit:
    • process.exit() with non-zero exit code.
    • Unhandled SIGHUP kill signal (code 1).
    • Any uncaught exception (code 1).
    • Any unhandled rejection (code 1).
    • Any closed connection listeners, eg. on worker disconnect (code 255).

If shutting down one of the servers is too slow, a timeout will eventually trigger an exit (exit code 255).

The shutdown logic is programmed to handle almost any conceivable exit condition, and provides 100% test coverage. The only instances that onPreHook code is not called, are uncatchable signals, like SIGKILL, and fatal errors that trigger during shutdown.

Example

Basic server example:

const Hapi = require('hapi');
const Exiting = require('exiting');

const server = Hapi.Server();
const manager = Exiting.createManager(server);

server.events.on('stop', () => {

    console.log('Server stopped.');
});

const provision = async () => {

    server.route({
        method: 'GET',
        path: '/',
        handler: () => 'Hello' 
    });

    await manager.start();

    console.log('Server started at:', server.info.uri);
};

provision();

The server and process life-cycle will now be managed by exiting.

If you need to delay the shutdown for processing, you can install an extention function on the onPreStop or onPostStop extension points, eg:

server.ext('onPreStop', () => {

    return new Promise((resolve) => {

        setTimeout(resolve, 1000);
    });
});

Multiple servers example:

const Hapi = require('hapi');
const Exiting = require('exiting');

const publicServer = Hapi.Server();
const adminServer = Hapi.Server();
const manager = Exiting.createManager([publicServer, adminServer]);

const provision = async () => {

    publicServer.route({
        method: 'GET',
        path: '/',
        handler: () => 'Hello'
    });

    adminServer.route({
        method: 'GET',
        path: '/',
        handler: () => 'Hello Admin'
    });

    await manager.start();

    console.log('Public server started at:', publicServer.info.uri);
    console.log('Admin server started at:', adminServer.info.uri);
};

provision();

Installation

Install using npm: npm install exiting.

Usage

To enable exiting for you server, replace the call to server.start() with Exiting.createManager(server).start().

Exiting.createManager(servers, [options])

Create a new exit manager for one or more hapi.js servers. The options object supports:

  • exitTimeout - When exiting, force process exit after this amount of ms has elapsed. Default: 5000.

await manager.start()

Starts the manager and all the managed servers, as if server.start() is called on each server. If any server fails to start, all will be stopped with server.stop() before the error is re-thrown.

Note that process.exit() is monkey patched to intercept such calls. Starting also installs the signal handlers and an uncaughtException handler.

await manager.stop([options])

Stops the manager and all the servers, as if server.stop() is called on each server.

Notes on process.exit()

The process.exit() method is handled in a special manner that allows the asyncronous stop logic to resolve before actually exiting. Since this can be called from anywhere in the code, and subsequent code is never expected to be executed, the manager will throw an Exiting.ProcessExitError to attempt to escape the current execution context. This allows something like the following to still exit:

while (true) {
    process.exit(1);
}

This might not always work, and can potentially cause a lock up instead of exiting. Eg. with this code:

try {
    process.exit(1);
}
catch (err) {
    /* do nothing */
}
while (true) {}

You should avoid using process.exit() in your own code, and call manager.stop() instead.