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

kickapp

v1.0.7-0

Published

Application architecture framework with pluggable services

Downloads

121

Readme

Version Dependency Status Build Status

KickApp

KickApp is an application architecture framework for organizing the logic into runnable services.

Split your logical components into services contained in a top-level Applicaton object, define service dependencies and launch them all with a single app.start()!

Key features:

  • Structured application architecture approach
  • Service workflow: init(), start(), stop()
  • Service dependencies resolution
  • Can deal with hierarchical services
  • Promise-based: using the q package
  • Unit-tested

Table of Contents

  • KickApp
  • Table of Contents
  • Core Components
    • Service
      • Object Service
    • Application
      • Application(App, ...args)
      • Structure
        • Application.addService(name, serviceConstructor, ...args):Application
        • Application.dependsOn(name, ...names):Application
        • Application.get(serviceName):IService
        • Application.getServiceWrapper(serviceName):ServiceWrapper
        • Application.getServiceNames():Array.
        • Application.isRunning():Boolean
      • Workflow
        • Application.init()
        • Application.start()
        • Application.stop()
      • Events
    • ServiceWrapper
  • Full Example
  • Promise-Haters
  • Application as a Service
  • Bundled Services
    • NetService
    • TimerService

Core Components

Service

A Service is an arbitrary constructor function or an object that implements the IService interface:

  • Its constructor function gets an Application as the first argument. More arguments can be provided with the Application.addService() method.
  • It can optionally have the init() method: here you can perform some preparation steps
  • It must have the start() and stop() methods which bring your service up and down respectively.

Consider an example:

/** Database service
 * @param {Application} app
 *     The parent Application
 * @param {String} url
 *     Connection string
 * @constructor
 * @implements {IService}
 */
var DbService = function(app, url){
    this.app = app;
    this.url = url;
    this.client = undefined;
};

DbService.prototype.init = function(){
    this.client = new DatabaseClient(this.url); // init some imaginary client
};

DbService.prototype.start = function(){
    return this.client.connect(); // assuming it's promise-based
};

DbService.prototype.stop = function(){
    return this.client.disconnect();
};

Having such a service, you can:

  • Add it to an Application
  • Define service dependencies
  • Launch them all in the correct order

Object Service

If your service is simple, you don't have to implement the constructor and everything. Use an object instead, which is also a valid service:

var DbService = {
    app: undefined,
    client: new DatabaseClient('db://localhost/'),
    start: function(){
        return this.client.connect();
    },
    stop: function(){
        return this.client.disconnect();
    },
};

It will automatically get the app property when added to the Application.

Application

An Application is the container for your services.

You define an application by creating an instance of it, providing a constructor function as an argument:

var kickapp = require('kickapp');

var App = new kickapp.Application(function(configFile){
    this.config = require(configFile); // init the configuraton

}, 'app/config.js');

By design, an Application does not have custom start/stop behavior: instead, it wraps services.

Application(App, ...args)

Structure

Application.addService(name, serviceConstructor, ...args):Application

Add a service to the application.

  • name: String: Name of the service. Use any reasonable string.
  • serviceConstructor: Function: Constructor function for an object that implements the IService interface.
  • ...args: Variadic arguments for the service constructor

Returns: an instance of ServiceWrapper (see below).

This function can also be called inside the Application constructor:

var kickapp = require('kickapp');

var App = new kickapp.Application(function(configFile){
    // Load the configuration
    this.config = require(configFile);

    // Add services
    this.addService('db', require('./services/db.js'), this.config.db );

    this.addService('web', require('./services/web.js'), this.config.web )
        .dependsOn('db'); // Define a dependency on other services

}, 'app/config.js');

Application.dependsOn(name, ...names):Application

Add dependencies for the recently added Service.

When a Service depends on other services, they will be started before starting this one.

Dependencies can be given either as arguments, or as a single array argument.

Application.get(serviceName):IService

Get the Service object by name:

app.get('db').client; // get the Service property

Application.getServiceWrapper(serviceName):ServiceWrapper

Get the ServiceWrapper object by name.

See: ServiceWrapper

Application.getServiceNames():Array.

Get the list of service names added to this Application object.

Application.isRunning():Boolean

Check whether the Application is running.

An Application is running if all its services are running.

Workflow

Service methods are run in the following fashion:

  • Service constructors are called immediately whilst you add your services to the Application
  • init(), start(), stop() methods are called when the corresponding Application method is called. Unlike the constructor, these honor the service dependencies.

Application.init()

Call init() on all services, honoring the dependencies. Returns a promise.

Note that IService.init is optional.

Application.start()

Call start() on all services, honoring the dependencies. Returns a promise.

If some services were not yet initialized with init(), Application does that.

Application.stop()

Call stop() on all services, honoring the dependencies in reverse order. Returns a promise.

Events

Application is an EventEmitter which fires the following events:

  • 'init': All services have been initialized
  • 'start': All services have been started
  • 'stop': All services have been stopped

ServiceWrapper

Each Service is internally wrapped in ServiceWrapper which controls the service state.

  • ServiceWrapper is an EventEmitter which fires the 'init', 'start', 'stop' events of the service
  • Contains metainformation about the Service: service, initialized, running, dependencies

Usually, you won't need it. See the source code: ServiceWrapper.

Full Example

var kickapp = require('kickapp');

// Application
var App = new kickapp.Application(function(configFile){ // Application constructor
    // Load the configuration
    this.config = require(configFile);

    // Services
    this.addService(
        'db', // Service name
        require('./services/db.js'), // Service constructor with init()/start()/stop() methods
        this.config.db // Arguments for the Service constructor
    );

    this.addService('web', require('./services/web.js'), this.config.web )
        .dependsOn('db'); // Dependencies on other services

}, 'app/config.js'); // Arguments for the Application constructor

// Launch it
App.start()
    .then(function(){
        console.log('Application initialized and started!');
    })
    .catch(function(err){
        console.error('Application.start failed:', err.stack);
    });

// Stop the services properly when the application exitsq
process.on('SIGINT', process.exit);
process.on('exit', function(){
    App.stop()
        .catch(function(err){
            console.error('Application.stop failed:', err.stack);
        });
});

Promise-Haters

If you dislike promises, you can always get back to the old good NodeJS-style callbacks:

var App = new kickapp.Application(function(){
});

App.start().nodeify(function(err){
    if (err)
        console.error('Application.start failed:', err.stack);
    else
        console.log('Application initialized and started!');
});

Application as a Service

The Application object can be used as a service as it implements the IService interface. This allows creating reusable components from sustainable application parts and add them to other applications as a service:

var app = new kickapp.Application(function(){
    // ... init some services
});

var top = new kickapp.Application(function(){
    this.addService('app', app); // added as a service
});

Bundled Services

KickApp comes with some handy bundled services.

NetService

NetService is a helper to wrap net.Server, http.Server, https.Server, tls.Server, dgram.createSocket network servers in a KickApp service. It also supports static configuration with generalized interface.

NetService accepts two arguments:

  • config: Object: Server configuration object:

    • config.lib: String: The server type to create. Supported values: 'net' (TCP socket), 'tls' (TLS socket), 'http' (HTTP server), 'https' (HTTPS server), 'udp4' (UDP IPv4 socket), 'udp6' (UDP IPv6 socket).

    • config.listen: Array: Array of arguments for the listen() function.

      For 'net', 'http', 'https': port, [hostname], [backlog]

      For 'tls': port, [host]

      For 'udp4', 'udp6': port, [address]

    • config.options: Object: Options for the createServer() function. See NodeJS Manual.

      Note: for 'tls' and 'https' certificates & stuff, you can optionally specify filenames for the following keys: 'pfx', 'key', 'cert', 'ca', 'crl'.

  • accept: Function: Method used to accept the incoming connections.

      For `'net', 'tls'`: `function(sock: net.Socket)`
    
      For `'http'`, `'https'`: `function(req: http.ClientRequest, res: http.ServerResponse)`
    
      For `'udp4'`, `'udp6'`: `function(msg: String|Buffer, rinfo: Object)`

Wielding the service, you can start/stop it an get error handling:

var app = new kickapp.Application(function(){
    this.addService('net', kickapp.services.NetService,
        { lib: 'net', listen: [6001, 'localhost'] },
        function(sock){
            sock.end('hi');
        }
    );
    this.addService('http', kickapp.services.NetService,
        { lib: 'http', listen: [6080, 'localhost'] },
        function(req, res){
            res.end('hi');
        }
    );
});

NetService has the following properties:

  • config: Server configuration object
  • server: The created server

TimerService

TimerService handles timers as a Service, which are stopped together with the application.

The service provides the following methods which wrap global counterparts:

  • setTimeout(), clearTimeout()
  • setInterval(), clearInterval()

By using the set*() family of methods, you add the timer to the internal service registry, and when the server is stopped -- it clears the timers automatically.

This is especially important with unit-tests: when unit-testing an application which uses timers -- these continue to function even after the tested application was restarted for a new test case.

These public methods have same signatures with the methods they wrap, except for the requirement to use these through the service:

// Define the application
var app = new kickapp.Application(function(){
    // Timers
    this.addService('timer', kickapp.services.TimerService);
    // Custom service that depends on timers
    this.addService('my-service', MyService)
        .dependsOn('timer');
});

// Custom service
function MyService(app){
    this.app = app;
};

MyService.prototype.start = function(){
    // Get the 'timer' service
    this.app.get('timer')
        // beep every second :)
        .setInterval(function(){
            console.log('beep!');
        }, 1000);
};

When the application is started with app.start(), it starts its services. The custom service "my-service" sets a timer through the "timers" service.

Now, when the application is stopped through app.stop(), the timers service will clear all timers that were set on it.