README.md

helo

Promise-based HTTP request handling stack.

Deprecation Notice

This module is no longer maintained. It still has some neat ideas though.

Installation

npm install legendary
npm install helo

This module has a peer dependency on Legendary.

Usage

See API Docs.

Request handling

Helo handles requests a little differently than a standard Node HTTP server. To see how, let's start with a standard example:

function app(req, res) {
  setTimeout(function() {
    res.writeHead(200, { 'content-type': 'text/html' });
    res.end('<p>Hello world</p>');
  }, 2000);
}

var server = require('http').createServer(app);
server.listen(8080);

The application receives a req argument, the incoming message, and a res argument, the outgoing message. It writes directly to the outgoing message.

Here's Helo's equivalent:

var delay = require('legendary').timed.delay;

function app(request) {
  return delay(2000).yield({
    statusCode: 200,
    html: '<p>Hello world</p>'
  });
}

var server = require('http').createServer();

require('helo').Stack()
  .finalize(app)
  .observe(server);

server.listen(8080);

Responses

The application does not receive the outgoing message, instead it's expected to return a (promise for a) response object. This is then written to the outgoing message by Helo. Read more about the response objects.

The promise is cancelled if the outgoing message is closed before the promise has fulfilled. This allows the application to decide whether to continue handling the request even though its response will never reach the end-user.

Requests

The request received by the application is a wrapper for the incoming message. Read more about the default request properties.

Each stack has its own Request class. You can extend its prototype:

var stack = require('helo').Stack();
stack.Request.prototype.metasyntactic = function() {
  return 'foo';
};

To add specific properties to the request, as it's instantiated, you can use Stack#addRequestInitializer():

require('helo').Stack()
  .addRequestInitializer(function() {
    this.start = this.headers['x-request-start'];
  })
  .finalize(function(request) {
    return {
      statusCode: 200,
      html: ['<p>Received request at ', request.start, '</p>']
    };
  });

Middleware

Stack#addMiddleware() can be used to set up a chain of functions that'll be invoked before the request is passed to the application:

function app(request) {
  return {
    statusCode: 200,
    html: '<p>Hello world</p>'
  };
}

require('helo').Stack()
  .addMiddleware(function(next) {
    return function(request) {
      if (Math.random() < 1/3) {
        return {
          statusCode: 200,
          html: '<p>Intercepted request before application was reached.</p>'
        };
      }

      return next(request);
    };
  })
  .addMiddleware(function(next) {
    return function(request) {
      return next(request).then(function(response) {
        if (Math.random() < 1/3) {
          return {
            statusCode: 200,
            html: '<p>Replaced application response.</p>'
          };
        }

        return response;
      });
    };
  })
  .finalize(app);

next will invoke the next middleware function or indeed the application. It always returns a promise, albeit rejected if the function throws.

Middleware should not be used to set properties on the request. Use request initializers instead.

Plugins

Plugins combine request initializers and middleware in a single object:

require('helo').Stack()
  .install({
    requestInitializer: function() {},
    middleware: function(next) {
      return next;
    }
  });

Low-level error responses

A 503 response is generated when the response promise is rejected with an error that has a cancel as its name. Similarly if the error has timeout as its name, a 504 response is generated. For all other errors a 500 response is used.

Normally these responses do not have headers or indeed a response body, but they can be customized:

require('helo').Stack()
  .setErrorResponse({
    statusCode: 500,
    headers: { 'content-type': 'text/html' },
    chunk: new Buffer('<p>An internal error occurred.</p>')
  })
  .setErrorResponse({
    statusCode: 503,
    headers: { 'content-type': 'text/html' },
    chunk: new Buffer('<p>Service unavailable.</p>')
  })
  .setErrorResponse({
    statusCode: 504,
    headers: { 'content-type': 'text/html' },
    chunk: new Buffer('<p>Service timed out.</p>')
  });

Lifecycle events

Events are emitted from the stack when new requests are made, errors occur, or responses are written. Read more about events.

And more!

See API Docs.