HATEOAS – Introduction – Benefits



HATEOAS – Introduction – Benefits

0 1


mc-hateoas-evaluation

The evaluation of HATEOAS standards

On Github zmeda / mc-hateoas-evaluation

HATEOAS

WILMA - Brand Solutions

@zmeda

Introduction

[H]ypermedia [a]s [t]he [E]ngine [o]f [A]pplication [S]tate

Standard/Specification on how servers and clients interact

Introduction

  • client interacts with server entirely through hypermedia
  • hypermedia is dynamically generated at server side
  • client needs no prior knowledge on how to interact with server
  • no contract like SOA

Motivation

Initial user feedback

  • Which are the possible state transitions (from current state)?
    • Duplicated logic on server and client side
  • How to sort, filter, paginate?
  • Where is the picture of article?

Benefits

  • Discoverability
  • Consistency
  • Ecosystem
  • Self documented API

both human and machines understand and know how to follow links. use OPTIONS and GET to explore URL and understand the relationships better.

the entire application (all the APIs) look and behave in similar ways

its not about creating a application, its about a meta-application - the web. its about integration with other applications/platforms

documentation won't be for a super smart client that will automatically understand and navigate the application. Its to ease up the clients developers life when integrating or using our API

Disadvantages

  • Complexity
  • Learning curve
  • Won't replace proper documentation

Its obviously more complex to build HATEOAS applications (servers)

Its something that does not come naturally to all developers. There's a learning curve to understand how to effectively use HATEOAS. It may lead to a crAPI.

Some relationships won't be so common sense as the article of an order. These relationships will still need some proper documentation to be understanded and used effectively.

Evaluation

Known hypermedia specifications:

  • JSON API
  • Siren
  • HAL
  • Collections+JSON
  • JSON-LD

JSON API

Purpose:

  • minimize requests and data transfered
  • speed up API development
  • built by the community
  • future-proof
  • avoid bike shedding

Last update: 2015-05-29

Extendable

It is a specification for how a client should request/update resources and how a server should answer to those requests. 1. The efficiency is achieved without compromising readability, flexibility or discoverability. 2. Convention over configuration - using standards instead of writing the API for the specific use cases. 3. During the specification draft, all issues were raised via the GitHub repo and discussed publicly with the community. 4. They promise that new versions will *always* be backwards compatible. 5. Statistically, a developers team will argue more about the bike's color than the bike's wheels Version 1.0 is recent (May this year) and they are currently working on version 1.1. Currently there are some experimental, oficial and custom extensions, for example: multiple operations in single request

Example:

Get a specific order

							GET /orders/1 HTTP/1.1
Accept: application/vnd.api+json
						

Document structure

							{
  "data": null,
  "errors": null,
  "meta": null,
  "links": null,
  "included": null,
  "jsonapi": null
}
						
Every document should have one of "data", "errors" or "meta". The others are optional. "DATA" - where the actual data is provided "ERRORS" - in case some error happened "META" - for non-standard meta information "LINKS" - links for the main data represented "INCLUDED" - in case the client requests embeded data "JSONAPI" - description of server's implementation

Data structure

							{
  "data": {
    "id": "1",
    "type": "order",
    "links": {
      "self": "/orders/1"
    },
    "attributes": "...",
    "relationships": "..."
  }// ...
}
						
A resource object must always contain its type and ID. The properties of the resource go inside the attributes object May contain links for the resource or related resources.

Data structure: Attributes

							{
  "data": {
    "id": "1",
    "type": "order",
    "links": "...",
    "attributes": {
      "order-id": "1",
      "order-number": "1010101010",
      "shipment-number": "1012121212",
      "status": "approved"
    },
    "relationships": "..."
  }// ...
}
						

Data structure: relationships

							{
  "data": {
    "id": "1",
    "type": "order",
    "links": "..."
  },
  "attributes": "...",
  "relationships": {
    "order-items": {
      "data": [
        {"id": 1, "type": "order-item"},
        {"id": 2, "type": "order-item"}
      ],
      "links": {
        "related": "/orders/1/items"
      }
    }
  }
}
						

Meta structure

							{
  "data": {
    "id": "1",
    "type": "order",
    "links": "...",
    "attributes": "...",
    "relationships": "..."
  },
  "meta": {
    "order-items-count": 2,
    "order-lines-count": 3,
    "full-order-price": "1000"
  }
}
						

Siren

Siren is a hypermedia specification for representing entities.

Last update: 2015-10-21

Entity consits of:

  • class definition
  • title
  • properties
  • (sub)entities
  • links
  • actions
Entities are natural representation and well known in IT world. Class definition - identifies the entity's purpose. Title - descriptive text about the entity. Properties - a set of key-value pairs describing the state of the entity. (sub)entities - a collection of related sub entities. Important part is the relation between the parent and a child entity. links - non-entity based relation bind to the entity. actions - a collection of actions that can change the state of the entity.

Example:

