58
Hypermedia APIs @jankronquist

Hypermedia APIs - GeekOut

Embed Size (px)

DESCRIPTION

REST is more than just JSON or XML over HTTP. In this presentation we take a closer look how to use links (hypermedia) when designing your RESTful API. Not only does this make your service discoverable and self-descriptive, but it also makes life easier for client developers as the business logic is simply checking the presence or absence of links (HATEOAS). The presentation is based on a sample application including real business logic and not just basic collection CRUD! We also explore what mediatypes exists for hypermedia and when to use them.

Citation preview

Page 1: Hypermedia APIs - GeekOut

Hypermedia APIs@jankronquist

Page 2: Hypermedia APIs - GeekOut

Hypermedia API:s @ Jayway

Rickard Öberg (~2010)!Qi4j & Streamflow!

Usecases as your API!

Mattias Arthursson & Karl-Johan Stenflo (~2011)!JAX-RS-HATEOAS!

HATEOAS with Standard Java APIs!

Mads Enevoldsen & Jan Kronquist (~2011)!Forest (CQS conventions, sensible default links, constraints...)!

Gustaf Nilsson Kotte (~2012)!Adaptive HTML as your API

Page 3: Hypermedia APIs - GeekOut

Outline

Example domain!

REST introduction (Richardson maturity model)!

Exploring media types!

html!

collection+json!

hal+json

Page 4: Hypermedia APIs - GeekOut

Example domain

Page 5: Hypermedia APIs - GeekOut

Rock - Paper - Scissors

Page 6: Hypermedia APIs - GeekOut

Opening gambit

http://www.worldrps.com/gambit-play

Page 7: Hypermedia APIs - GeekOut

Opening gambit

http://www.worldrps.com/gambit-play

Page 8: Hypermedia APIs - GeekOut

http://rps.com

The future Facebook of Rock Paper Scissors!

Millions of users!

Many games per user

Page 9: Hypermedia APIs - GeekOut

Playing the game

Player A

Player B

Server

rock

paperPlayer B: paper

Player A: rock

Game 123

Game 123 winner: Player B loser: Player A

CREATED WAITING

GAME WON

GAME TIED

any move

other move (victory)

other move (tie)

T

Page 10: Hypermedia APIs - GeekOut

Introducing REST

Page 11: Hypermedia APIs - GeekOut

Towards REST

REST architectural style!

Defined by Roy Fielding !

Richardson maturity model

http://martinfowler.com/articles/richardsonMaturityModel.html

Page 12: Hypermedia APIs - GeekOut

Level 0 - Not at all RESTful

Single resource, single verb!

Examples:!

SOAP, XML-RPC, JSON-RPC

Page 13: Hypermedia APIs - GeekOut

Level 0 - Example

