Hypermedia and HAL
Joost Cassee
joost@cassee.net
@jcassee
jcassee.com
Human Environment and Transport Inspectorate
October 19, 2015
I will talk about
- modeling business logic as state manipulation
- using links and profiles to create a loosely coupled web of API resources
- keeping an API malleable while maintaining backward compatibility
Resource
URI
http://example.com/ship/9334026
"An entity"
Representation
{
"name": "VOS PROMINENCE",
"imo": 9334026,
"type": "cargo"
}
Media Type
application/json
Type
Profile
http://example.com/profiles/ship
"It is a ship"
The resource identified by http://example.com/ship/9334026
{
"name": "VOS PROMINENCE",
"imo": 9334026,
"type": "cargo"
}
and is represented as application/json by
has profile http://example.com/profiles/ship
Representational State
... Transfer
A resource identified by a http/https URL
can be manipulated using HTTP methods
GET
PUT
DELETE
PATCH
POST
Retreive a representation
Store a representation
Delete a resource
Update a resource
"Process" a representation
}
idempotent
REST is CRUD
... for the most part
Expose business logic as state manipulation
Hypertext reference
href
http://example.com/company/3342
"That entity over there"
References in resource state
{
"name": "VOS PROMINENCE",
"imo": 9334026,
"type": "cargo",
"ownerHref": "http://example.com/company/3342"
}
"That is the owner"
(semantics described by the profile)
Linking
Relation
http://example.com/relations/owner
"It has an owner"
The resource identified by http://example.com/ship/9334026
to the resource identified by http://example.com/company/3342
has relation http://example.com/relations/owner
HAL
Hypermedia Application Language
application/hal+json
Why HAL?
- Links in response body:
Better client support than Link headers
- Embedded resources:
Reduce the number of requests
{
"name": "VOS PROMINENCE",
"imo": 9334026,
"type": "cargo",
...
{
"name": "VOS PROMINENCE",
"imo": 9334026,
"type": "cargo",
"_links": {
"self": {
"href": "http://example.com/ship/9334026"
},
"profile": {
"href": "http://example.com/profiles/ship"
},
"http://example.com/relations/owner": {
"href": "http://example.com/company/3342"
}
},
...
{
...
"_embedded": {
"http://example.com/relations/owner": {
"name": "Vroon B.V.",
"_links": {
"self": {
"href": "http://example.com/company/3342"
},
"profile": {
"href": "http://example.com/profiles/company"
}
}
}
}
}
State reference
- Part of resource state
- Defined by profile
- Mutable by client
- Removal is backward-incompatible
Link relation
- Part of API wiring
- Defined by relation
- Not mutable by client
- Can be deprecated and removed
vs.
{
"_links": {
"rel": { "href": "uri" }
}
}
{
"property": "uri"
}
HATEOAS
Hypertext As The Engine Of Application State
"Use links to traverse the API"
Elements of a hypermedia API
- The URI of the root resource
- A set of relations
- A set of profiles
"It's not an API, it's a protocol!"
Hypermedia clients ...
- know about profiles, relations and root URI
- treat URIs as opaque
- assume backward compatibility of state
- let presence of links drive interactions
(recover gracefully from missing links)
Hypermedia servers ...
- decide what links to provide, based on
- user roles
- service availability
- decide what resources to embed, based on
- business workflow
- client-server negotiation
URI Templates
{
"_links": {
"http://example.com/relations/owner": {
"href": "http://example.com/company/3342"
},
"http://example.com/relations/crew-search": {
"href": "http://example.com/ship/9334026/crew{?name,nationality}",
"templated": true
}
}
}
Link parameters
{
"_links": {
"http://example.com/relations/owner": {
"href": "http://example.com/company/3342",
"type": "application/hal+json",
"methods": ["GET"]
}
}
}
Hypermedia APIs ...
give you
- reuse of relations and profiles
- seamless navigation across services
- well-defined API evolution mechanism
- compatibility with server push models
require
- a departure from URL-centric design
- client robustness w.r.t. links
- design of API wiring
Reuse of profiles and relations
{
"_links": {
"self": { "href": "http://example.com/users/john" },
"profile": { "href": "http://profiles.example.com/person" }
}
}
{
"_links": {
"self": { "href": "http://example.com/clients/4335/contact" },
"profile": { "href": "http://profiles.example.com/person" }
}
}
Navigation across services
{
...
"_links": {
"self": {
"href": "http://example.com/ship/9334026"
},
"http://example.com/relations/owner": {
"href": "http://companies.example.com/3342"
}
}
}
API evolution (1) - topology change
{
"_links": {
"http://example.com/relations/owner": {
"href": "http://example.com/company/3342"
}
}
{
"_links": {
"http://example.com/relations/owner": {
"href": "http://companies.example.com/3342"
}
}
API evolution (2) - concept change
{
"_links" : {
"http://example.com/relations/owner" : {
"href" : "http://example.com/company/3342",
"deprecation": "http://example.com/docs/deprecated/owner.html"
},
"http://example.com/relations/ownership" : {
"href" : "http://example.com/ship/9334026/ownership/3"
}
}
}
Effect on development process
- Incremental and iterative API design
- Decoupling of server and client development
Server push with STOMP
SUBSCRIBE
id:0
destination:http://example.com/company/3342/ships
MESSAGE
subscription:0
message-id:007
destination:http://example.com/company/3342/ships
content-type:application/hal+json
{
"total": 5,
...
I talked about
-
modeling business logic as state manipulation
- using links and profiles to create a loosely coupled web of API resources
- keeping an API malleable while maintaining backward compatibility
Hypermedia with HAL
By Joost Cassee
Hypermedia with HAL
Presentation at the Dutch Human Environment and Transport Inspectorate.
- 2,548