Building Hypermedia 
APIs in Javascript 
from homemade solution to using Fortune.js
You? 
HTML5 Conference SF Oct. 2014 Nicolas Grenié - !picsoung
You? 
• Who uses APIs? 
HTML5 Conference SF Oct. 2014 Nicolas Grenié - !picsoung
You? 
• Who uses APIs? 
• Who knows what Hypermedia APIs are? 
HTML5 Conference SF Oct. 2014 Nicolas Grenié - !picsoung
You? 
• Who uses APIs? 
• Who knows what Hypermedia APIs are? 
• Who had ever build one? 
HTML5 Conference SF Oct. 2014 Nicolas Grenié - !picsoung
Me 
HTML5 Conference SF Oct. 2014 Nicolas Grenié - !picsoung
Me 
• 3scale evangelist 
HTML5 Conference SF Oct. 2014 Nicolas Grenié - !picsoung
Me 
• 3scale evangelist 
• API freak 
HTML5 Conference SF Oct. 2014 Nicolas Grenié - !picsoung
Me 
• 3scale evangelist 
• API freak 
• Hacker in Residence 
HTML5 Conference SF Oct. 2014 Nicolas Grenié - !picsoung
Me 
• 3scale evangelist 
• API freak 
• Hacker in Residence 
• Node.js, Meteor 
HTML5 Conference SF Oct. 2014 Nicolas Grenié - !picsoung
Hypermedia 101 
HTML5 Conference SF Oct. 2014 Nicolas Grenié - !picsoung
you are already using 
Hypermedia APIs 
HTML5 Conference SF Oct. 2014 Nicolas Grenié - !picsoung
Core values 
HTML5 Conference SF Oct. 2014 Nicolas Grenié - !picsoung
Core values 
• Discoverable API 
HTML5 Conference SF Oct. 2014 Nicolas Grenié - !picsoung
Core values 
• Discoverable API 
• Machine readable API 
HTML5 Conference SF Oct. 2014 Nicolas Grenié - !picsoung
Core values 
• Discoverable API 
• Machine readable API 
• Link-based 
HTML5 Conference SF Oct. 2014 Nicolas Grenié - !picsoung
Example: Github 
GET api.github.com 
{ 
current_user_url: "https://api.github.com/user", 
authorizations_url: "https://api.github.com/authorizations", 
code_search_url: "https://api.github.com/search/code?q={query} 
{&page,per_page,sort,order}", 
emails_url: "https://api.github.com/user/emails", 
emojis_url: "https://api.github.com/emojis", 
events_url: "https://api.github.com/events", 
feeds_url: “https://api.github.com/feeds",ed{/owner}{/repo}", 
… 
team_url: "https://api.github.com/teams", 
user_url: "https://api.github.com/users/{user}", 
user_organizations_url: "https://api.github.com/user/orgs", 
user_repositories_url: "https://api.github.com/users/{user}/ 
repos{?type,page,per_page,sort}", 
user_search_url: "https://api.github.com/search/users?q={query} 
{&page,per_page,sort,order}" 
} 
HTML5 Conference SF Oct. 2014 Nicolas Grenié - !picsoung
Example: building 
REST approach 
/rooms/r123 
/doors/d123 
HTML5 Conference SF Oct. 2014 Nicolas Grenié - !picsoung
Example: building 
Adding relations 
/rooms/r123 
• /rooms/r123/doors/d123 
/doors/d123 
• /doors/d123/rooms/r123 
HTML5 Conference SF Oct. 2014 Nicolas Grenié - !picsoung
Example: building 
Buildings have hallways 
/doors/1/rooms/1 
/doors/1/hallways/1 
/rooms/1/doors/1 
/rooms/1/hallways/1 
/hallways/1/doors/1 
/hallways/1/rooms/1 
HTML5 Conference SF Oct. 2014 Nicolas Grenié - !picsoung
Me… 
HTML5 Conference SF Oct. 2014 Nicolas Grenié - !picsoung
Me… 
HTML5 Conference SF Oct. 2014 Nicolas Grenié - !picsoung
Let’s put Hypermedia in it 
Linking ressources 
• /rooms/1 
- rel: door, href: /what/ever/door 
- rel: door, href: /another/door 
• /door 
- rel: entry-to, href: /rooms/1 
- rel: entry-to, href: /hallways/1 
• /hallways/1 
- rel: floor, href: /floors/1 
HTML5 Conference SF Oct. 2014 Nicolas Grenié - !picsoung
HATEOAS 
Hypermedia as the Engine of Application State 
• Basic implementation with Express 
• add “link” properties to the JSON response 
HTML5 Conference SF Oct. 2014 Nicolas Grenié - !picsoung
HATEOAS 
GET mybuilding.com/rooms/1 
{ 
"data": { 
"name": "Kitchen", 
"id": "1", 
"links" : [ 
{ 
"rel": "door", 
"href": "https://mybuilding.com/rooms/12345678" 
}, { 
"rel": "door", 
"href": "https://theirbuilding.com/rooms/ 
12345678" 
} ] 
} 
} 
HTML5 Conference SF Oct. 2014 Nicolas Grenié - !picsoung
There is a framework for that 
HTML5 Conference SF Oct. 2014 Nicolas Grenié - !picsoung
Fortune.js 
HTML5 Conference SF Oct. 2014 Nicolas Grenié - !picsoung
Fortune.js 
• simple to setup 
var fortune = require('fortune') 
, app = fortune({ 
db: 'petstore' 
}) 
.resource('person', { 
name: String, 
age: Number, 
pets: ['pet'] // "has many" relationship to pets 
}) 
.resource('pet', { 
name: String, 
age: Number, 
owner: 'person' // "belongs to" relationship to a person 
}) 
.listen(1337); 
HTML5 Conference SF Oct. 2014 Nicolas Grenié - !picsoung
Fortune.js 
HTTP Person Pet Notes 
GET /people /pets 
HTML5 Conference SF Oct. 2014 Nicolas Grenié - !picsoung 
Get a collection 
of resources, 
accepts query ? 
POST /people /pets 
Create a 
resource 
GET /people/:id /pets/:id Get a specific 
resource, or 
multiple: 1,2,3 
PUT /people/:id /pets/:id Create or update 
a resource 
PATCH /people/:id /pets/:id Patch a 
resource (see 
RFC 6902) 
DELETE /people/:id /pets/:id Delete a 
resource 
GET /people/:id/pets /pets/:id/owner Get a related 
resource (one 
level deep)
Fortune.js 
• JSON API spec 
{ 
"people": [ 
{ 
"id": "tO69XnLz7amaYVRq", 
"name": "Wall-E", 
"age": 1000, 
"links": { 
"pets": [ 
"dPuXqUJxRIn9cOdH" 
] 
} 
} 
], 
"links": { 
"people.pets": { 
"href": "/pets/{people.pets}", 
"type": "pets" 
} 
} 
} 
HTML5 Conference SF Oct. 2014 Nicolas Grenié - !picsoung
Fortune.js 
• JSON API spec 
/people/tO69XnLz7amaYVRq 
{ 
"people": [ 
{ 
"id": "tO69XnLz7amaYVRq", 
"name": "Wall-E", 
"age": 1000, 
"links": { 
"pets": [ 
"dPuXqUJxRIn9cOdH" 
] 
} 
} 
], 
"links": { 
"people.pets": { 
"href": "/pets/{people.pets}", 
"type": "pets" 
} 
} 
} 
HTML5 Conference SF Oct. 2014 Nicolas Grenié - !picsoung
Fortune.js 
• JSON API spec 
/people/tO69XnLz7amaYVRq 
{ 
"people": [ 
{ 
"id": "tO69XnLz7amaYVRq", 
"name": "Wall-E", 
"age": 1000, 
"links": { 
"pets": [ 
"dPuXqUJxRIn9cOdH" 
] 
} 
} 
], 
"links": { 
"people.pets": { 
"href": "/pets/{people.pets}", 
"type": "pets" 
} 
} 
} 
/pets/dPuXqUJxRIn9cOdH 
{ 
"pets": [ 
{ 
"id": "dPuXqUJxRIn9cOdH", 
"name": "Cucaracha", 
"age": 1000, 
"links": { 
"owner": "tO69XnLz7amaYVRq" 
} 
} 
], 
"links": { 
"pets.owner": { 
"href": "/people/{pets.owner}", 
"type": "people" 
} 
} 
} 
HTML5 Conference SF Oct. 2014 Nicolas Grenié - !picsoung
Fortune.js 
adapters for NeDB, MongoDB, MySQL, Postgres, SQLite 
handles all the routing 
handles database interactions 
hooks for specific logic before/after interacting with 
resources 
authentication not implemented (build your own) 
HTML5 Conference SF Oct. 2014 Nicolas Grenié - !picsoung
API-based game: 
APIbunny 
HTML5 Conference SF Oct. 2014 Nicolas Grenié - !picsoung
APIbunny 
{ 
north: { ref: 'cell', inverse: 'south' }, 
east: { ref: 'cell', inverse: 'west' }, 
south: { ref: 'cell', inverse: 'north' }, 
west: { ref: 'cell', inverse: 'east' } 
} 
HTML5 Conference SF Oct. 2014 Nicolas Grenié - !picsoung
APIbunny 
• Inverse relation does not work in case of maze 
{ 
north: { ref: 'cell', inverse: 'south' }, 
east: { ref: 'cell', inverse: 'west' }, 
south: { ref: 'cell', inverse: 'north' }, 
west: { ref: 'cell', inverse: 'east' } 
} 
HTML5 Conference SF Oct. 2014 Nicolas Grenié - !picsoung
APIbunny 
• Inverse relation does not work in case of maze 
{ 
north: { ref: 'cell', inverse: 'south' }, 
east: { ref: 'cell', inverse: 'west' }, 
south: { ref: 'cell', inverse: 'north' }, 
west: { ref: 'cell', inverse: 'east' } 
} 
1 4 7 
2 5 8 
3 6 9 
HTML5 Conference SF Oct. 2014 Nicolas Grenié - !picsoung
APIbunny 
• Inverse relation does not work in case of maze 
{ 
north: { ref: 'cell', inverse: 'south' }, 
east: { ref: 'cell', inverse: 'west' }, 
south: { ref: 'cell', inverse: 'north' }, 
west: { ref: 'cell', inverse: 'east' } 
} 
1 4 7 
2 5 8 
3 6 9 
expected on #4 
{ 
west:1 
east:7 
south:5 
} 
HTML5 Conference SF Oct. 2014 Nicolas Grenié - !picsoung
APIbunny 
• Inverse relation does not work in case of maze 
{ 
north: { ref: 'cell', inverse: 'south' }, 
east: { ref: 'cell', inverse: 'west' }, 
south: { ref: 'cell', inverse: 'north' }, 
west: { ref: 'cell', inverse: 'east' } 
} 
1 4 7 
2 5 8 
3 6 9 
expected on #4 
{ 
west:1 
east:7 
south:5 
} 
east of #1 is #4 -> west of #4 is #1 
but east of #7 is #4 -> west of #4 is #7 
HTML5 Conference SF Oct. 2014 Nicolas Grenié - !picsoung
APIbunny 
• POST requests format is not usual 
{"people":[{"age":1000,"name":"Wall-E"}]} 
HTML5 Conference SF Oct. 2014 Nicolas Grenié - !picsoung
APIbunny 
• thousands players, 44 winners, ~10 code shared 
• Open https://github.com/picsoung/apibunny/ 
• V2 is coming ;) 
HTML5 Conference SF Oct. 2014 Nicolas Grenié - !picsoung
Ressources 
• http://apicodex.3scale.net/ 
• http://amundsen.com/hypermedia/ 
• http://www.designinghypermediaapis.com/ 
• https://www.youtube.com/watch?v=_UG7u7ARTfM 
(APIStrat SF) 
• APIdays and APIstrat conferences 
• API-craft meetup + google group 
HTML5 Conference SF Oct. 2014 Nicolas Grenié - !picsoung
Questions? 
" picsoung !picsoung 
HTML5 Conference SF Oct. 2014 Nicolas Grenié - !picsoung

