OpenWGA 7.10 - OpenWGA Concepts and Features

Design and development » REST web service » The REST APIs

The Custom API

The Custom API allows designers to build their own REST API via TMLScript objects. Each TMLScript object provides a REST collection plus the contained resources.

To create a new Custom REST collection create a new script module in your design under folder /scripts/tmlscript/wga/rest/custom-api. The name of the module file will be the name of the collection. The contents of the script module will be used as a custom TMLScript object. Here you can define the following methods:

query(RESTContext):

A query method called by url "/rest/v1/custom/<scriptName>". May return either a Javascript data object directly (whose JSON representation will be the request output) or some sort of iterable objects (the same kinds also valid for the query API) containing any element types. The REST server then will construct a response from the iterated objects created by your APIs resolve() method which is then mandatory.

resolve(element, RESTContext) (optional):

If method query returns an iterable object then this method is called for each iterated element to create a JavaScript data object from it, containing the data that should be put out in the response. The response will contain the JSON representation of this object in place of the given element.

A resolve method may return null to omit the given resource in the output.

get(id, RESTContext) (optional)

 A method to return a specific JSON object called by url url "/rest/v1/custom/<scriptName>/<id>". The ID from the path is given as parameter.

put(id, input, RESTContext) (optional) 

A method to update a resource from a JSON object PUT as request body to url "/rest/v1/custom/<scriptName>/<id>". The ID from the path is given as parameter. The JSON object is given as parameter "input".

delete(id, RESTContext) (optional)

A method to delete a resource on url "/rest/v1/custom/<scriptName>/<id>". The ID from the path is given as parameter.

post(input, RESTContext) (optional)

A method to insert a resource from a JSON object POSTed as request body to url "/rest/v1/custom/<scriptName>". The JSON object is given as parameter "input".


Here is an example of a query/resolve method pair for a custom API. The query() method returns an iterator of child contents below the "phones" storage of a HDB model app. The resolve() method is called for every single WGAPI content object returned from that iterator and itself returns a data object containing the data of the content that should go into the response. Additionally it builds a URL as "href" property to the detail resource.

this.query = function() {
    return HDBModel.getDocument("phones").getChildContentIterator(11);
}

this.resolve = function(con, restContext) {
    var phoneId = HDBModel.getID(con);
    var urlBuilder = WGA.urlBuilder(restContext.uri);
    urlBuilder.setPath(urlBuilder.getPath() + "/" + phoneId);
    uri = urlBuilder.build(true);

    return {
        id: phoneId,
        title: con.getTitle(),
        href: uri
    };
}

The URL to the detail resource points to the location where the get() method of the custom API is called. Here is an example method. It is given the id which was added to the URL as last element. By that it retrieves the content and returns a data object with an even more detailed overview of the objects data. Note how sub objects are also possible. In the case that the phone is not found it throws an error object with attributes code/msg which will be used as HTTP code and message for the client.

this.get = function(id) {
    var con = db().getContentByName("phones." + id);
    if (con != null) {
        return {
            id: HDBModel.getID(con),
            key: con.getDocumentKey(),
            title: con.getTitle(),
            storage: {
                flash: con.getItemValue("storage_flash"),
                ram: con.getItemValue("storage_ram")
            }
        }
    }
    else {
        throw {code:404, message:"No phone of that id: " + id};
    }
}

Now we can also defined the writing methods post/put/delete for this API which will create/update/delete resources. Have a look at these example implementations. Some notes about these:

  • put() and delete() receive the ID from the end of the URL that adresses the resource to update/delete.
  • The put() method expects to receive the object data as "input" parameter in the same format that the get() method serves. It is programmed to only update the storage data when given.
  • put() and post() both redirect to the get() method so the data of the resource that is updated/created is returned as response of these calls. delete() only returns the status as "SUCCESS".
  • post() additionally sets the selfUri of the response to the URI of the just created resource, using the id of the created phone. Otherwise the created resource would be returned but with the wrong URI, the one that was used to do the POST request.

this.put = function(id, input) {
    var con = db().getContentByName("phones." + id);
    if (con != null) {
        if (input.storage) {
            con.setItemValue("storage_flash", input.storage.flash);
            con.setItemValue("storage_ram", input.storage.ram);
            con.save();
            return this.get(id);
        }
        else {
            throw {code:400, message:"Nothing writable in your input data"};
        }
    }
    else {
        throw {code:404, message:"No phone there mate!"};
    }
}

this.delete = function(id, restContext) {
    var con = db().getContentByName("phones." + id);
    if (con != null) {
        HDBModel.deleteContent(con);
        return {status: "SUCCESS"};
    }
}

this.post = function(input, restContext) {
    var con = HDBModel.createContent("phone", null, input);
    var phoneId = HDBModel.getID(con);
    var urlBuilder = WGA.urlBuilder(restContext.uri);
    urlBuilder.setPath(urlBuilder.getPath() + "/" + phoneId);
    uri = urlBuilder.build(true);
    restContext.selfUri = uri;
    return this.get(phoneId);
}