POST /api --> { "method": “viewGame", "params": ["123"]} <-- { "result": { "state": "waiting"}, "error": null}

POST /api --> { "method": "makeMove", "params": ["123","rock"]} <-- { "result": { "state": "won", "winner": "player1", "loser": "player2"}, "error": null}

Page 14: Hypermedia APIs - GeekOut

Level 0 - Analysis

HTTP as transport protocol!

Problems!No client side caching!

Tight coupling

Page 15: Hypermedia APIs - GeekOut

Level 0 - In the wild

JSON/RPC over HTTP!

Why?!Simplicity!

Track all user actions!

If it ain’t broke, don't fix it

Page 16: Hypermedia APIs - GeekOut

Level 1 - Resources

Many resources, single verb!/api/games/123

/api/players/player1

Almost “object oriented”

Page 17: Hypermedia APIs - GeekOut

Level 1 - Example

POST /api/games/123 --> { "method": "viewGame"} <-- { "result": { "state": "waiting"}, "error": null}

POST /api/games/123 --> { "method": "makeMove", "params": ["rock"]} <-- { "result": { "state": "won", "winner": "player1", "loser": "player2"}, "error": null}

Page 18: Hypermedia APIs - GeekOut

Level 1 - Analysis

Still just using HTTP as transport

Page 19: Hypermedia APIs - GeekOut

Level 2 - Verbs

Resources, verb and status codes!!GET, POST, PUT, DELETE...

200 OK, 304 Not Modified, 404 Not found…

RESTish!

Uniform interface

Page 20: Hypermedia APIs - GeekOut

HTTP 101

GET - safe!

POST - danger!!

PUT - idempotent!!

Note!GET will not modify the resource, but other clients might!

Interleaving other requests might break idempotency

Page 21: Hypermedia APIs - GeekOut

GET /api/games/123 <-- 200 OK <-- { "state": "waiting"}

POST /api/games/123 --> { "move": "rock"} <-- 200 OK <-- { "state": "won", "winner": "player1", "loser": “player2"}

Level 2 - Example

Page 22: Hypermedia APIs - GeekOut

GET /api/games/123 <-- 200 OK <-- { "state": "waiting"}

POST /api/games/123 --> Content-Type: application/json --> { "move": "rock"} <-- 200 OK <-- { "state": "won", "winner": "player1", "loser": "player2"}

Level 2 - Mediatype json

Page 23: Hypermedia APIs - GeekOut

Level 2 - Mediatype form

GET /api/games/123 <-- 200 OK <-- { "state": "waiting"}

POST /api/games/123 --> Content-Type: application/x-www-form-urlencoded --> move=rock <-- 200 OK <-- { "state": "won", "winner": "player1", "loser": "player2"}

$ curl -d move=rock http://rps.com/api/games/123

Page 24: Hypermedia APIs - GeekOut

Level 2 - In the wild

Verbs: GET, DELETE, PUT!

Resources: buckets, objects, acls, policies…!

Why?!CRUD operations!

GET allows caching

Page 25: Hypermedia APIs - GeekOut

Level 2 - Analysis

Using HTTP, not fighting it!

Problems!Client must construct URLs!

Client must contain business rules

Page 26: Hypermedia APIs - GeekOut

GET /api/games <-- 200 OK <-- { "totalCount": 23, "offset": 0, "pageSize": 10, "items": [{"id": "111", "players": ["jan", "cecilia"]}, {"id": "222", "players": ["jan", "mike"]}, {"id": "333", "players": ["cecilia", "mike"]}, ...]}

Level 2 - Constructing URLs

GET /api/games?offset=10 GET /api/games?page=2

How to navigate to page 2?

Page 27: Hypermedia APIs - GeekOut

GET /api/games <-- 200 OK <-- { "totalCount": 23, "offset": 0, "pageSize": 10, "items": [{"id": "111", "players": ["jan", "cecilia"]}, {"id": "222", "players": ["jan", "mike"]}, {"id": "333", "players": ["cecilia", "mike"]}, ...]}

Level 2 - Constructing URLs

GET /api/games/{game.id}

Navigating to game

Page 28: Hypermedia APIs - GeekOut

Level 2 - Business logic

GET /api/games/123 <-- 200 OK <-- { "state": "waiting", "players": ["ply1", "ply2"], "moves": { "ply1" : true} }

Should the client display the move selector?

Page 29: Hypermedia APIs - GeekOut

Level 2 - Business logic solved?

GET /api/games/123 <-- 200 OK <-- { "state": "waiting", "players": ["ply1", "ply2"], "moves": { "ply1" : true}, "shouldMove": true }

Page 30: Hypermedia APIs - GeekOut

Level 3 - Hypermedia Controls

Links & forms!

REST as defined by Roy Fielding

Hypermedia is defined by the presence of application control information embedded within, !or as a layer above, the presentation of information

The simultaneous presentation of information and controls such that the information becomes the affordance through which the user obtains choices and selects actions.

Page 31: Hypermedia APIs - GeekOut

GET /api/games <-- 200 OK <-- { "totalCount": 23, "offset": 0, "pageSize": 10, "next": "/api/games?offset=10", "items": [{"href": "/api/games/111", "players": ["jan", "cecilia"]}, {"href": "/api/games/222", "players": ["jan", "mike"]}, {"href": "/api/games/333" "players": ["cecilia", "mike"]}, ...]}

Level 3 - Example (links)

Page 32: Hypermedia APIs - GeekOut

Level 3 - Example (form)

<html> <body> <ol id="players"> <li>Jan</li> <li>Cecilia</li> </ol> <form action="/api/games/111" method="POST"> <select name="move"> <option value="rock">Rock</option> <option value="paper">Paper</option> <option value="scissors">Scissors</option> </select> <input type="submit"/> </form> </body></html>

Page 33: Hypermedia APIs - GeekOut

Level 3 - Opportunity: Extra moves

<select name="move"> <option value="rock">Rock</option> <option value="paper">Paper</option> <option value="scissors">Scissors</option> <option value="lizard">Lizard</option> <option value="spock">Spock</option> </select>

Page 34: Hypermedia APIs - GeekOut

Level 3 - Changing the rules

For one of the players: Flip a coin!head - he must play rock!

tail - he can play whatever he wants!

!

How can the other player take advantage of this?

http://blog.gtorangebuilder.com/2014/04/gto-brain-teaser-1-exploitation-and.html

<select name="move"> <option value="rock">Rock</option> </select>

Page 35: Hypermedia APIs - GeekOut

Level 3 - In the wild

Page 36: Hypermedia APIs - GeekOut

Media types & other tools

Page 37: Hypermedia APIs - GeekOut

Media types

Format!application/json, text/xml, text/plain, text/csv!

Domain specific!text/vcard, text/calendar, application/calendar+json!

Hypermedia!text/html, application/xhtml+xml!

application/vnd.collection+json, application/vnd.hal+json!

application/vnd.siren+json, application/vnd.amundsen-uber+json

Page 38: Hypermedia APIs - GeekOut

GET /api/games <-- 200 OK <-- Content-Type: application/json <-- { "totalCount": 23, "offset": 0, "pageSize": 10, "next": "/api/games?offset=10", ... }

json

Page 39: Hypermedia APIs - GeekOut

xhtmlGET /api/games <-- 200 OK <-- Content-Type: application/xhtml+xml <--

<html> <body> <a href="/api/games?offset=10">Next</a> <div id="totalCount">23</div> <ol id="games"> <li>...</li> <li>...</li> </ol> </body></html>

Page 40: Hypermedia APIs - GeekOut

collection+json

CRUD for collections of objects!

Parts of the document:!items!

links!

queries!

templates!

errors

Page 41: Hypermedia APIs - GeekOut

GET /api/games <-- 200 OK <-- Content-Type: application/vnd.collection+json <-- { "collection": { "version" : "1.0", "href" : "http://rps.com/api/games", "links" : [ {"rel" : "next", "href" : "/api/games?offset=10"} ], "items" : [ { ... }, { ... } ] } }

collection+json

Page 42: Hypermedia APIs - GeekOut

Anatomy of a Link

target url (href)!

human readable string (title)!

semantic information (rel)!

returned media types (type)!

!

http method (method)!

secondary key (name)!

support media types for requests (accept)

RFC 5988

Page 43: Hypermedia APIs - GeekOut

Standard rels

item!

collection!

next!

edit!

enclosure!

latest-version!

self

http://www.iana.org/assignments/link-relations/link-relations.xml

Page 44: Hypermedia APIs - GeekOut

Custom rels

Register with IANA :-)!

Just make up your own :-)!move!

