Using hypermedia to build an offline-first webapp

Joost Cassee

joost@cassee.net

@jcassee

jcassee.com

Nextbuild, 30 May 2015

Press down / right to advance slides

Human Environment and Transport Inspectorate

"Monitor and encourage compliance with legislation [for safe] human environment and transport."

Using hypermedia to build an offline-first webapp

REST as it should be

An extremely personal view on REST

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

POST

(PATCH

Retreive a representation

Store a representation

Delete a resource

"Process" a representation

Update a resource)

}

idempotent

Sending

{
  "name": "VOS PROMINENCE",
  "imo": 9334026,
  "type": "cargo"
}

results in response

GET http://example.com/ship/9334026

Accept: application/json

PUT http://example.com/ship/9334026

Content-Type: application/json

{
  "name": "COSCO BREMERHAVEN",
  "imo": 9334026,
  "type": "cargo"
}

results in changing the name of the ship

Sending

REST is CRUD

... for the most part

Expose business logic as state manipulation

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

{
  "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"
        }
      }
    }
  }
}

Links vs. properties

{
  "_links" : {
    "http://example.com/relations/profile-image" : {
      "href" : "http://example.com/user/23/profile-image"
    }
  }
}
{
  "profileImageUri": "http://example.com/images/bunny.jpg"
}

Backward-compatible 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"
    }
  }
}

Using hypermedia to build an offline-first webapp

Operate on local data, keep it synchronized

Browser Cache?

Application Cache

for pages, code and assets

index.html

<html manifest="offline.appcache">

offline.manifest

CACHE MANIFEST
# v1 2015-05-30
index.html
styles/main.css
images/logo.png
scripts/app.js

NETWORK:
*

IndexedDB

for data

Using IndexedDB

var db;
var request = indexedDB.open("MyDatabase");
request.onsuccess = function(event) {
  db = event.target.result;
};

var transaction = db.transaction("customers");
var objectStore = transaction.objectStore("customers");

objectStore.openCursor().onsuccess = function(event) {
  var cursor = event.target.result;
  if (cursor) {
    console.log(cursor.key + ": " + cursor.value.name);
    cursor.continue();
  }
};

resources:

uri -> representation

dirty:

uri

resourcecache:

Itempotent flows

GET

  1. HTTP GET request
  2. If succes: cache put
    If failure: cache get

PUT

  1. Cache put dirty
  2. HTTP PUT request
  3. If success: clear dirty
    If failure: leave dirty

 

DELETE flow similar to PUT

Non-idempotent flow

POST

  1. Cache request
  2. HTTP POST request
  3. If success: remove request
    If failure: leave request

When coming online

PUT all dirty resources

 

Reissue all stored POST requests in order

Pruning the cache

  1. Mark live resources
  2. Remove non-reachable resources

Server push

Keep related resources synchronized

Deleting an item changes the collection

STOMP

https://github.com/jmesnil/stomp-websocket

Simple pub/sub over websockets

Subscribe to resource updates

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,
  ...

Summary

  • Model business logic as state manipulation
  • Manipulate state locally
  • Synchronize state using HTTP and STOMP
  • Use IndexedDB for storing local state
  • Use Application Cache for offline operation
  • Use hypermedia for API evolution

Code will be available at https://github.com/jcassee

Photo credits

http://en.wikipedia.org/wiki/File:FCC_and_CE.jpg

https://www.flickr.com/photos/tranbc/6944672554

http://commons.wikimedia.org/wiki/File:Air_Malta_Pre_Flight_Inspection_Airbus_A320.jpg

http://commons.wikimedia.org/wiki/File:Fireworks_PDX_1.jpg

http://commons.wikimedia.org/wiki/File:Nationaal_Park_De_Biesbosch.jpg

http://jakarta.usembassy.gov/pr_09222010.html

http://commons.wikimedia.org/wiki/File:Een_%28oud%29_voorbeeld_van_een_vergunnning_2013-05-09_21-53.jpeg

http://commons.wikimedia.org/wiki/File:TsingTao_Express_IMO_9320702,_Port_of_Rotterdam,_Holland,_06JAN2009_pic1.JPG

http://commons.wikimedia.org/wiki/File:Pet_dog_fetching_sticks_in_Wales-3April2010.jpg

 

Using hypermedia to build an offline-first webapp

By Joost Cassee

Using hypermedia to build an offline-first webapp

For the Dutch Inspectie Leefomgeving en Transport I built a webapp that allow inspectors to work without an internet connection, e.g. when inside a ship. The result is a reusable library for offline apps based on Hypermedia APIs (HAL).

  • 1,563