Request handling

Plugins can implement request handlers to respond to requests for URLs. This allows them to implement their own user interface seamlessly within the main Haplo application.

The templating system allows plugins to use the same layout (header, footer, navigation, etc) as the main application, but plugins have full control over the response body if they need it.

The request handler API aims to be close to HTTP without any unhelpful abstractions, but avoid the need to write lots of boring boilerplate code. For example, most argument decoding, data lookup and error checking can be done by the framework with simple declarations of requirements.

Declare URL roots

Declare the URL roots in plugin.json with the respond key.

Choose URLs which look good and aren’t too long. Try to pick names which aren’t too generic so they’re unlikely to clash with other plugins.

You can only use URLs starting with /do and /api. The root level namespace is used for objects, with the first path element being the section name. /do is used for user visible UI, and /api for automated systems.

Note that if you are intending to write an HTTP API which is accessed by external software, it needs to be under /api since API keys are only allowed to access URLs under this root.

Create links in main UI

Use hooks to add user interface to the main application which links to your URLs. The most useful hook for this purpose is hObjectDisplay.

In addition, Elements can be used to add user interface to the application home page and add extra information to object pages, and the work flow system can be used to add entries to the Task list and on object pages.

Handle requests

In one of your JavaScript files included in the load key in plugin.json, define the request handlers using the respond() function in your Plugin object. A very simple handler might look like this:

P.respond("GET", "/do/example/object", [
    {pathElement:0, as:"object"}
], function(E, displayedObject) {
    E.render({display: displayedObject});

Line 1 declares that the plugin will respond to GET requests for any URL at /do/example/object or any ‘sub-directory’.

Line 2 defines the argument for the handler function. In this case, the first path element after the given path is expected to be an object reference. This will be loaded and passed as the displayedObject argument. If the object doesn’t exist or the user is not permitted to read it, the handler function will never be called.

Line 3 declares the handler function. The first argument, E, is the Exchange object which represents the HTTP request / response exchange. The other arguments have been declared previously. This handler will respond to /do/example/object/123y5.

Line 4 is the actual handler. The arguments are guaranteed to have passed validation, so don’t need to be checked, and hopefully you won’t need to do much processing of parameters yourself.

The handler simply renders a Template. In this case it’s loaded from the template/object.html file, as if you don’t explicitly give a template name, the last path element of the URL in the definition is used. The correct MIME type is set for you — if the template/object.* file had a different extension, such as css, the appropriate MIME type would be sent.

For more information on how to declare arguments to your function, see the documentation for respond().


Three callbacks are provided to modify the request handling process, requestBeforeHandle(), requestBeforeRender() and requestAfterHandle().

These allow common functionality in all handlers to implemented without having to copy code into each handler, which is easy to forget. This is useful for things such as authorisation checks and common visual elements for views.


By default, HTML responses are rendered within a platform ‘layout’ to include the common user interface.

There are three layouts provided, "std:standard", "std:wide", and "std:minimal", along with the ability to turn off the layout by setting it to false. See the view’s layout property for details.

If no layout is specified, the "std:standard" layout is used.

The platform layouts include client side resource handling through templates, for example, the std:plugin:resources() template function.

CSRF protection

The platform implements automatic CSRF protection for all POSTed requests.

Unless the request is authenticated by an API key, it must include a __ (double underscore) parameter which is set to a per-session secret token. To include this token in your <form>s, use the std:form:token() template function, which generates a suitable hidden input field.