HTTP REST API

When a model has been declared, HTTP REST access to your modeled resources can be enabled in just one simple step. This document shows how to enable the HTTP REST API for your declared resource models and shows how the Model API and queries are performed in HTTP.

Sample resource model

The following examples that follow assumes the following persons model has been declared in /app/models/persons.json:

{
    "fields": {
        "firstname": {"type": "string"},
        "birthdate": {"type": "date"}
    }
}

Enabling HTTP access

The HTTP API has many symmetries to the Model API and allows HTTP REST access to models collections and members with very little coding. The HTTP API is not enabled by default but requires you to provide a resource event handler in the /app/resources directory. In its simplest form, the application resource event handler delegates requests to a ZRM-provided resource event handler.

A resource event handler for the persons model located in /app/resources/persons.groovy only needs to contain:

ZRM.delegate()

Similarly, if you are using PHP, you will first need to declare a dependency on zero.php in the /config/ivy.xml file. Now create a resource event handler for the persons model located in /app/resources/persons.php which only needs to contain:

<?php
zrm_delegate();
?>

The ZRM resource event handler provides default data access functionality with HTTP for the data defined in the /app/model file. The remainder of this section describes that default functionality.

Reading collections

The Model API introduced the concept of a model's collection, Member instances and collection filtering. ZRM's HTTP API maps HTTP requests to invocations on the Model API, providing access to the same collection and member through easy to learn (and construct) URIs.

ZRM follows common REST idioms. Namely, you can use the HTTP GET command for a full or filtered a model's collection and POST member instances into the collection. You can also learn more about Reading and writing model collection member instances.

The HTTP GET URI syntax for addressing model collections is shown in the following example:

<urlPrefix>/<collectionName>[?<queryParameters>]

The following examples are valid URIs for the persons model:

http://localhost/resources/persons
    Unfiltered collection of members

http://localhost/resources/persons?firstname__startswith=S
    Filtered collection of members where all firstname values start with 'S'

Collection filtering

You can filter collection by limiting the set of members to be returned based on one or more filter conditions. A set of filter parameters is enabled for each collection type based on the data model specified. Some examples were given earlier in this document. Here are some further examples:

http://localhost:8081/resources/persons?firstname__contains=Jo
    Filtered collection where member instances' firstname field contains
    'Jo'
http://localhost:8081/resources/persons?firstname__contains=Jo&birthdate__day=25,
    Filtered collection where member instances' firstname field contains
    'Jo' and birthdate falls on the 25th of the any month.

The valid filter conditions and corresponding parameter syntax for the URL is based on the Model API filtering syntax of the Zero Resource Model. This is defined in the Filter conditions section under the Model API topic.

The URL syntax uses equal signs (=) to set the filter parameters and ampersands (&) to combine the filter conditions.

Collection paging

Just as the Model API allows for specifying slice, or subset, of the overall collection, the HTTP API allows you to indicate a page range. This is done by defining the start and count parameters in the URI as follows:

http://localhost/resources/persons?start=100&count=199
    Unfiltered collection of members from 101 to 200

http://localhost/resources/persons?firstname__contains=rand&start=5&count=5
    Filtered collection of members that contain 'rand' and from 6 to 10
Collection paging uses zero-based indexing. For example, the first member in the collection is indexed at 0 and the 10th at 9.

If the data slice specified does not have a complete "page", the returned members are as expected. For example, if there are 7 members in a given collection and the first request is for start=0&count=5 then items 1 through 5 are returned. If the second request is made for start=5&count=5 then items 6 and 7 are returned.

Specifying ordering in the following section changes the members in specified page ranges.

You can optionally specify both start and count. One or both must exist in order to obtain a slice of the results. The lack of both parameters means the results is not paged. However, if count is missing, the results start at the specified start and return up to the end of the collection. Similarly, if start is missing, the results start at the beginning of the collection and return the number of members specified by count.