Get a specific order

							GET /orders/1 HTTP/1.1
Response: application/vnd.siren+json
						

Class

Define entitiy's purpose.

							{
  "class": [ "order" ],
  "title": "",
  "properties": {},
  "entities": [],
  "links": [],
  "actions": []
}
						

Title

Descriptive text about the entity.

							{
  "class": [],
  "title": "An order entity which represents the top level order.",
  "properties": {},
  "entities": [],
  "links": [],
  "actions": []
}
						

Properties

A set of key-value pairs describing the state of the entity.

							{
  "class": [],
  "title": "",
  "properties": {
    "order-id": "1",
    "order-number": "1010101010",
    "shipment-number": "1012121212"
  },
  "entities": [],
  "links": [],
  "actions": []
}
						

Entities

A collection of related sub entities. Important part is the relation between the parent and a child entity.

							{
  "class": [],
  "title": "",
  "properties": {},
  "entities": [
    {
      "class": [ "items", "collection" ],
      "rel": [ "/rels/order-items" ],
      "href": "/orders/1/items"
    }
  ],
  "links": [],
  "actions": []
}
						
Sub entity could be either embedded link or embedded representation.

Links

Non-entity based relation bound to the entity.

							{
  "class": [],
  "title": "",
  "properties": {},
  "entities": [],
  "links": [
    {
      "rel": [ "self" ],
      "href": "/orders/1/"
    }
  ],
  "actions": []
}
						
Difference between embedded entities and links. Links are used for linking something outside of application domain (in our case article). Embedded entities are used normally within the same context (in our case order, order-line, order-item).

Actions

A collection of actions that can change the state of the entity.

							{
  "class": [],
  "title": "",
  "properties": {},
  "entities": [],
  "links": [],
  "actions": [
    {
      "name": "update-order-status",
      "title": "Update an order status",
      "method": "POST",
      "href": "/orders/1",
      "type": "application/json",
      "fields": [
        { "name": "status", "type": "radio", "value": "approved" }
      ]
    }
  ]
}
						
With the use of "radio" button we can easily manipulate state transitions.

HAL

Hypertext Application Language

Last update: 2013-09-18

Example:

Get a specific order

							GET /orders/1 HTTP/1.1
Accept: application/hal+json
						

Links

							{
  "_links": {
    "self": { "href": "/orders/1" }
  },
  "_embedded": {},
  "order-id": "1",
  "order-number": "1010101010",
  "shipment-number": "1012121212"
}
						

Resources

							{
  "_links": {},
  "_embedded": {
    "items": [{
      "_links": { "self": { "href": "/orders/1/items/1"}},
      "order_item_id": "1"
    }]
  },
  "order-id": "1",
  "order-number": "1010101010",
  "shipment-number": "1012121212"
}
						

PAGINATION

JSON API

							{
  "meta": {
    "count": 3
  },
  "data": [
    {
      "type": "order",
      "id": "1",
      "attributes": {
        "order-number": "1010121312379",
        "shipment-number": "1010123123",
        "status": "approved"
      }
    },
    {
      "type": "order",
      "id": "2",
      "attributes": {
        "order-number": "1010121312380",
        "shipment-number": "1010123124",
        "status": "approved"
      }
    },
    {
      "type": "order",
      "id": "3",
      "attributes": {
        "order-number": "1010121312381",
        "shipment-number": "1010123123",
        "status": "initial"
      }
    }
  ],
  "links": {
    "self": "/orders?page[cursor]=XYZ0",
    "first": "/orders?page[cursor]=XYZ0",
    "prev": null,
    "next": "/orders?page[cursor]=XYZ1",
    "last": "/orders?page[cursor]=XYZN"
  }
}
						

SIREN

							{
  "class": [ "orders", "collection" ],
  "title": "A list of orders",
  "properties": {},
  "entities": [],
  "links": [
    { "rel": [ "self" ], "href": "/orders/1/" },
    { "rel": [ "first" ], "href": "/orders?page[cursor]=XYZ0" },
    { "rel": [ "next" ], "href": "/orders?page[cursor]=XYZ1" },
    { "rel": [ "last" ], "href": "/orders?page[cursor]=XYZN" },
  ],
  "actions": []
}
						

HAL

							{
  "_links": {
    "self": { "href": "/orders/1/" },
    "first": { "href": "/orders?page[cursor]=XYZ0" },
    "next": { "href": "/orders?page[cursor]=XYZ1" },
    "last": { "href": "/orders?page[cursor]=XYZN" },
  },
  "_embedded": []
}
						

SORTING

Sorting is not directly a part of HATEOAS.

Best practice:

						GET /orders?sort=-created_at,status HTTP/1.1
					

FILTERING

Filtering is not directly a part of HATEOAS.

Best practice:

						GET /orders?status=initial HTTP/1.1
					

Conclusion

Team decision / Open discussion

HATEOAS WILMA - Brand Solutions @zmeda