API Services

Services provide a generalized way to create API calls. These calls automatically use the authentication mechanism (session id or OAuth) to perform access checks.

A Zotonic service is created in a separate service (sub)module. Here you typically define the method name, the type of access (GET or POST) and if authentication is required.

How service names are mapped to URLs

The URL to call an API service is defined by the module and the method name.

In a vanilla Zotonic install, there is one single URL namespace under which all API services can be accessed. controller_api by default intercepts all URLs according to the following patterns:

/api/:module/:method
/api/:module

On these URLs, a lookup is done to find the corresponding Zotonic module inside the services subdirectory of the module:

mod_modulename/
  mod_modulename.erl
  services/
    service_modulename_methodname.erl

If no method name is used in the /api/:module URL, the method name will be equal to the module name - see /api/search in the table below.

Examples of URLs that correspond to service handlers:

URL Module Method Located in service .erl file
/api/base/export mod_base export mod_base/services/service_base_export.erl
/api/base/info mod_base info mod_base/services/service_base_info.erl
/api/search mod_search search mod_search/services/service_search_search.erl

For creating services at alternative URLs, see Creating services at a non-standard URL in the controller_api documentation.

Service naming in detail

As stated above, a service module is defined as:

service_<modulename>_<processname>.erl

And is then reachable on the URL http://<hostname>/api/<module_name>/<process_name>.

The module module_name to be activated for the API call to work.

If you have a module named mod_something that needs a service to return stats, your directory would look like this:

mod_something/
  mod_something.erl
  services/
    service_something_stats.erl

The url for this service will be http://<site_addr>/api/something/stats

The key is that an activated module (minus the mod_ prefix if you use them!) should be part of the service name. Zotonic parses the service modules filename to identify what module a service relates to and what process should be called. It checks to make sure that module is activated and it also uses that same information when matching a service url. So, reversly, service_something_stats.erl is served by http://<hostname>/api/something/stats.

Service metadata

Like stated, any service is a regular Erlang module. There are however a few extra attributes for use in the service which describe it more. Firstly, there is svc_title:

-svc_title("Retrieve uptime statistics.").

The title of a service should be a human-readable, one-line description of what the services does. This title is used in the OAuth authentication dialog: when authorizing an application, the titles of the services that it wants to access are listed, for the authorizing user’s consideration.

Secondary there is svc_needauth:

-svc_needauth(false).

This is a boolean value which tells the system whether or not a user needs to be authorized in order to use the service.

If authentication is needed for a service, a service can only be accessed either by using the session cookie or by using an authorized OAuth (1.0a) token.

Creating a GET service

By implementing the process_get/2 function in your service module, it indicates that it is able to handle GET requests. A full example of a services which handles a GET request is listed below:

-module(service_something_stats).
-author("Arjan Scherpenisse <arjan@scherpenisse.net>").

-svc_title("Retrieve uptime statistics of the system.").
-svc_needauth(true).

-export([process_get/2]).

-include_lib("zotonic.hrl").

process_get(_ReqData, _Context) ->
    Stats = [{count, 12310},
             {uptime, 399}],
    z_convert:to_json(Stats).

This module could be called service_something_stats.erl and then gets served at /api/something/stats. Its output is a JSON object containing a count and an uptime field, containing some values.

Of course, you would write real code there which retrieves actual stats. If your module something contains the function stats_data/1, call it from the process function like this:

process_get(_ReqData, Context) ->
    Stats = mod_something:stats_data(Context),
    z_convert:to_json(Stats).

Creating a POST service

Similar to GET, by implementing the process_post/2 function in your service module, it indicates that it is able to handle POST requests. The POST parameters are accessible to you by using z_context:get_q/2.

A full example of a services which handles a POST request is listed below:

-module(service_something_process).
-author("Arjan Scherpenisse <arjan@scherpenisse.net>").

-svc_title("Processes the given id.").
-svc_needauth(true).

-export([process_post/2]).

-include_lib("zotonic.hrl").

process_post(_ReqData, Context) ->
    Id = z_context:get_q("id", Context),
    %% Do some processing here...
    Response = [{result, Id}],
    z_convert:to_json(Response).

This module could be called service_something_process.erl and then gets served at /api/something/process. It requires authentication, and is only accessible with POST and expects an id argument to be posted.

Again, its output is a JSON object containing a result field.

Service authentication

Like stated, authentication and authorization is done either through the Zotonic session or through a custom notification hook, #service_authorize{}.

For session authentication, you need to have a valid session id (z_sid) cookie. This method of authentication is the easiest when you are accessing the services from Javascript from the same domain as your user is logged in to.

When no session is available, but the called services requires authentication (according to its svc_needauth metadata attribute), a notification hook with the name service_authorize is called.

In a default Zotonic install, this service_authorize hook is handled by the OAuth module, but can be replaced by a different service authentication module.

The module implementing the service_authorize hook is expected to return either undefined (when the request is not applicable) or a response which must conform to the Webmachine is_authorized/2 return format.

Table Of Contents

Previous topic

E-mail handling

Next topic

Extensions

Versions

Actions