Collection ordering

As illustrated in the URIs in the previous example, you can order collections results in your HTTP GET requests. By default, HTTP collection requests for the Atom representation are sorted by members' updated value in descending order. Conversely, JSON collection representations are not sorted, or rather, or sorted from oldest to more recently created.

The order_by value syntax for the URL is based on the ZRM API syntax which is defined in the "Model API" section. Ascending order is the default. To indicated descending order, prefix - before the field name as show in the previous URI examples:

http://localhost/resources/persons?order_by=birthdate
    Unfiltered collection of members sorted youngest to oldest
http://localhost/resources/persons?id__lt=100&order_by=-birthdate,name
    Filtered collection of members with id less than 100, sorted first from
    oldest to youngest and then by name from A to Z

Collection rendering

At present, collection data can be represented in either JSON or the Atom syndication format. The desired representation can be specified in the Accept header of the HTTP GET request by specifying the associated MIME type. The values application/json and application/atom+xml render JSON and Atom, respectively, to the response.

An alternative mechanism is provided for requesting a specific representation by using the URL query parameter format_as. Valid values for this parameter are json and atom:

http://localhost/resources/persons?format_as=atom
    Collection of members with representation returned as Atom.
http://localhost/resources/persons?format_as=json
    Collection of members with representation returned as JSON.

The default format is JSON if the Accept header is not specified and the URI parameter is not provided.

JSON representations

ZRM uses a JSON serialization APIs to render the JSON representation. A model collection is a JSON array of objects. Each object contains the member instance of the model in the collection. For example, the persons model when the collection contains two members rendered in JSON is:

[
    {
        "firstname": "Jane",
        "birthdate": "1973-12-03"
    },
    {
        "firstname": "John",
        "birthdate": "1982-04-29"
    }
]

Atom format

The default Atom representation of a model's collection is as you would expect: an Atom feed containing an Atom entry for each member. The default serialization of the model instance member data is put in the atom:content section as key, value pairs in XOXO format. The remaining required atom elements are set to default values as shown in the following example of the default Atom representation of the same data in the JSON example that was shown above:

<feed xmlns="http://www.w3.org/2005/Atom">
    <title>Title of Type persons Feed</title>
    <link href="http://localhost/resources/persons?format_as=atom"/>
    <updated>2003-12-13T18:30:02Z</updated>
    <id>"http://localhost/resources/persons"</id>
    <entry>
      <title>Title of persons 2</title>
      <link href="http://localhost/resources/persons/1?format_as=atom"/>
      <link rel="edit" href="http://localhost/resources/persons/1"/>
      <id>"http://localhost/resources/persons/1"</id>
      <updated>2003-12-13T18:30:02Z</updated>
      <author>Author of persons 1</author>
      <content type="xhtml">
        <div xmlns="http://www.w3.org/1999.xhtml">
        <ol class='xoxo'><li>persons<dl>
          <dt>firstname</dt><dd>Jane</dd>
          <dt>birthdate</dt><dd>1973-12-03</dd>
        </dl></li></ol>
        </div>
      </content>
    </entry>
    <entry>
      <title>Title of persons 2</title>
      <link href="http://localhost/resources/persons/2?format_as=atom"/>
      <link rel="edit" href="http://localhost/resources/persons/2"/>
      <id>"http://localhost/resources/persons/2"</id>
      <updated>2003-12-13T18:30:02Z</updated>
      <author>Author of persons 2</author>
      <content type="xhtml">
        <div xmlns="http://www.w3.org/1999.xhtml">
        <ol class='xoxo'><li>persons<dl>
          <dt>firstname</dt><dd>John</dd>
          <dt>birthdate</dt><dd>1982-04-29</dd>
        </dl></li></ol>
        </div>
      </content>
    </entry>
</feed>
Customizing the Atom representation

You can use the model declarations in the /app/models folder to customize the rendering of an Atom entry. You can override named templates in each model declaration using the calculated_fields definition.

Adding the following definition to the persons model's /app/model/persons.json file and using the same member instance data in the previous example results in the following Atom entry:

{
    "fields" : { . . . },
    "calculated_fields" : {
         "atom_author_name": "$member.lastname",
         "atom_title": "$member.firstname",
         "atom_summary": "All about ${member.firstname}."
    },
    "calculated_type_fields" : {
        "atom_feed_title": "My Custom Feed Title"
    }
}

<feed xmlns="http://www.w3.org/2005/Atom">
    <title>My Custom Feed Title</title>
     <link href="http://localhost/resources/persons?format_as=atom"/>
     <updated>2003-12-13T18:30:02Z</updated>
     <id>"http://localhost/resources/persons"</id>
     <entry>
        <title>Jane</title>
        <link href="http://localhost/resources/persons/1?format_as=atom"/>
        <link rel="edit" href="http://localhost/resources/persons/1"/>
        <id>"http://localhost/resources/persons/1"</id>
        <summary>All about Jane.</summary>
        <updated>2003-12-13T18:30:02Z</updated>
        <author>Doe</author>
        <content type="xhtml">
            <div xmlns="http://www.w3.org/1999.xhtml">
             <ol class='xoxo'><li>persons<dl>
                <dt>firstname</dt><dd>Jane</dd>
                <dt>lastname</dt><dd>Doe</dd>
             </dl></li></ol>
            </div>
        </content>
     </entry>
</feed>

Reading and writing collection members

Member rendering and parsing

Incoming data used for creating and updating members must be either in JSON or Atom representations. The Content-Type header of the HTTP request determines how the data is parsed. The MIME types application/json and application/atom+xml parse JSON and Atom, respectively. If neither is specified, the default parser is JSON.

Creating a member

New collection members can be created by making an HTTP POST request to the collection URI of the model with the member representation as shown in the following example:

POST /resources/persons
Content-Type: application/json
{
   "firstname": "Bill",
   "birthdate": "1976-09-25"
}

Retrieving a member

An individual collection member can be retrieved by issuing an HTTP GET on the collection URI with the member ID as a parameter, as shown in the following example:

GET resources/persons/1

Updating a member

A collection member can be updated by issuing an HTTP PUT with the member representation on the collection URI, as shown in the following example:

PUT /resources/persons/1
Content-Type: application/json
{
   "firstname": "Janie",
   "birthdate": "1973-12-04"
}

Deleting a member

A collection member can be deleted by issuing an HTTP DELETE with the member ID on the collection URI, as shown in the following example:

DELETE /resources/persons/1

Reading type collection metadata

By default, the metadata of a collection can be retrieved through HTTP as a JSON representation. This is done using the following URI convention:

/resources/types[/typesId]

For example, assume that the following resource model is defined:

{
    "fields" : {
        "first_name" : {"type":"string", "required": true},
        "birth_date":{"type":"date"},
        "is_child":{"type": "boolean"}
    }
}

With this model in place, the following HTTP JSON response is returned:

GET /resources/types/persons

{
  "type":"person",
  "fields": {
    "id": {
       "type":"auto",
       "required":false,
       "editable":true,
       "primary_key":true
    },
    "updated": {
       "type":"date-time",
       "required":false,
       "editable":true,
       "primary_key":false
    },
    "birth_date": {
       "type":"date",
       "required":true,
       "editable":true,
       "primary_key":false
    },
    "first_name": {
       "type":"string",
       "required":true,
       "editable":true,
       "primary_key":false,
       "max_length":50
       },
    "is_child": {
       "type":"boolean",
       "required":true,
       "editable":true,
       "primary_key":false
    }
  }
}

By specifying the named resource model /resources/types/persons only the JSON object for the persons type is returned. You can obtain a list of all resource model definitions by making an HTTP GET request to /resources/types.

Version 1.1.0.0.21442