URI!http://rps.com/rels/move!

CURIE!rps:move!

http://rps.com/rels/{rel}

Page 45: Hypermedia APIs - GeekOut

Forms / templates

More than links!

Allow user input!

May provide!Default values!

Data types!

Possible values

Page 46: Hypermedia APIs - GeekOut

html form

<html> <body> <ol id="players"> <li>Jan</li> <li>Cecilia</li> </ol> <form action="/api/games/111" method=“POST" name="move"> <select name="move"> <option value="rock">Rock</option> <option value="paper">Paper</option> <option value="scissors">Scissors</option> </select> <input type="submit"/> </form> </body></html>

Page 47: Hypermedia APIs - GeekOut

GET /api/games/111 <-- 200 OK <-- Content-Type: application/vnd.collection+json <-- { "collection": { "version" : "1.0", "href" : "http://rps.com/api/games/111", "template" : { "data" : [{ "name" : "move", "value" : "", "prompt" : "Your move" }] } } }

collection+json template for writes

Page 48: Hypermedia APIs - GeekOut

Forced resource structure

Collection of games

/api/games/

Game 111

/api/games/111

Collection of moves for game

111

/api/games/111/moves

Player 1 move for game 111

/api/games/111/moves/ply1

item

rps:moves

item

Page 49: Hypermedia APIs - GeekOut

