Mason Cook Book

Basic techniques

Problem

How can a client discover relevant resources at runtime?

Solution

Use links represented in a @controls element with URLs to the relevant resources.

Discussion

Use links to bind different resources together. Links represent relations such as "parent-of" between resources. The use of links in representations allows a client to discover related resources on the fly - instead of hard coding the client with knowledge of the URL structures used.

Building clients that discover URLs on the fly opens up for some really nice improvements:

  • Client developers can explores the resources to get a better understanding of them - without having to read any documentation. The returned representations may even contain inline documentation right where the client developers need it.

  • The server can off-load certain resources to Content Delivery Networks or other servers that better handle the load of specific resources.

  • The server can add tracking, signatures and authorization elements to URLs without having to modify clients.

  • The client can interact with different independent implementations of the same service even though they reside on different servers with different URL structures.

Documentation

Documentation for links should only describe the link relation type - which in Mason means the control name. The actual URL structure used is not relavant for the client as this will be discoved at runtime.

Example

Here we have the representation of a single issue from the issue tracker. It contains links to itself (the "self" linke), its parent project (the "up" link) and its own list of attachments (the "is:attachments" link):

{
  "@namespaces":{
    "is":{
      "name":"http://elfisk.dk/mason/issue-tracker/reltypes.html#"
    }
  },
  "@controls":{
    "self":{
      "href":"http://issue-tracker.org/issues/1"
    },
    "up":{
      "title":"Containing project",
      "href":"http://issue-tracker.org/projects/1"
    },
    "is:attachments":{
      "title":"All attachments for this issue",
      "href":"http://issue-tracker.org/issues/1/attachments"
    },
  }
}

How to shorten control names with curies

Problem

Custom control names are too long and clutters readability of the JSON source. How can the it be made more readable?

Solution

Use CURIES (Compact URIs) to shorten control names.

Discussion

Compact URIs or "CURIEs" for short is a standard way of shortening URIs as described in http://www.w3.org/TR/2009/CR-curie-20090116/. A CURIE is comprised of two components, a prefix and a reference. The prefix is separated from the reference by a colon (:) - like for instance "is:attachments" where "is" is the prefix and "attachments" is the reference. In Mason the prefix refers to a namespace declaration in the @meta section.

A CURIE is expanded into a full URI by looking up the prefix in the namespace declarations and substituting the prefix with the "name" value of the namespace (and removing the colon).

All CURIEs in Mason MUST be expanded to URIs by the client before looking for control names.

CURIES are not used for href values in hypermedia controls.

Example

Here we have a namespace declaration that maps from "is" to "http://elfisk.dk/mason/issue-tracker/reltypes.html#" and one single link named "is:attachments". If we expand the prefix "is" we get the link name "http://elfisk.dk/mason/issue-tracker/reltypes.html#attachments" which in this representation is equivalent to "is:attachments".

{
  "@namespaces":{
    "is":{
      "name":"http://elfisk.dk/mason/issue-tracker/reltypes.html#"
    }
  },
  "@controls":{
    "is:attachments":{
      "title":"All attachments for this issue",
      "href":"http://issue-tracker.org/issues/1/attachments"
    },
  }
}

How to represent URL templates

Problem

How can a client discover URLs that depends on values supplied by the client?

Solution

Use templated URLs represented as a @controls element with a templated href value.

Discussion

URL templates instruct clients (at runtime) about URL construction from a well known set of variables - as for instance a query where the client is supposed to include query parameters in the URL and then GET that resource.

A URL template in Mason is declared as a @control element with the isHrefTemplate property set to true. This indicates to the client that the related href value is to be interpreted as a URL template.

The rules for variable substitution in URL templates are described in RFC 6570 - URI Template.

A JSON schema definition can be used to described the variables used in the URL template. This is not a required feature as the variable names can be parsed from the template itself but a JSON schema makes it possible to generate automatic user interfaces on the fly - similar to HTML forms but without all the layout features.

All the values for the template variables should be represented in a JSON object. Variable names are then supposed to be JSONPath expressions (without leading slashes) that refer to properties in the JSON object.

Documentation

Documentation of URL templates should declare the control name, the expected variables and their usage.

Example

The example here contains the URL template control named "is:issue-query" which represents a query for issues in the common issue tracker example. It refers to three different variables text, severity and pid. All of the variables are described in the embedded JSON schema definition.

