Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

This guide covers the basics for creating API Builder Web Builder Web interfaces. An API Builder Web Builder Web interface is a custom endpoint that renders HTML content to a client application. An API Builder Web Builder Web interface is made up of:

  • assets (images, CSS, HTML and JavaScript files) located in the web/public folder
  • templates (EJS, Handelbars, Markdown or ReactJS) located in the web/views folder
  • API Builder Routes Builder Routes (endpoint definitions) located in the web/routes folder

You can create custom logic in your API Builder Routesyour API Builder Routes, which can internally access your service's models and APIs.

Route definition

Place all API Builder route definition files in the service's web/routes folder. You can only declare one endpoint definition per file. An API Builder route definition file is a JavaScript file, which:

  1. Loads the arrow module
  2. Calls the module's Router.extend() method method, passing in an object defining the API endpoint and logic
  3. Exports the defined endpoint using the module.exports variable

Set the following keys in the object passed to the the Router.extend() method method to define the API endpoint.

NameRequiredDescription
nametrueName of the route.
pathtrueEndpoint/path for the route.
enabledfalseSpecifies whether the route is enabled. If not, it won’t be registered, and won’t accept requests.
sortfalse

An integer that determines the order routes are registered. Routes with a higher sort value are prioritized and registered earlier. For example, say you have /route/:id and /route/foo. If the route with the wildcard has a higher sort than the static route, the static route runs. So create the first with a lower sort, and /route/foo routes properly, as does /api/bar.

methodtrueHTTP method (GET, POST, PUT, DELETE).
descriptiontrueDescription of the route.
actiontrue

The function that allows you to interact with API Builder APIs and models and send data to your template engine.

...

A renderer engine renders data (or locals in the Express framework) to the view (template file). API Builder provides Builder provides a few renderer engines and allows you to add your own custom renderer engines.

Pre-built renderer engines

API Builder Web Builder Web supports the EJS, Handlebars, Markdown, and ReactJS renderer engines. Place all template files with the appropriate extension in the web/templates folder.

Renderer EngineFile Extension
EJS.ejs
Handlebars.hbs
Markdown.md
ReactJS.jsx

To use a template in the API Builder RouteBuilder Route's logic, reference its filename without the extension. Because the template is referenced using the filename, you cannot have the same filename with multiple extensions.

...

To create a custom renderer engine you need to create a renderer engine and register it with the API Builder instanceBuilder instance's middleware instance.

  1. Create an object that implements the the createRenderer() method method and specifies the the extension property property.
  2. Pass the object to the Middleware instance's registerRendererEngine() method method. You can retrieve a Middleware instance by using the middleware property of the API Builder instanceBuilder instance.

For example, to implement a renderer engine for Jade templates:

Code Block
linenumberslanguagetruejs
var jade = require('jade'),
    engine = {};
engine.jade = jade;
engine.createRenderer = function (content, filename, app) {
    return function(filename, opts, callback) {
        if (!content) {
            content = require('fs').readFileSync(filename, 'utf8').toString();
        }
        callback(null, jade.render(content, opts));
    }
};
engine.extension = 'jade';
// server is an Arrow instance
server.middleware.registerRendererEngine(engine);

Any view with a  jade  extension will be routed to the Jade renderer engine.

Handlebars partials and helpers

API Builder exposes Builder exposes some APIs to allow you to register Handlebar partials or helpers.

...

  1. Get a reference to the Handlebar renderer engine using the the Arrow.Middleware.getRendererEngine('hbs') method method.
  2. Call either the Handlebar renderer engine's registerHelper() to to register a helper function. Pass the method the name of the helper and the function to invoke.
Code Block
linenumberslanguagetruejs
var Arrow = require('arrow'),
    hbs = Arrow.Middleware.getRendererEngine('hbs');

hbs.registerHelper('doFoo', function(foo) {
    // this.name references the name parameter passed to the template
    // in the render call, that is, res.render('template', {name: 'Joe'});
    if (foo) {
        return this.name + ' is great!';
    } else {
        return this.name + ' is ok.';
    }
});

Template example:

Code Block
languagejs
<div>doFoo(true)</div>
<div>doFoo(false)</div>

...

  1. Get a reference to the Handlebar renderer engine using the Arrow.Middleware.getRendererEngine('hbs') method.
  2. Call either the Handlebar renderer engine's registerPartial() to to register a partial file. Pass the method the name of the partial and the template file to use as a partial.
Code Block
languagejs
var Arrow = require('arrow'),
    hbs = Arrow.Middleware.getRendererEngine('hbs');
hbs.registerPartial('fooView', 'web/views/foo.hbs');

Template example:

Code Block
languagejs
<!-- Partial web/views/foo.hbs -->
<!-- id and name are passed as data to the res.render() method -->
<a href="/people/{{id}}">{{name}}</a>
 
<!-- Main Template web/views/main.hbs -->
<ul>{{#people}}<li>{{> fooView}}</li>{{/people}}</ul>

...

You can interact with API Builder APIs from your API Builder Web route. The following is an example.

Code Block
linenumberstrue
languagejs
var Arrow = require('arrow');

var TestRoute = Arrow.Router.extend({
    name: 'car',
    path: '/car',
    method: 'GET',
    description: 'get some cars',
    action: function (req, resp, next) {

        req.server.getAPI('api/car', 'GET').execute({}, function(err, results) {
            if (err) {
                next(err);
            } else {
                req.log.info('got cars ' + JSON.stringify(results));
                resp.render('car', results);
            }
        });
    }
});

module.exports = TestRoute;

In the preceding example, the route calls the car API. You can retrieve a reference to an API by specifying its path or nickname property when specified by the model/API that you are using. For example:

req.server.getAPI('api/car');

This code returns a reference to the car API. Once you have the API, you need to call execute:

...

The final part of the example is calling the template with the response data from the API call.

resp.render('car',results);

In this example, car references the name of a handlebars template file (car.hbs) and results contains the API response with the array of cars.

...

The preceding example shows how to access APIs from a route. You can also directly access models. The following modifies the preceding example to use the car model.

Code Block
linenumberstrue
languagejs
ar Arrow = require('arrow');

var TestRoute = Arrow.Router.extend({
    name: 'car',
    path: '/car',
    method: 'GET',
    description: 'get some cars',
    action: function (req, resp, next) {
        var model = req.server.getModel('car');
        model.findAll(function(err, results){
            if (err) {
                next(err);
            } else {
                req.log.info('got cars ' + JSON.stringify(results));
                resp.render('car', {cars:results});
            }

        });
    }
});

module.exports = TestRoute;

The first line of the action function retrieves the car model by name:

var model = req.server.getModel('car');

The next line calls the findAll function of the model. It’s important to note that calling APIs is different than calling models. Calling an API programmatically on the server is nearly identical to calling it remotely - you supply some input parameters and call execute and it returns the API response. Calling a model programmatically is slightly different. Since it’s a model, it does not have a REST interface. Instead, it has the functions that are called underneath the covers when an API is called, so a GET call to an API is the same as a findAll call on the model. The other difference is in the response data. The model only returns the data results - hence the results are placed in an object property called car, so my UI template can render it properly.

...