GET /api/games/111 <-- 200 OK <-- Content-Type: application/vnd.collection+json <-- { "collection": { "version" : "1.0", "href" : "http://rps.com/api/games/111", "links" : [ {"rel" : "rps:moves", "href" : "/api/games/111/moves"} ], ... } }

The game links to moves

Page 50: Hypermedia APIs - GeekOut

GET /api/games/111/moves <-- 200 OK <-- Content-Type: application/vnd.collection+json <-- { "collection": { "version" : "1.0", "href" : "http://rps.com/api/games/111/moves", "template" : { "data" : { "name" : "move", "value" : "", "prompt" : "Your move" } } } }

moves collection contain template

Page 51: Hypermedia APIs - GeekOut

URI templates

Constructing paths

Form-style!Making a move

Creating a new game

RFC 6570

"http://rps.com/api/games/111{?move}"

"http://rps.com/api/games/{gameId}"

"http://rps.com/api/games{?player1,player2}"

Page 52: Hypermedia APIs - GeekOut

application/vnd.hal+json

Minimalistic (plain old json)!

"_links" with mandatory rels!

"_embedded" (recursive!)

Page 53: Hypermedia APIs - GeekOut

GET http://rps.com/api/games/123 <-- { !!!!!!!!!!!!!! "createdAt" : "2014-05-22T16:24:01", "state" : "waiting" }

hal+json

Page 54: Hypermedia APIs - GeekOut

GET http://rps.com/api/games/123 <-- { !!!!!!!! "_links": { "rps:move": { "href": "http://rps.com/api/games/123{?move}", "templated": true } } "createdAt" : "2014-05-22T16:24:01", "state" : "waiting" }

hal+json

Page 55: Hypermedia APIs - GeekOut

GET http://rps.com/api/games/123 <-- { "_embedded": { "rps:player" : [{ "_links": { "self": {"href" : "http://rps.com/api/players/111"}, "name": "Jan" }, { "_links": { "self": {"href" : "http://rps.com/api/players/222"}, "name": "Cecilia" }] }, "_links": { "rps:move": { "href": "http://rps.com/api/games/123{?move}", "templated": true } } "createdAt" : "2014-05-22T16:24:01", "state" : "waiting" }

hal+json

Page 56: Hypermedia APIs - GeekOut

Application state, not objects

GET http://rps.com/api/players/111/games/123 <-- { "_links": { "rps:game": {"href" : "http://rps.com/api/games/123"}, "rps:move": { "href": "http://rps.com/api/games/123{?move}", "templated": true } } }

Page 57: Hypermedia APIs - GeekOut

GET http://rps.com/api/players/111/games/123 <-- { "_embedded": { "rps:game" : [{ "_links": { "self": {"href" : "http://rps.com/api/games/123"}, "createdAt" : "2014-05-22T16:24:01", "state" : "waiting" }] }, "_links": { "rps:move": { "href": "http://rps.com/api/games/123{?move}", "templated": true } } }

Application state, not objects

Page 58: Hypermedia APIs - GeekOut

Summary

Hypermedia simplifies client development!No constructing of URLs!

Opportunities for less business logic!

Pick a mediatype with reasonable semantics!

Consider form-encoded when POSTing!

Some useful tools!CURIE, URITemplates