{
  "@namespaces":{
    "is":{
      "name":"http://elfisk.dk/mason/issue-tracker/reltypes.html#"
    }
  },
  "@controls":{
    "is:issue-query":{
      "title":"Search for issues",
      "description":"This is a simple search that do not check attachments.",
      "href":"http://issue-tracker.org/issues-query?text={text}&severity={severity}&project={pid}",
      "isHrefTemplate":true,
      "schema":{
        "properties":{
          "text":{
            "description":"Substring search for text in title and description",
            "type":"string"
          },
          "severity":{
            "description":"Issue severity (exact value, 1..5)",
            "type":"int"
          },
          "pid":{
            "description":"Project ID",
            "type":"int"
          }
        }
      }
    }
  }
}

If the client supplies the variables as a JSON object as the one below then the expanded URL becomes "http://issue-tracker.org/issues-query?text=ABC&severity=3&project=17".

{
  "text": "ABC",
  "severity": 3,
  "pid": 17
}

How to represent actions

Problem

How can a client discover available actions and their operational details?

Solution

Use @controls elements to describe each possible action including both HTTP method and JSON payload.

Discussion

Hypermedia APIs that allow clients to modify content needs some way to represent the possible operations. At the most basic level all that is needed is a link:

{
  "@controls": {
    "update-stuff": {
      "title": "This is how you update stuff",
      "href": "..."
    }
  }
}

But links does not tell clients about the expected HTTP operation (POST, PUT, …​) and neither does it say anything about the expected payload - what data should be included and how should it be encoded? For this purpose Mason includes the properties "method", "encoding", "schema", "schemaUrl" and "template".

If we include "method" and "encoding" in the example then we get this:

{
  "@controls": {
    "update-stuff": {
      "title": "This is how you update stuff",
      "href": "...",
      "method": "POST",
      "encoding": "json"
    }
  }
}

With the above information clients can now discover what HTTP method to use (POST) and how the payload should be encoded (plain JSON).

Note
Currently Mason only expects clients to handle JSON encoding for simple data and "multipart/form-data" for file uploads. But future or proprietary extensions might for instance use URL encoding for simple data and Zip for file uploads - or other better suited encodings.

But knowing the encoding alone is not enough - clients should also be able to discover what the expected payload is. This can be done using JSON schema which is a format for describing JSON data structures. Such a schema can either be embedded directly in the hypermedia control using "schema" or referred to by a URL using "schemaUrl".

Here is how an embedded schema could describe the payload needed to "update stuff":

{
  "@controls": {
    "update-stuff": {
      "title": "This is how you update stuff",
      "href": "...",
      "method": "POST",
      "encoding": "json",
      "schema": {
        "type": "object",
        "properties": {
           "title": {
             "description": "The title of your stuff",
             "type": "string"
          },
          "description": {
            "description": "The description of your stuff",
            "type": "string"
          }
        },
        "required": ["title"]
      }
    }
  }
}

It is also possible to include default values for input - or rather a complete template for the payload. This is covered in the next recipe How to supply default values for input data.

Note
It can be argued that runtime discovery of method and encoding just adds unnecessary complexity as clients can be hardcoded to know these things based on the name of the hypermedia control: if the client knows how to look for the "update-stuff" control then it might as well know how to invoke it. On the other hand - if we accept that runtime lookup of URLs using links is good then we might as well go all the way and discover HTTP method and encoding at runtime too.

The same argument goes for the schema description of the payload: if the client knows how to look for the "update-stuff" control then it surely also knows what the payload is expected to be.

So why should we bother with method, encoding and schema at runtime? First of all because it enables client developers to explore and discover all parts the API. This can be a huge help when learning how an API works for the first time. It will reduce developer frustration and help them getting started with the API.

Unfortunately none of the standard browsers understand Mason (but they can still be used to render Mason as JSON structures) so a special browser is needed. At the time of writing there is only a Windows based implementation which can be found here https://github.com/JornWildt/Mason/wiki/Generic-Mason-browser.

Example

This is how the issue tracker demo represents hypermedia controls for updating and deleting a single issue:

{
  "@namespaces": {
    "is": {
      "name": "http://elfisk.dk/mason/issue-tracker/reltypes.html#"
    }
  },
  "@controls": {
    "is:issue-update": {
      "title": "Update issue details",
      "encoding": "json",
      "href": "http://issue-tracker.org/issues/1",
      "method": "POST",
      "template": {
        "Title": "Crash after payment",
        "Description": "I have justed paid for two pairs of shoes - or rather I tried to. When I clicked 'Pay' all I got was a yellow error screen.",
        "Severity": 3
      }
    },
    "is:issue-delete": {
      "title": "Delete issue",
      "href": "http://issue-tracker.org/issues/1",
      "method": "DELETE"
    }
  }
}

