Home Reference Source Repository

src/Index.js

// Import the neccesary modules.
import cluster from 'cluster';
import domain from 'domain';
import Express from 'express';
import http from 'http';
import os from 'os';

import setupRoutes from './config/routes';
import doSetup from './config/setup';
import { createLogger } from './config/logger';
import {
  createTemp,
  resetLog
} from './utils';
import {
  master,
  port,
  workers
} from './config/constants';

/**
 * Class for starting the API.
 *
 * @example
 * // Simply start the API by creating a new instance of the Index class.
 * const index = new Index();
 *
 * @example
 * // Or override the default configuration of the Index class.
 * const index = new Index({
 *    pretty: true,
 *    verbose: false,
 *    debug: false
 * });
 */
export default class Index {

  /**
   * Create an index object.
   * @param {Object} config - Configuration for the API.
   * @param {Boolean} [config.pretty=true] - Pretty output with Winston logging.
   * @param {Boolean} [config.verbose=false] - Debug mode for no output.
   */
  constructor({pretty = true, verbose = false} = {}) { // eslint-disable-line object-curly-spacing
    // The express object.
    const _app = new Express();

    // Setup the global logger object.
    createLogger(pretty, verbose);

    // Setup the MongoDB configuration and ExpressJS configuration.
    doSetup(_app, pretty, verbose);

    // Setup the API routes.
    setupRoutes(_app);

    /**
     * The http server object.
     * @type {Object}
     */
    Index._server = http.createServer(_app);

    // Start the API.
    Index._startAPI();
  }

  /**
   * Function to start the API.
   * @returns {void}
   */
  static _startAPI() {
    if (cluster.isMaster) { // Check is the cluster is the master
      // Clear the log files from the temp directory.
      resetLog();

      // Setup the temporary directory
      createTemp();

      // Fork workers.
      for (let i = 0; i < Math.min(os.cpus().length, workers); i++) // eslint-disable-line semi-spacing
        cluster.fork();

      // Check for errors with the workers.
      cluster.on('exit', worker => {
        logger.error(`Worker '${worker.process.pid}' died, spinning up another!`);
        cluster.fork();
      });

      if (master) {
        // WARNING: Domain module is pending deprication: https://nodejs.org/api/domain.html
        const scope = domain.create();
        scope.run(() => logger.info('API started'));
        scope.on('error', logger.error);
      }
    } else {
      Index._server.listen(port);
    }
  }

  /**
   * Function to stop the API from running.
   * @returns {void}
   */
  static closeAPI() {
    Index._server.close(() => {
      logger.info('Closed out remaining connections.');
      process.exit();
    });
  }

}