Building Hypermedia APIs in JavaScript

  • 1.
    Building Hypermedia APIsin Javascript from homemade solution to using Fortune.js
  • 2.
    You? HTML5 ConferenceSF Oct. 2014 Nicolas Grenié - !picsoung
  • 3.
    You? • Whouses APIs? HTML5 Conference SF Oct. 2014 Nicolas Grenié - !picsoung
  • 4.
    You? • Whouses APIs? • Who knows what Hypermedia APIs are? HTML5 Conference SF Oct. 2014 Nicolas Grenié - !picsoung
  • 5.
    You? • Whouses APIs? • Who knows what Hypermedia APIs are? • Who had ever build one? HTML5 Conference SF Oct. 2014 Nicolas Grenié - !picsoung
  • 6.
    Me HTML5 ConferenceSF Oct. 2014 Nicolas Grenié - !picsoung
  • 7.
    Me • 3scaleevangelist HTML5 Conference SF Oct. 2014 Nicolas Grenié - !picsoung
  • 8.
    Me • 3scaleevangelist • API freak HTML5 Conference SF Oct. 2014 Nicolas Grenié - !picsoung
  • 9.
    Me • 3scaleevangelist • API freak • Hacker in Residence HTML5 Conference SF Oct. 2014 Nicolas Grenié - !picsoung
  • 10.
    Me • 3scaleevangelist • API freak • Hacker in Residence • Node.js, Meteor HTML5 Conference SF Oct. 2014 Nicolas Grenié - !picsoung
  • 11.
    Hypermedia 101 HTML5Conference SF Oct. 2014 Nicolas Grenié - !picsoung
  • 12.
    you are alreadyusing Hypermedia APIs HTML5 Conference SF Oct. 2014 Nicolas Grenié - !picsoung
  • 13.
    Core values HTML5Conference SF Oct. 2014 Nicolas Grenié - !picsoung
  • 14.
    Core values •Discoverable API HTML5 Conference SF Oct. 2014 Nicolas Grenié - !picsoung
  • 15.
    Core values •Discoverable API • Machine readable API HTML5 Conference SF Oct. 2014 Nicolas Grenié - !picsoung
  • 16.
    Core values •Discoverable API • Machine readable API • Link-based HTML5 Conference SF Oct. 2014 Nicolas Grenié - !picsoung
  • 17.
    Example: Github GETapi.github.com { current_user_url: "https://api.github.com/user", authorizations_url: "https://api.github.com/authorizations", code_search_url: "https://api.github.com/search/code?q={query} {&page,per_page,sort,order}", emails_url: "https://api.github.com/user/emails", emojis_url: "https://api.github.com/emojis", events_url: "https://api.github.com/events", feeds_url: “https://api.github.com/feeds",ed{/owner}{/repo}", … team_url: "https://api.github.com/teams", user_url: "https://api.github.com/users/{user}", user_organizations_url: "https://api.github.com/user/orgs", user_repositories_url: "https://api.github.com/users/{user}/ repos{?type,page,per_page,sort}", user_search_url: "https://api.github.com/search/users?q={query} {&page,per_page,sort,order}" } HTML5 Conference SF Oct. 2014 Nicolas Grenié - !picsoung
  • 18.
    Example: building RESTapproach /rooms/r123 /doors/d123 HTML5 Conference SF Oct. 2014 Nicolas Grenié - !picsoung
  • 19.
    Example: building Addingrelations /rooms/r123 • /rooms/r123/doors/d123 /doors/d123 • /doors/d123/rooms/r123 HTML5 Conference SF Oct. 2014 Nicolas Grenié - !picsoung
  • 20.
    Example: building Buildingshave hallways /doors/1/rooms/1 /doors/1/hallways/1 /rooms/1/doors/1 /rooms/1/hallways/1 /hallways/1/doors/1 /hallways/1/rooms/1 HTML5 Conference SF Oct. 2014 Nicolas Grenié - !picsoung
  • 21.
    Me… HTML5 ConferenceSF Oct. 2014 Nicolas Grenié - !picsoung
  • 22.
    Me… HTML5 ConferenceSF Oct. 2014 Nicolas Grenié - !picsoung
  • 23.
    Let’s put Hypermediain it Linking ressources • /rooms/1 - rel: door, href: /what/ever/door - rel: door, href: /another/door • /door - rel: entry-to, href: /rooms/1 - rel: entry-to, href: /hallways/1 • /hallways/1 - rel: floor, href: /floors/1 HTML5 Conference SF Oct. 2014 Nicolas Grenié - !picsoung
  • 24.
    HATEOAS Hypermedia asthe Engine of Application State • Basic implementation with Express • add “link” properties to the JSON response HTML5 Conference SF Oct. 2014 Nicolas Grenié - !picsoung
  • 25.
    HATEOAS GET mybuilding.com/rooms/1 { "data": { "name": "Kitchen", "id": "1", "links" : [ { "rel": "door", "href": "https://mybuilding.com/rooms/12345678" }, { "rel": "door", "href": "https://theirbuilding.com/rooms/ 12345678" } ] } } HTML5 Conference SF Oct. 2014 Nicolas Grenié - !picsoung
  • 26.
    There is aframework for that HTML5 Conference SF Oct. 2014 Nicolas Grenié - !picsoung
  • 27.
    Fortune.js HTML5 ConferenceSF Oct. 2014 Nicolas Grenié - !picsoung
  • 28.
    Fortune.js • simpleto setup var fortune = require('fortune') , app = fortune({ db: 'petstore' }) .resource('person', { name: String, age: Number, pets: ['pet'] // "has many" relationship to pets }) .resource('pet', { name: String, age: Number, owner: 'person' // "belongs to" relationship to a person }) .listen(1337); HTML5 Conference SF Oct. 2014 Nicolas Grenié - !picsoung
  • 29.
    Fortune.js HTTP PersonPet Notes GET /people /pets HTML5 Conference SF Oct. 2014 Nicolas Grenié - !picsoung Get a collection of resources, accepts query ? POST /people /pets Create a resource GET /people/:id /pets/:id Get a specific resource, or multiple: 1,2,3 PUT /people/:id /pets/:id Create or update a resource PATCH /people/:id /pets/:id Patch a resource (see RFC 6902) DELETE /people/:id /pets/:id Delete a resource GET /people/:id/pets /pets/:id/owner Get a related resource (one level deep)
  • 30.
    Fortune.js • JSONAPI spec { "people": [ { "id": "tO69XnLz7amaYVRq", "name": "Wall-E", "age": 1000, "links": { "pets": [ "dPuXqUJxRIn9cOdH" ] } } ], "links": { "people.pets": { "href": "/pets/{people.pets}", "type": "pets" } } } HTML5 Conference SF Oct. 2014 Nicolas Grenié - !picsoung
  • 31.
    Fortune.js • JSONAPI spec /people/tO69XnLz7amaYVRq { "people": [ { "id": "tO69XnLz7amaYVRq", "name": "Wall-E", "age": 1000, "links": { "pets": [ "dPuXqUJxRIn9cOdH" ] } } ], "links": { "people.pets": { "href": "/pets/{people.pets}", "type": "pets" } } } HTML5 Conference SF Oct. 2014 Nicolas Grenié - !picsoung
  • 32.
    Fortune.js • JSONAPI spec /people/tO69XnLz7amaYVRq { "people": [ { "id": "tO69XnLz7amaYVRq", "name": "Wall-E", "age": 1000, "links": { "pets": [ "dPuXqUJxRIn9cOdH" ] } } ], "links": { "people.pets": { "href": "/pets/{people.pets}", "type": "pets" } } } /pets/dPuXqUJxRIn9cOdH { "pets": [ { "id": "dPuXqUJxRIn9cOdH", "name": "Cucaracha", "age": 1000, "links": { "owner": "tO69XnLz7amaYVRq" } } ], "links": { "pets.owner": { "href": "/people/{pets.owner}", "type": "people" } } } HTML5 Conference SF Oct. 2014 Nicolas Grenié - !picsoung
  • 33.
    Fortune.js adapters forNeDB, MongoDB, MySQL, Postgres, SQLite handles all the routing handles database interactions hooks for specific logic before/after interacting with resources authentication not implemented (build your own) HTML5 Conference SF Oct. 2014 Nicolas Grenié - !picsoung
  • 34.
    API-based game: APIbunny HTML5 Conference SF Oct. 2014 Nicolas Grenié - !picsoung
  • 35.
    APIbunny { north:{ ref: 'cell', inverse: 'south' }, east: { ref: 'cell', inverse: 'west' }, south: { ref: 'cell', inverse: 'north' }, west: { ref: 'cell', inverse: 'east' } } HTML5 Conference SF Oct. 2014 Nicolas Grenié - !picsoung
  • 36.
    APIbunny • Inverserelation does not work in case of maze { north: { ref: 'cell', inverse: 'south' }, east: { ref: 'cell', inverse: 'west' }, south: { ref: 'cell', inverse: 'north' }, west: { ref: 'cell', inverse: 'east' } } HTML5 Conference SF Oct. 2014 Nicolas Grenié - !picsoung
  • 37.
    APIbunny • Inverserelation does not work in case of maze { north: { ref: 'cell', inverse: 'south' }, east: { ref: 'cell', inverse: 'west' }, south: { ref: 'cell', inverse: 'north' }, west: { ref: 'cell', inverse: 'east' } } 1 4 7 2 5 8 3 6 9 HTML5 Conference SF Oct. 2014 Nicolas Grenié - !picsoung
  • 38.
    APIbunny • Inverserelation does not work in case of maze { north: { ref: 'cell', inverse: 'south' }, east: { ref: 'cell', inverse: 'west' }, south: { ref: 'cell', inverse: 'north' }, west: { ref: 'cell', inverse: 'east' } } 1 4 7 2 5 8 3 6 9 expected on #4 { west:1 east:7 south:5 } HTML5 Conference SF Oct. 2014 Nicolas Grenié - !picsoung
  • 39.
    APIbunny • Inverserelation does not work in case of maze { north: { ref: 'cell', inverse: 'south' }, east: { ref: 'cell', inverse: 'west' }, south: { ref: 'cell', inverse: 'north' }, west: { ref: 'cell', inverse: 'east' } } 1 4 7 2 5 8 3 6 9 expected on #4 { west:1 east:7 south:5 } east of #1 is #4 -> west of #4 is #1 but east of #7 is #4 -> west of #4 is #7 HTML5 Conference SF Oct. 2014 Nicolas Grenié - !picsoung
  • 40.
    APIbunny • POSTrequests format is not usual {"people":[{"age":1000,"name":"Wall-E"}]} HTML5 Conference SF Oct. 2014 Nicolas Grenié - !picsoung
  • 41.
    APIbunny • thousandsplayers, 44 winners, ~10 code shared • Open https://github.com/picsoung/apibunny/ • V2 is coming ;) HTML5 Conference SF Oct. 2014 Nicolas Grenié - !picsoung
  • 42.
    Ressources • http://apicodex.3scale.net/ • http://amundsen.com/hypermedia/ • http://www.designinghypermediaapis.com/ • https://www.youtube.com/watch?v=_UG7u7ARTfM (APIStrat SF) • APIdays and APIstrat conferences • API-craft meetup + google group HTML5 Conference SF Oct. 2014 Nicolas Grenié - !picsoung
  • 43.
    Questions? " picsoung!picsoung HTML5 Conference SF Oct. 2014 Nicolas Grenié - !picsoung