How to supply default values for input data

Problem

How can default values for input data be represented?

Solution

Include default values in the "template" property of a hypermedia control.

Discussion

A hypermedia control for creating new items may need to include default values to present to the end user. This could of course be included as part of the API data itself but Mason includes a standard option for handling this kind of thing - the template property of a hypermedia control. This property defines a template for the data to be sent and clients are required to take this template and merge it with other input before sending the result or resolving URL templates.

Example

Consider a hypermedia control for adding a new issue to an issue tracker. It could look somewhat like the one below with a title, a URL, the HTTP method and request encoding details:

{
  "@controls": {
    "is:add-issue": {
      "title":"Add issue",
      "encoding":"json",
      "href":"http://issue-tracker.org/projects/1/issues",
      "method":"POST"
    },

In order to supply default values to the client we now add the template object that contains two properties containing the default values for the issue title and severity:

{
  "@controls": {
    "is:add-issue": {
      "title":"Add issue",
      "encoding":"json",
      "href":"http://issue-tracker.org/projects/1/issues",
      "method":"POST",
      "template": {
        "Title": "<insert issue title here>",
        "Severity": 3
      }
    },

The client is now expected to process this as follows:

  1. Read template value and use this to present default values to the end user (if one is involved).

  2. Get user input or calculated values and store that in a JSON object.

  3. Now merge the result back into the template object: overwrite the template with user input and keep the rest of the template intact as it may contain other values necessary for the server to handle the request correctly.

How to handle file uploads

Problem

How can a client discover operations that include file uploads?

Solution

Use a @controls element with the encoding json+files.

Discussion

It is sometimes relevant to include one or more binary documents and some related JSON data in a server request in order to store those documents on the server together with the JSON data. This could for instance be a blog post with associated images or a new issue in the issue tracker with associated documentation. But how should the client encode such documents and data in one request and how does the server inform the client about it?

For this purpose Mason inctroduces the json+files encoding scheme that allows clients to send both a JSON object and a set of binary documents. This scheme requires the client to send all documents and the JSON object as multipart/form-data where each part represents either a document or the JSON object. This is identical to how HTML forms handle file uploads except that the whole data set must be JSON encoded instead of sending each JSON object property as a separate part.

For the server to inform the client of the expected encoding scheme it must of course use a @controls element with encoding set to json+files. But it may also include a jsonFile property (string) that identifies the part containing the JSON data plus a files property (array) describing the set of possible documents to send.

Example

The common issue tracker example allows clients to create new issues with attached documents. For this purpose it includes the following hypermedia control:

{
  "@namespaces":{
    "is":{
      "name":"http://elfisk.dk/mason/issue-tracker/reltypes.html#"
    }
  },
  "@controls":{
    "is:add-issue":{
      "title":"Add issue",
      "description":"Add new issue to project",
      "encoding":"json+files",
      "href":"http://mason-issue-tracker.azurewebsites.net/projects/1/issues",
      "method":"POST",
      "schemaUrl":"http://mason-issue-tracker.azurewebsites.net/schemas/create-issue",
      "jsonFile":"args",
      "files":[
        {
          "name":"attachment",
          "title":"Attachment",
          "description":"Include attachment for new issue."
        }
      ]
    }
  }}

What the above example means is:

  1. Use multipart/form-data to encode one or more documents (since encoding is json+files).

  2. Expect one document (multipart) named args which will contain a JSON object with related data.

  3. Expect one file named "attachment".

As a result the client might send a request like the one below:

POST http://issue-tracker.org/projects/1/issues HTTP/1.1
Content-Type: multipart/form-data; boundary=95e1d3c8-454e-4ced-96e7-d6f3a6e65db5


--95e1d3c8-454e-4ced-96e7-d6f3a6e65db5
Content-Disposition: form-data; name="attachment"; filename="log.txt"

... logfile content ...
--95e1d3c8-454e-4ced-96e7-d6f3a6e65db5
Content-Disposition: form-data; name="args"; filename="args"
Content-Type: application/json

{
  "Title": "Crash",
  "Description": "I pressed X and it crashed",
  "Severity": 5,
  "Attachment":
  {
    "Title": "Documentation",
    "Description": "This is the logfile"
  }
}

How to represent a home document (TBD)

Problem

Solution

Discussion

Example