Skip to content

Instantly share code, notes, and snippets.

@skalnik
Created February 14, 2012 23:42
Show Gist options
  • Save skalnik/1831703 to your computer and use it in GitHub Desktop.
Save skalnik/1831703 to your computer and use it in GitHub Desktop.

WTF is this REST thing?

  • REpresentational State Transfer
  • Introduced by Roy Fielding in 2000
  • Lets focus on APIs.
  • Lets talk about the Richardson Maturity Model

Credit: Martin Fowler

Level 0: The Swamp of Pox (Plain ol' XML)

  • You're using HTTP as an RPC (Remote procedure call)
  • It's a start

What meetings exist?

POST /meetingsList

HTTP/1.1 200 OK
<other headers>
[
  {
    'title'        : 'Meeting - Backbone.js',
    'date'         : '02-07-2012',
    'organization' : 'WIT',
    'id'           : 'meeting00'
  }
]

Cool, we need a new one

POST /createMeetingPlease
{
'title'        : 'Weekly Meeting - REST',
'date'         : '02-14-2012',
'organization' : 'WIT'
}

HTTP/1.1 200 OK
<other headers>
{
  'title'        : 'Meeting - REST',
  'date'         : '02-14-2012',
  'organization' : 'WIT',
  'id'           : 'meeting01'
}

And let me check in

POST /checkInToMeeting
{
  'name'      : 'Mike Skalnik',
  'meetingId' : 'meeting01'
}

HTTP/1.1 200 OK
<other headers>
{
  'name'      : 'Mike Skalnik',
  'meetingId' : 'meeting01',
  'id'        : 'checkin01'
}

Level 1: Resources

  • RESTful URIs.
  • Represent objects in your URIs

What meetings exist?

POST /meetings/list

HTTP/1.1 200 OK
<other headers>
[{
  'title'        : 'Meeting - Backbone.js',
  'date'         : '02-07-2012',
  'organization' : 'WIT',
  'id'           : 0
}]

Cool, we need a new one

POST /orgs/WIT/meetings/create
{
  'title' : 'Weekly Meeting - REST',
  'date'  : '02-14-2012'
}

HTTP/1.1 200 OK
<other headers>
{
  'title'        : 'Meeting - REST',
  'date'         : '02-14-2012',
  'organization' : 'WIT',
  id             : 1
}

And lets check in

POST /meetings/meeting01/checkins/create
{
  'name' : 'Mike Skalnik'
}

HTTP/1.1 200 OK
<other headers>
{
  'name' : 'Mike Skalnik',
  'id'   : 1
}

Level 2: HTTP Verbs

  • Use HTTP verbs on those resources
  • GET/POST/PUT/DELETE

What meetings exist?

GET /meetings

HTTP/1.1 200 OK
<other headers>
{
  'title'        : 'Meeting - Backbone.js',
  'date'         : '02-07-2012',
  'organization' : 'WIT',
  'id'           : 0
}

Cool, we need a new one

POST /orgs/WIT/meetings
{
  'title' : 'Weekly Meeting - RSET',
  'date'  : '02-14-2012'
}

HTTP/1.1 201 Created
<other headers>
{
  'title'        : 'Meeting - RSET',
  'date'         : '02-14-2012',
  'organization' : 'WIT',
  'id'           : 1
}

Oh no, I typo'd the title. Lets update it

PUT /meetings/1
{
  'title' : 'Meeting - REST'
}

HTTP/1.1 200 OK
<other headers>
{
  'title'        : 'Meeting - REST',
  'date'         : '02-14-2012',
  'organization' : 'WIT',
  'id'           : 1
}

Eh, lets just delete

DELETE /meetings/1

HTTP/1.1 200 OK
<other headers>

Level 3: Hypermedia Controls

Content Negotiation

I want JSON!

GET /meetings
Accept: application/json

HTTP/1.1 200 OK
Content-Type: application/json
[
  {
    'title' : 'Weekly Meeting - Backbone.js'
    'id'    : 0
  }
]

Actually, XML!

GET /meetings
Accept: application/xml

HTTP/1.1 200 OK
Content-Type: application/xml
<meetings>
  <meeting id="0">
    <title>Weekly Meeting - Backbone.js</title>
  </meeting>
</meetings>

Custom MIME Types

  • This is pretty damn cool. You can use custom MIME types to version our APIs
  • People normally start shoving versions in their URL, not so RESTful

API V1 in JSON:

GET /meetings
Accept: application/vnc.ccorgs-v1+json

V2 in JSON:

GET /meetings
Accept: application/vnc.ccorgs-v2+json

V1 in XML:

GET /meetings
Accept: application/vnc.ccorgs-v1+xml

Hypertext As The Engine Of Application State - HATEOAS

  • My interest recently
  • APIs serve links, like the links you click on
  • REST is all about state. Your web app is a state machine!
  • Hypertext being XML, but you can do this with the HTTP Link header for non-hypertext.

Tell me about the first meeting

GET /meetings/0
Accept: application/json

HTTP/1.1 200 OK
Content-Type: application/json
Link: </meetings>; rel="up", </meetings/0/checkins>; rel="checkins", \
</meetings?page=2>; rel="next", </meetings?page=100>; rel="last"
<other headers>
  [
    {
      'title'        : 'Meeting - Backbone.js',
      'date'         : '02-07-2012',
      'organization' : 'WIT',
      'id'           : 0
    }
  ]

Cool, lets make a new meeting

POST /meetings
{
  'title' : 'Weekly Meeting - REST',
  'date'  : '02-14-2012'
}

HTTP/1.1 201 Created
Content-Type: application/json
Location: /meetings/1
Link: </meetings/1/checkins>; rel="checkins"
<other headers>
{
  'title'        : 'Meeting - RSET',
  'date'         : '02-14-2012',
  'organization' : 'WIT',
  'id'           : 1
}

Lets check if anyone has checked in yet. I'll just follow that link.

GET /meetings/1/checkins

HTTP/1.1 200 OK
Link: </meetings/1>; rel="up"
<other headers>
[
  {
    'name'      : 'Mike Skalnik',
    'timestamp' : '1329262247'
  },
  {
    'name'      : 'Ankit Shankar'
    'timestamp' : '1329262534'
  }
]

http://blog.kevburnsjr.com/self-descriptive-hypermedia-in-riak is cool. I can't wait for http://getsomere.st/

@skalnik
Copy link
Author

skalnik commented Feb 22, 2012

Once you're messing with HATEOAS, you shouldn't be messing with URLs anymore. You should just send your HTTP requests to the links sent in the server response (either in the body if its hypertext, or in the Link header). However, we still want to be able to support multiple content types, so we need to send it via the Accept header. Does that make sense?

@daveworth
Copy link

Totally... my question actually started in the Level 3, content negotiation section, before HATEOAS and I didn't think about it the context of the latter. Nice.

@skalnik
Copy link
Author

skalnik commented Feb 22, 2012

Level 3 has two parts, content negotiation and HATEOAS. HATEOAS is can be tricky to implement if content negotiation hasn't been done and you want to support multiple content types :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment