Resource Representations

From Ocean Framework Documentation Wiki
Jump to: navigation, search

General Resource Representation

A resource JSON representation as received from the API will always have the following general structure:

{"<resource name>":
  {"<key1>": <value1>,
   "<key2>": <value2>,
   "<key3>": <value3>,
   ...
   "_links": { ... }
  }
}

This is an Authentication resource, generated in the staging environment for the webapp client. We've pretty-printed it here for legibility.

{"authentication": { 
   "api_user": "webapp_client", 
   "max_age": 1800, 
   "token": "lIbNpdvo9MSN2WHDagnKE4xQP0E",
   "created_at": "2012-12-01T17:09:04Z", 
   "expires_at": "2012-12-01T17:39:04Z", 
   "_links": {"self": 
                {"href": "https://staging-api.example.com/v1/authentications/lIbNpdvo9MSN2WHDagnKE4xQP0E", 
                 "type": "application/json" }, 
              "api_user":
                {"href": "https://staging-api.example.com/v1/api_users/cfab1554-7ac6-489e-81cb-94ee8a4c1eb5", 
                 "type": "application/json"}
             }
   }
 }

Receiving Resources

A resource is always a nested object as received from the API. The outermost object is only there as a wrapper to provide the type of the resource: <resource name> could be user, basket, app or anything along the same lines. The type wrapper allows resources of different types to be sent in the same collection.

The innermost object is the actual resource: a hash specifying the names and values of attributes.

It always contains the key _links, the value of which is a hash array of all valid hyperlinks (see Hypermedia).


Sending Resources

When sending a resource to the Ocean API, the type wrapper should not be included. Only the inner object attributes should be sent, i.e. everything except the _links hash (you may send it, but it serves no purpose).

PUT

When modifying a previously received resource via PUT, you should send back the inner object only, in its entirety. You should not send it with a type wrapper.

PATCH

To set only some resource attributes, you can use PATCH in a similar way to PUT.

POST

When creating a new resource using POST, attributes should be passed as in a PATCH. You may omit optional attributes.    

The Importance of Hyperlinks

The API documentation for each resource representation describes its attributes (== data fields == properties == key/value pairs) and their meaning and intended use. It will also describe all hyperlinks and the conditions under which each one they appears in the representation. Resources are stateful, in that only those hyperlinks which may be called at a given point in the resource's life cycle are present in the representation at that time.

It's important to understand that apart from the Public URIs – the inroads to the service – the API descriptions don't include any URIs at all. Thus, to be HATEOAS compliant, clients should never construct Internal URIs to obtain the resources they represent, but should instead use Public URLs (which clients are allowed to construct freely) and, more importantly, hyperlinks to discover the URIs of related data. This is extremely important in that it yields a very high degree of decoupling between server and client.

Hypermedia

Each resource representation, being hypermedia, always contains a property with a key of _links. Its value is a hash array of hyperlinks. Each hyperlink entry is keyed by the name of the relation, such as self. The value of each hyperlink item is another hash array of at least two items, href, and type, where href is the URI of the resource, and type is the MIME type of the resource to be obtained at that URL. Every resource representation will always have at least one hyperlink called self giving the resource's URI.

Here's an example of a User resource representation with hyperlinks for self, address, phone, and avatar:

{"user": {"name": "Joe Blow",
          "age": 42,
          "body_type": "mesomorph",
          "faction": "orch",
          "alignment": "chaotic",
          "_links": { "address":
                        { "href": "https://apis.example.com/v3/addresses/12235a7c-9524-4ba6-901f-79c7a68ac74e",
                          "type": "application/json" },
                      "phone":
                        { "href": "https://apis.example.com/v3/phone_numbers/24f6e5f9-151d-4889-8d00-76306abd069e",
                          "type": "application/json" },
                      "self":
                        { "href": "https://apis.example.com/v3/users/58bf9129-6a35-4c65-a978-91a0433d28a3",
                          "type": "application/json" }
                      "avatar":
                        { "href": "https://media.example.com/users/c6c930a1-89b9-40de-b6c9-8dd502331604/5/4/avatar.jpg",
                          "type": "image/jpeg" }
                     } 
          }
 }

From the above, you can see that this particular resource representation includes properties called name, age, body_type, faction, and alignment.

The _links property is a hash of objects, each with two key/value pairs and keyed on the hyperlink name (ref). In all our documentation, when we refer to something like "the foo hyperlink", we mean "the object with a key of foo in the resource's _link hash". Very often, we specifically mean the href value which contains the URL of the resource.

The object has two members:

href
The URI, such as https://api.example.com/v2/doodads or http://somewhere-else.com/some/path. The URI is opaque. This means that you should never modify it and never draw any conclusions from its perceived structure. In some cases you may add query arguments; this is described in the API documentation for each resource.
type
The MIME type to use in the HTTP header of requests made to the URI. In calls to the API it is normally application/json or a vendored type such as application/vnd.com.example.ocean+json. For an image, for instance a type such as image/jpeg might be used. Indeed, any MIME type may be used here.

In the above resource representation the hyperlink data is takes up more space than the rest of the properties, but this won't always be the case. Most frequently, there will only be the self link.

Hyperlink URIs are Opaque

The API documentation never describes the structure of the href URIs. As stated repeatedly elsewhere in this documentation, you shouldn't make any assumptions as to the structure of these URIs. Given the resource representation example above, it's tempting to deduce that the real ID of the user Joe Blow is 12235a7c-9524-4ba6-901f-79c7a68ac74e, and then start constructing URLs based on this assumption. However, this is something you must never do, since

  1. your assumption may be incorrect and/or incomplete, and
  2. the URI may change without prior notice.

For instance, not only the ID part may vary, but the domain might too, e.g. if the architects of an Ocean application at some point decide that the collection of resources is to be moved or sharded.

You should treat the href URIs in resource representations as opaque. In fact, you never need to obtain any database ID of a user (if that even is a relevant notion) – instead, you must realise that the self hyperlink in its entirety is the real ID of the resource, and that the resource always can be obtained by making a HTTP GET request to its URI.

A simple solitary ID such as 12235a7c-9524-4ba6-901f-79c7a68ac74e doesn't on its own say anything about the kind of object it is meant to represent. Solitary IDs always rely on out-of-band information (such as to which resource type the ID applies and on what server) which always must be built into the logic of the receiving entity. Relying on out-of-band information creates coupling between client and server, something which REST is designed to eliminate to as large an extent as possible. URIs, on the other hand, are complete IDs which unambiguously identify a resource anywhere on the internet.

NB: the hyperlink consists of both a URI and a content type, both of which may change at any time. You should use the provided content type where applicable, and when storing the hyperlink for later use you should also therefore also store the content type with the URI.