Options
All
  • Public
  • Public/Protected
  • All
Menu

Node Service Manager

Service Status Build Status Coverage Status NPM version GitHub license Known Vulnerabilities

Motivation

Managing the lifetime of multiple services such as a database connection, logger or HTTP server can be difficult as a project grows in size. Ensuring that all services are started without error, stopped as needed, and finally destroyed when your process is about to exit is prone to programmer error and can leave a process stalled, unable to successfully exit. This library aims to provide the means to manage your services so that you do not need to.

Install

npm install --save @mitmaro/service-manager

Documentation

Usage

Creating an instance

Creating a Service Manager is very straight forward.

JavaScript

const {ServiceManager} = require('@mitmaro/service-manager');
const serviceManager = new ServiceManager();

TypeScript

import {ServiceManager} from '@mitmaro/service-manager';
const serviceManager = new ServiceManager();

Service interface

In order for the Service Manager to interact with a system you generally must wrap the system in a service adapter. The adapter interface is very simple:

interface Service<E> {
    start (service: ServiceWrapper<E>): E | Promise<E>;
    stop (service: ServiceWrapper<E>): E | Promise<E>;
    destroy (service: ServiceWrapper<E>): E | Promise<E>;
}

The service argument passed to each method can be used to determine meta information about the service, such as the name, current state, etc.

Register a service

Registering a service is done with the registerService function. Attempting to registered a service after the Service Manager has been started, stopped or destroyed will result in an error.

JavaScript

const {ServiceManager} = require('@mitmaro/service-manager');

function createService(port) {
    let server = null;
    return {
        async start() {
            server = http.create();
            await server.listen(port);
            return `Server started on ${port}`;
        },
        async stop() {
            if (server) {
                await server.close();
                server = null;
                return 'Server shutdown';
            }
            return 'Server not started';
        },
        async destroy() {
            await server.destroy();
            await server.unref();
            return 'Server destroyed';
        }
    };
}

const serviceManager = new ServiceManager();
serviceManager.registerService('my-service', createService(8080));

TypeScript

import {Service, ServiceManager} from "@mitmaro/service-manager";

function createService(port: number): Service<string> {
    let server: http.Server = null;
    return {
        async start() {
            server = http.createServer();
            await server.listen(port);
            return `Server started on ${port}`;
        },
        async stop() {
            if (server) {
                await server.close();
                server = null;
                return `Server shutdown`;
            }
            return 'Server not started';
        },
        async destroy() {
            await server.destroy();
            await server.unref();
            return 'Server destroyed';
        }
    };
}

const serviceManager = new ServiceManager();
serviceManager.registerService<string>('my-service', createService(8080));

Starting, Stopping and Destroying services

Services can be started, stopped and destroyed using the start, stop and destroy methods respectively. Services transition happen in parallel and will catch any errors that might occur during the transition. After a transition, the state of the Service Manager should be checked for error. The destroy method will not transition into an error state, and will always show a state of State.Destroyed. The primary difference between destroy and stop is that once the system is placed into a destroyed state, it cannot be restarted.

await serviceManager.start();

if (serviceManager.state === State.Error) {
    for (const service of serviceManager.services) {
        console.error(`${service.name}: ${getStateNameForState(service.state)}`);
        if (service.state === State.Error) {
            console.error(service.result);
        }
    }
}

await serviceManager.stop();

if (serviceManager.state === State.Error) {
    for (const service of serviceManager.services) {
        console.error(`${service.name}: ${getStateNameForState(service.state)}`);
        if (service.state === State.Error) {
            console.error(service.result);
        }
    }


await serviceManager.destroy();
for (const service of serviceManager.services) {
    console.error(`${service.name}: ${getStateNameForState(service.state)}`);
    if (service.state === State.Error) {
        console.error(service.result);
    }
}

Development

Development is done using Node 8 and NPM 5, and tested against both Node 6, Node 8 and Node 10. To get started:

  • Install Node 8 from NodeJS.org or using nvm
  • Clone the repository using git clone git@github.com:MitMaro/node-errors.git
  • cd node-errors
  • Install the dependencies npm install
  • Make changes, add tests, etc.
  • Run linting and test suite using npm run test

Debugging

This library uses debug to produce debugging output. To enable add DEBUG=server-shutdown before your run command.

License

This project is released under the ISC license. See LICENSE.

Index

Variables

Private Const transitionStates

transitionStates: State[] = [State.Starting, State.Stopping, State.Destroying]

Functions

getStateNameForState

  • getStateNameForState(state: State): string
  • Get a printable name for the provided state

    Parameters

    Returns string

    A printable name

Object literals

Private Const stateNameLookup

stateNameLookup: object

__computed

__computed: string = "stopping"

Private Const stateTransitions

stateTransitions: object

__computed

__computed: (Error | Stopped)[] = [State.Stopped, State.Error]

Legend

  • Module
  • Object literal
  • Variable
  • Function
  • Function with type parameter
  • Index signature
  • Type alias
  • Enumeration
  • Enumeration member
  • Property
  • Method
  • Interface
  • Interface with type parameter
  • Constructor
  • Property
  • Method
  • Index signature
  • Class
  • Class with type parameter
  • Constructor
  • Property
  • Method
  • Accessor
  • Index signature
  • Inherited constructor
  • Inherited property
  • Inherited method
  • Inherited accessor
  • Protected property
  • Protected method
  • Protected accessor
  • Private property
  • Private method
  • Private accessor
  • Static property
  • Static method

Generated using TypeDoc