Senior Engineer - Acquia Octo
JSON API co-maintainer
HTTP/2 enthusiast
Specification-junkie
Loses lots of sleep over API design
It's the Hypertext Transfer Protocol
and that's a protocol layer sitting atop the TCP/IP network protocols
And there are many such protocols.
SMTP, POP & IMAP
SSH
FTP
IRC
& XMPP
SOAP
HTTP is an application protocol for distributed, collaborative, and hypermedia information systems.
Almost always a URL
Which is simply a unique identifier that can be "located"
http://example.com/api/user/1
"http:" "//" host [ ":" port ] [ abs_path [ "?" query ]]
Describe "actions" on a resource
HEAD & OPTIONS are about information discovery
GET - Show me the identified resource
POST - Add this resource
PATCH - Update the identified resource with this information
PUT - Replace the identified resource with this one
DELETE - Remove the identified resource
HEAD - Is there a resource with this identifier?
OPTIONS - What actions can I take on a resource?
Categorize communication into conversational buckets
200 - Understood
300 - Maybe you meant X?
400 - Sorry, I don't understand
500 - It's not you, it's me
Contains a start line (URI + Method/Status), headers & body
Headers describe the resource
Content-Type: text/html <html> <title>Gabe Sullice</title> <body>stout</body> </html>
Content-Type: application/json { "type": "user", "id": 1, "attributes": { "name": "gabesullice" } }
GET www.example.com/user/1 HTTP/1.1
HTTP/1.1 200 OK
Content-Type: application/json { "type": "user", "id": 1, "attributes": { "name": "gabe" } }
REST is an architectural style that defines a set of constraints and properties based on HTTP.
A separation of concerns
Client handles presentation
Server handles data storage
Foundational principle for "decoupled" sites
Every request/response is independent
A response should contain information to govern its own intermediate storage
Its purpose is to eliminate requests/responses cycles or to reduce the time it takes for that cycle to complete (latency)
The system should be indifferent to proxies, load balancers, CDNs, etc.
Optional.
Useful when a client does not have the know-how on how to process a resource. It sends a request to a remote server for the code representing that know-how, receives that code, and executes it locally.
Entity representations are decoupled from their storage.
Hypermedia is the engine of application state.
I'm a teapot, short and stout
<a href="https://httpstatus.es/218">I'm a teapot</a>
{ "type": "joke", "id": 1,
"attributes": {
"content": "I'm a teapot"
},
"links": {
"explanation": "https://httpstatus.es/218"
}
}
A link is soo much more than that clickable blue text.
It takes the form:
"{this URI} has a {relation type} resource at {that URI}, which has {target attributes}".
A link relation type identifies the semantics of a link.
Example:
<a rel="help" href="http://foo.com/guide">
<a rel="my-cart" href="http://foo.com/cart/4">
Hints information about the linked-to resource.
Example:
<a rel="help" hreflang="en, fr, es" href="http://foo.com/guide">
<a rel="my-cart" title="Checkout" href="http://foo.com/cart/4">
index.html
index.html
hero.jpg
index.html
style.css
hero.jpg
index.html
style.css
url('https://www.example.com/assets/separator.svg');
hero.jpg
index.html
style.css
url('https://www.example.com/assets/hipster-heavy.ttf');
url('https://www.example.com/assets/separator.svg');
hero.jpg
/home
/posts
/posts/2
/posts/1
/about
/home
/posts
/posts/2
/posts/1
/about
/posts
/posts/2
/posts/2/tags
/posts/2/author
/posts/1
/posts
/posts/2
/posts/2/tags
/posts/2/author
/posts/1
/posts/2
/posts/2/author
/posts/2/tags
/tags/2
/tags/1
/api/content
/api/schemas/content
/api/content?page=2
/api/posts/1/comments
/api/content
/api/schemas/content
/api/content?page=2
/api/content?page=3
/api/posts/2/comments
/api/posts/1/comments
/api/content
/api/schemas/content
/api/content?page=2
/api/content?page=3
/api/content?page=4
/api/posts/2/comments
/api/posts/1/comments
Hypermedia As The Engine Of Application State
is like
for your APIs
{
"data": [...],
"links": {
"next": "https://foo.com/collection?page=1",
"last": "https://foo.com/collection?page=10"
},
}
{
"data": [...],
"links": {
"self": "https://foo.com/birds",
"family#0": {
"href": "https://foo.com/birds/paradisaeidae",
"meta": {
"title": "Birds of Paradise"
}
},
"family#1": {
"href": "https://foo.com/birds/corvidae",
"meta": {
"title": "Crows, Ravens & Jays"
}
}
}
}
{
"data": [...],
"links": {
"self": "https://foo.com/shirt/1",
"product-variant#0": {
"href": "https://foo.com/shirt/1/a",
"color": "#0033ff",
"title": "Marine"
},
"product-variant#1": {
"href": "https://foo.com/shirt/1/b",
"color": "#ee0000",
"title": "Ruby"
}
}
}
{
"data": [{
"type": "post",
"id": 1,
"attributes": {...},
"links": {
"self": "http://foo.com/post/1?resource_version=id:27",
"latest-version": "http://foo.com/post/1?resource_version=id:29"
}
}, {
"type": "post",
"id": 2,
"attributes": {...},
"links": {
"self": "http://foo.com/post/21?resource_version=id:13",
"latest-version": "http://foo.com/post/2?resource_version=id:13"
}
}]
}
{
"data": [{
"type": "post",
"id": 1,
"attributes": {...},
"links": {
"self": "http://foo.com/post/1",
"operation#0": {
"href": "http://foo.com/post/1",
"meta": {
title: "Edit",
rel: "edit"
}
},
"operation#1": {
"href": "http://foo.com/post/1",
"meta": {
title: "Delete",
rel: "drupal://link-relation/jsonapi/remove"
}
}
}
}]
}
{
"data": [{
"type": "product",
"id": 1,
"attributes": {...},
"links": {
"self": "http://foo.com/product/1",
"cta": {
"href": "http://foo.com/cart/4",
"meta": {
title: "Add to Cart",
rel: "drupal://link-relation/jsonapi/relate",
template: {
"method": "POST",
"body": "{\"type\": \"product\", \"id\": 1}"
}
}
}
}
}]
}
{
"data": [{
"type": "product",
"id": 1,
"attributes": {...},
"links": {
"self": "http://foo.com/product/1",
"cta": {
"href": "http://foo.com/wishlist/5",
"meta": {
title: "Add to Wishlist",
rel: "drupal://link-relation/jsonapi/relate",
template: {
"method": "POST",
"body": "{\"type\": \"product\", \"id\": 1}"
}
}
}
}
}]
}