Skip to main content

Links representations together.

Project description

“REST APIs must be hypertext-driven”, Roy T. Fielding

Version Downloads Status License

Wait… Why?

The “Why” is pretty simple… there are an abundance of frameworks that make things RESTful by generating deterministic URLs for your database objects. Then you implement the popular HTTP methods to provide a nice way to interact with your newly minted resources. Congratulations, you have successfully mapped SQL into HTTP. The URL has become your WHERE clause and PUT, POST, and GET are synonyms for UPDATE, INSERT and SELECT.

So that is a very tongue-in-cheek answer for why did I feel the need to write this library. However it is not a great answer nor even an acceptable one in my opinion. The why is much more nuanced than that. The implementation that I just ranted about may indeed be quite RESTful but we need to know more about a solution than its URL structure and how it interprets various protocol actions before we can make that decision. This isn’t a dissertation on the Representation State Transfer architectural principles, I leave that to Dr. Fielding. This library attempts to provide a few mechanisms to make developing RESTful protocols simpler.


This library offers functionality that simplifies writing RESTful service implementations over an existing HTTP server stack such as Flask or Tornado. The HTTP stacks provide very clean and usable ways to construct URLs and route HTTP requests to specific chunks of code. I’m not going to implement that again. Instead, this library provides ways to embed hypermedia controls into your responses without introducing lots of nasty coupling in your application code.

This library is essentially an on-ramp to supporting Hypermedia Controls in your HTTP application. Hypermedia Controls referring to what is known as Level 3 of the Richardson Maturity Model. This model was described by Leonard Richardson at QCon in 2008 and has been further examined in Jim Webber’s most excellent REST in Practice. Here is a brief recap:

Level Zero

One URL, one HTTP method - POST. Document describes the function to invoke, parameters, etc. Response is the return value.

“There’s a little web-based peephole into some other universe, and you can only communicate wit the other universe by passing messages through the peephole.” - L. Richardson.

Level One: Resources

URLs identify resources but interactions are limited to sending a message that describes the function to invoke, parameters, etc. The interactions with different resources instances usually depend on URL patterns.

Level Two: HTTP

Resources are still identified by constructed URLs but interactions follow the rules of HTTP with respect to its verbs (methods). This is where most RESTful APIs stop.

Level Three: Hypermedia Controls

This is where the seldom understood and inpronouncable term HATEOAS shows up. Instead of the URL being formulated by the user of the service based on what they want to do and some URL pattern syntax, the available actions are represented directly in the document as named links. See REST APIs must by hypertext-driven for a well-written and relatively short rationale.

That is the part of the story that this library attempts to fill. It lets you write code like:

from hypermedia.tornado import mixins
from tornado import web

class PersonHandler(mixins.Linker, web.RequestHandler):

   def get(self, uid):
      person = get_person_information(uid)
      self.add_link('related-shows', SearchHandler, 'GET',
                    person_id=uid, type='shows')
      self.add_link('related-movies', SearchHandler, 'GET',
                    person_id=uid, type='movies')
      self.add_link('add-comment', CommentHandler, 'POST', uid=uid)
      self.add_link('comments', CommentHandler, 'GET', uid=uid)

      response = {}
      # ... build out the response
      response['links'] = self.get_link_map()


class SearchHandler(web.RequestHandler):

   def get(self):
      # processes query parameters

class CommentHandler(web.RequestHandler):

   def get(self, uid):
      # retrieve comments

   def post(self, uid):
      # add a comment

The hypermedia.tornado.mixins.Linker class takes care of constructing the appropriate links based on the registered handlers and makes them available via the get_link_map() method.

Ok… Where?






Project details

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page