kapp

Kondoot Application Wrapper - the baseline wrapper for building distributed, centrally managed apps at Kondoot

npm install kapp
19 downloads in the last week
76 downloads in the last month

Kondoot Application Template (Kapp)

This repository is designed to make the process of creating node applications that fit into the stack used at Kondoot super simple and repeatable.

Creating a Kapp

Creating a Kapp is very simple, and basic initialization goes as follows:

var app = require('kapp')('appname');

Options can be specified that influence how a Kapp behaves, and these are covered in more detail later. Once you have completed your application specific initializations you then call app.start() to get the application to load handlers, configuration information, start the default application server (restify is used by default), etc.

When the application is ready and serving requests it will fire the ready event, and if the app.start() call is supplied a callback that will be called shortly after the ready event is triggered.

Baseline package.json for a Kapp

The following represents a baseline package.json file that should be used to create new a new Kondoot node application (assuming that you want to use restify):

{
  "name": "appname",
  "description": "Application Description",
  "author": "Kondoot <development@kondoot.com>",
  "tags": [
    "build"
  ],
  "version": "0.0.0",
  "engines": {
    "node": ">= 0.6.x < 0.9.0"
  },
  "dependencies": {
    "kapp": "0.1.x",
    "restify": "1.4.x"
  },
  "devDependencies": {
  },
  "repository": {
    "type": "git",
    "url": "git://github.com/kondoot/appname.git"
  },
  "bugs": {
    "url": "http://github.com/kondoot/appname/issues"
  },
  "scripts": {
    "test": "./node_modules/.bin/mocha --reporter spec --timeout 30000"
  },
  "contributors": []
}

If you are using a different server framework, then remove the dependeny on restify.

A Simple Application (using a Mongo Backend)

var app = require('kapp')('mongo-example', {
      beforeReady: [ connectDB ]
    });

function connectDB() {
  app.db = require('monk')(app.config.mongo);
}

app.start(function(err) {
  // if we encounted an error, then report it
  if (err) return;

  // bind handlers

  // handle mongo configuration changes
  app.config.on('update.mongo', connectDB);
});

NOTE: If the kapp is running in a "seaport enabled" environment then the

Handler Loading

One of the helpful things that Kapp does for you is load route handlers from a handlers directory from within your application structure. For instance consider the following:

- handlers
|- echo.js
|- hello.js
- server.js
- package.json

In the case above, during initialization Kapp will have autodiscovered the echo and hello handlers for you and wired them into the handlers object (app.handlers.echo and app.handlers.hello respectively). It's important to note however that the handlers are not connected to the server instance in any way as defining route handlers and directing them to the handlers is the responsibility of the application.

In terms of the actual handler functions, you are essentially implementing handlers that are identical to the application server you are using in your Kapp application (restify by default), but with a leading app argument that is injected by Kapp:

// an example echo handler
module.exports = function(app, req, res, next) {
    res.end(req.body);
};

This app argument will allow you to access your application object within your handlers without having to try and manage messy external references to the object.

Defining Application Routes

To specify your application routes, the best time to do this is in response to the started event that is emitted by a Kapp:

app.once('started', function(server) {
    server.get('/hello', function(req, res, next) {
    });
});

Authentication

By default, a KApp expects valid user credentials when serving a request. Valid credentials can either be in the form of:

  • a user header (x-kondoot-user) and basic auth, or;
  • through a session details populated by rails (stored in the _kondoot_session cookie).

For example, when registering a handler such as the one shown below, if no authentication information is provided, then the KApp will generate a 401 header without you needing to take any specific action:

app.server.get('/me', function(req, res) {
  res.end('User: ' + req.userid);
});

You may notice in the code above that a user's ID can be accessed, through the request object (req.userid). In the case of unauthenticated requests this will be set to -1.

Specifying Publicly Accessible Routes

To define a route and handler that does not require user authentication, we must define a route as public by using the allowPublic() method that KApp patches in to restify's native Route class:

app.server.get('/public', function(req, res) {
  res.end('Hi');
}).allow('public');

Specifying User Accessibly Routes

To define a route that can be accessed directly from the Kondoot frontend (as opposed to through other applications), you need to define the route as allow('user'). If you accidently type users that's ok too:

app.server.get('/frontend-ok', function(req, res) {
  res.end('Hi');
}).allow('user');

Logging in a KApp

KApps use winston for logging by default. While various options for logging were investigated, Winston provides flexible configuration and a wide variety of logging transports. Configuration for logging is set in the distrbuted configuration files.

Access to the logger is provided through the application instance, using the logger member. For instance, to log a warning the following code could be used:

app.logger.warn('That was unexpected');

Or more generically using log:

app.logger.log('warn', 'That was unexpected');

During the logging initialization phases, a KApp is configured to use syslog logging levels defined by winston:

- debug   (0)
- info    (1)
- notice  (2)
- warning (3)
- error   (4)
- crit    (5)
- alert   (6)
- emerg   (7)

NOTE: Using syslog levels the function is warning not warn. That said, the KApp initialization binds a helper from warn to warning to stop you getting in to trouble.

The default logging configuration that is used routes log messages of error and above to the console, and messages of warn and able to syslog (which is then passed on to loggly for aggregration). Additional log profiles of info and debug are also available to which will put your application into more verbose logging modes. This can be programmatically enabled in an application:

app.useLogLevel('debug');

Or through making updates to the centralized configuration section log-overrides which specify the hostname of the machine you wish to configure to use a logging scheme other than the default scheme.

Advanced: Using a server framework other than RESTify

If restify isn't your favourite framework, then you can easily implement an alternative by providing a createServer function for the kapp creation opts. A few examples of implementations for specific frameworks are shown below:

Tako

https://github.com/mikeal/tako

var tako = require('tako')
  , app;

// initialise the app
module.exports = app = require('../')('tako-example', {
  createServer: function() {
    var server = tako();

    // tako exposes the listen interface through httpServer and httpsServer
    // expose the appropriate one through a bound handler
    server.listen = server.httpServer.listen.bind(server.httpServer);

    // return the server instance
    return server;
  }
});

// start the application and create the routes
app.start(function(err) {
  if (err) return;

  app.server.route('/hello').json({ name: 'Bob' });
});
npm loves you