Request Handling

There are only two ways of communicating with your application: evaluate an expression in it and send a request to it. This document describes the means Akshell offers for request handling.

main Function

Each request handling starts from the main() function exported by the main module of your application. main() receives a Request object as an argument; it should handle the request and return a Response object.

Here is the main.js file of the hello-world app:

require('ak').setup();

exports.main = function (request) {
  return new Response('Hello world!');
};

It returns the same text for all requests. Test it!

MVC Framework

You can handle requests via a big if and switch mess in the main() function, but this approach is rather fragile for nontrivial applications. The Akshell Model-View-Controller framework provides a simple and robust way of request handling. The defaultServe() function is a framework entry point; the main() function defaults to it.

The MVC framework splits an application into three parts:

Models
are the representation of the data upon which the application operates. In Akshell the database and the files store the data; they are described in the next chapter.
Views
render models into a form suitable for interaction; for web applications this form is HTML code. Akshell provides the template language for this purpose.
Controllers
receive requests and produce responses by operating models and views. Akshell provides the Handler base class to facilitate creation of controllers.

The MVC approach isolates domain logic (models) from input (controllers) and presentation (views). This permits independent development of each part; so the resulting application is more robust and maintainable.

The rest of this document describes controllers in Akshell; the next two documents describe models and views.

RESTful Design

REST is an acronym for Representational State Transfer. It was introduced by Roy Fielding in his doctoral dissertation. Fortunately, you don’t have to read this document to understand the REST principles employed in Akshell.

In RESTful applications requests and responses are built around the transfer of representations of resources. A resource can be any meaningful concept that may be addressed. A representation of a resource is typically a document that captures the current state of the resource. A limited set of verbs enables clients to express operations to be performed on resources.

In web applications resources are addressed by URLs; HTTP methods (GET, POST, HEAD, PUT, DELETE) constitute the set of verbs.

For example, a request such as:

DELETE /posts/42/

... should be understood by a RESTful application to refer to the post number 42 and to indicate the desired action – delete this post.

RESTful design is natural for web applications; it makes them more structured and comprehensible. Akshell encourages it in the MVC framework by providing the URLMap class for mapping addresses to resource controllers and the Handler base class for creating controllers whose operation is determined by a request verb (an HTTP method).

URL Mapping

An URL mapping tie together resources and their URLs. It serves as a front end of request handling: an URL dispatching determines a controller responsible for the requested resource by the value of the request.path property. The mapping is also capable of the reverse task: determine the path of the given resource by its name and arguments.

An URL mapping of an application is defined by the root object exported by main.js. It should be an instance of the URLMap class. The mapping is a tree-like structure where each node is either a constant part of a path (a string value) or a variable part of it (a RegExp object or an empty string '' for the default pattern ([^/]+)/).

Dispatching

After a request has arrived and has been processed by the middleware (see below), an URL dispatching takes place. It starts from the root of the mapping tree and applies the patterns of the tree nodes one after another until the path matches one of them.

Example:

exports.root = new URLMap(
  IndexHandler, 'index'
  ['users/',
   ['',
    ['profile/', ProfileHandler, 'profile'],
    ['posts/', PostsHandler, 'posts',
     ['add/', AddPostHandler, 'add-post'],
     [/(\d+)\//, PostHandler, 'post']
    ]
   ]
  ]);

It maps path patterns to controllers:

  • / to IndexHandler;
  • /users/userName/profile/ to ProfileHandler;
  • /users/userName/posts/ to PostsHandler;
  • /users/userName/posts/add/ to AddPostHandler;
  • /users/userName/posts/postId/ to PostHandler.

The pattern tree is the following:

digraph {
   rankdir = LR;
   node [
      shape = box,
      fontname = monospace,
      fontsize = 10,
      height = .25,
      width = .9,
      fixedsize = true
   ];

   "/" -> "users/" -> "([^/]*)/" -> "profile/";
   "([^/]*)/" -> "posts/" -> "add/";
   "posts/" -> "(\\d+)/";
}

The controller found by the URL dispatching is responsible for the rest of the request handling. If the dispatching has failed, a 404 “Not found” response is returned.

Reversing

Note that each pattern in the example has a string name ('index', 'profile', etc.); it’s optional and intended for reconstructing a path of a particular resource. This task is reverse to dispatching; it can arise, for example, when you need to redirect a user to this resource.

A resource path can be reconstructed in-place, but this approach is ugly because it violates the DRY principle: an URL mapping should be defined only once in one place to be maintainable, not scattered about the whole application. Akshell provides a clear solution to the problem – the reverse() function. It accepts a name of a map pattern and positional arguments for variable parts of the pattern.

In the previous example reversing will work as follows:

>>> reverse('index')
/
>>> reverse('profile', 'Anton')
/users/Anton/profile/
>>> reverse('post', 'Anton', 42)
/users/Anton/posts/42/

Handlers

Handlers are responsible for retrieving data from a database and a file storage, making the necessary changes to them, forming a response content (usually via templates), and returning a response.

A handler receives a request object and positional arguments obtained by an URL dispatching. A plain JavaScript function can serve as a handler, but the Handler base class offers a more RESTful solution. To create a resource controller, subclass this class and define get(), post(), head(), put(), or del() methods performing the required actions. The subclass constructor should perform initialization common for all methods.

For example:

var PostsHandler = Handler.subclass(
  function (request, userName) {
    this._user = ... // Retrieve the user info
  },
  {
    get:  function (request, userName) { /* Return a post list */ },
    post: function (request, userName) { /* Create a new post  */ }
  });

Note that the constructor and the methods receive the same arguments. This redundancy is deliberate: it’s convenient to have request arguments at hand.

The zest of handler classes is that you can subclass them. In a subclass you can use properties set by the parent class constructor and methods of its prototype.

For example, a handler of single post could be written as follows:

var PostHandler = PostsHandler.subclass(
  function (request, userName, postId) {
    PostsHandler.call(this, request, userName);
    this._post = ... // Retrieve the post info
  },
  {
    get: function () { /* Return a post representation */ }
  });

Note that you can use the post() method of the parent class in PostHandler, but it won’t be called if PostHandler receives a POST request – that’s exactly what you need.

Middleware

Some logic is common for the whole application; so it would be inconvenient to add it to every handler class. Akshell offers a DRY way of implementing such logic – application middleware. A piece of middleware is a decorator of the serve() function; it adds the desired behavior to the very entry point of request handling. The above-mentioned defaultServe() function is simply serve() with the default middleware applied.

For example, the serve.catchingFailure() middleware catches Failure exceptions thrown by your handlers and returns error responses rendered from the error.html template. This way of error handling is extremely handy because you don’t have to worry about returning an appropriate error response whenever you encounter an error situation – you just throw a Failure exception and forget about it.

You could borrow middleware from third-party libraries or even write you own – it’s easy. For example, this middleware adds a custom HTTP header to all responses:

function addingUselessHeader(func) {
  return function (request) {
    var response = func(request);
    response.headers['X-Useless'] = '42';
    return response;
  };
}

To enable it in your application decorate the defaultServe() function:

exports.main = defaultServe.decorated(addingUselessHeader);