Positive Incline Mike Burrows (@asplake) moving on up, positively

February 18, 2010

PathTo for Python gets a JSON-capable HTTP client

Filed under: Web Integration — Tags: , , , , , , , , — Mike @ 1:55 pm

…and it works!

>>> import path_to
>>> app = path_to.open_app('http://example.com', format='json')
>>> app.login.post(credentials, expected_status=302)
>>> print app.products.resource_template
products      products     GET, POST http://example.com/products{.format}
  new_product new_product  GET       http://example.com/products/new{.format}
  {product}   product      GET, PUT  http://example.com/products/{product}{.format}
    edit      edit_product GET       http://example.com/products/{product}/edit{.format}
>>> product = app.products['Foo'].get(expected_status=200).parsed
>>> product['description'] = 'Updated!'
>>> app.products['Foo'].put(product, expected_status=302)
>>> app.products['Foo'].get(expected_status=200).parsed['description']
u'Updated!'

OK, so it wasn’t really “example.com”, and the updated product wasn’t called “Foo”, but the rest is for real.

In stages:

0) The Pylons-based server has the JSON-capable @validate (un)decorator of my previous post and DescribedRoutesMiddleware from DescribedRoutes installed in its wsgi stack.

1) Create a client-side proxy to the app. Following link headers published by the app, it finds the ResourceTemplates description, which it retrieves in JSON format.

>>> import path_to
>>> app = path_to.open_app('http://example.com', format='json')

The format='json' is a small red herring here, it’s simply remembered for later.

2) Log in by posting credentials, expecting a 302 status (a failed attempt would return a 200 and the validation errors in the body). The client handles cookies automatically behind the scenes.

>>> app.login.post(credentials, expected_status=302)

3) View a friendly representation of the metadata (or rather the part that relates to products):

>>> print app.products.resource_template
products      products     GET, POST http://example.com/products{.format}
  new_product new_product  GET       http://example.com/products/new{.format}
  {product}   product      GET, PUT  http://example.com/products/{product}{.format}
    edit      edit_product GET       http://example.com/products/{product}/edit{.format}

4) Get a product identified by ‘Foo’ and return the JSON payload parsed into a Python dict.:

>>> product = app.products['Foo'].get(expected_status=200).parsed

Behind the scenes it has expanded the “http://example.com/products/{product}{.format}” template seen previously into “http://example.com/products/Foo.json”, using the remembered format parameter and the supplied ‘Foo’ key which is assumed to correspond to the required product parameter.

5) Update the local product representation and send it back (it gets converted back to JSON along the way):

>>> product['description'] = 'Updated!'
>>> app.products['Foo'].put(product, expected_status=302)

6) Finally, demonstrate that it was successful!

>>> app.products['Foo'].get(expected_status=200).parsed['description']
u'Updated!'

February 10, 2010

Experimental {.format} in Routes

Filed under: Web Integration — Tags: , , , — Mike @ 7:34 pm

A while back I raised on the URI Template mailing list the idea of a {.var} syntax that generates optional path extensions, “.html”, “.json”, etc.

For example, given a path template of /releases/{id}/notes{.format} and an id parameter of “1”, paths of /releases/1/notes or /releases/1/notes.pdf will be generated depending on whether the supplied format parameters are undefined or “pdf” respectively.

It’s not a completely original idea.  Rails has a (.:format) syntax that DescribedRoutes maps to the flexible but somewhat clunky {-prefix|.|format} syntax of the old draft URI Template spec. The proposed syntax seems more in keeping with current draft (very much a work in progress) though.

Today my Routes fork gained support for the syntax and tweaks to the new SubMapper helpers so that duplicate “formatted routes” aren’t generated (something that happened to Rails a long time ago).

Example config (<code>config/routing.py</code> in your Pylons app):

with mapper.collection(
                'releases',
                'release',
                requirements={'id': 'd+'}) as c:
    c.member.link(rel='notes', name='release_notes')

And in the paster shell:

>>> print mapper
Route name     Methods Path
releases       GET     /releases{.format}
create_release POST    /releases{.format}
new_release    GET     /releases/new{.format}
release        GET     /releases/{id}{.format}
update_release PUT     /releases/{id}{.format}
delete_release DELETE  /releases/{id}{.format}
edit_release   GET     /releases/{id}/edit{.format}
release_notes  GET     /releases/{id}/notes{.format}
>>> url('release_notes', id=1, format='pdf')
'/releases/1/notes.pdf'
>>> url('release_notes', id=1)
'/releases/1/notes'
>>> mapper.match('/releases/1/notes')
{'action': u'notes', 'controller': u'release', 'id': u'1', 'format': None}
>>> mapper.match('/releases/1/notes.pdf')
{'action': u'notes', 'controller': u'release', 'id': u'1', 'format': u'pdf'}

Just add formatted=False to your collection() call if you don’t want this new behaviour.

I haven’t sent the Routes guys a pull request yet. I’d like some feedback first, and meanwhile I will try to come up with a nice helper that takes the format extension to override content negotiation. My app has some basic header-based conneg already but I would like the convenience of being able to override it in the URL.

June 30, 2009

Bootstrapping REST

Filed under: Web Integration — Tags: , , , , , , — Mike @ 6:55 pm

In this article I’m approaching REST from the perspective of client/server interaction, most especially (and slightly unusually I think) on the needs of the client and you, the client’s developer. I hope you find it helpful.

A caveat (no apology): my interest is in structure and navigation. HTTP has mechanisms already for clients and servers to negotiate content types and I’ve made a conscious design decision to rely on their existence. In other words, I ignore it completely here. Orthogonality is a wonderful thing!

We start at the beginning:

The completely hand-crafted client

At the most extreme, you’re working against an undocumented, unsupported server API that lacks regular structure. Any complexity implies significant reverse-engineering effort, made worse if you’re tracking a moving target. If you’re lucky though, you’ll be working against a supported and stable API.

Relying on regularity

Servers built using frameworks such as Rails (and even many that aren’t) will tend to exhibit some predictable patterns. The details may be framework-specific, but a collection resource, say “users”, can be expected to support these (or similar) operations and subresources:

GET, POST         http://www.example.com/users
GET               http://www.example.com/users/new
GET, PUT, DELETE  http://www.example.com/users/{user_id}
GET               http://www.example.com/users/{user_id}/edit

and a nested collection resource (each user’s articles, say) can be expected to follow a similar structure, rooted on a specific user resource.

GET, POST         http://www.example.com/users/{user_id}/articles
GET               http://www.example.com/users/{user_id}/articles/new
GET, PUT, DELETE  http://www.example.com/users/{user_id}/articles/{article_id}
GET               http://www.example.com/users/{user_id}/articles/{article_id}/edit

Armed therefore with just the knowledge of the user and articles collections, your client’s internal object model might easily support expressions such as

app.users
app.users.new
app.users[user_id]
app.users[user_id].edit
app.users[user_id].articles
app.users[user_id].articles.new
app.users[user_id].articles[article_id]
app.users[user_id].articles[article_id].edit

You’ll pay a price when the application departs from its usual pattern, but most of the time you’ll be fine. In fact for many client applications the edit and new are irrelevant, so all you need is the basic hierarchical structure and knowledge of the representations you can GET, PUT, POST there, also how to DELETE things.

Model-driven development

One approach to speeding application development is to abstract out that object model and generate skeleton code for both client and server from some common, logical representation. Not only are you spared the task of building the object model, but (given the right toolset) you can expect to have the web client functionality baked in for you. Most importantly, it’s much cheaper to track any changes made on the server.

The downside? You are now tied to a toolset. That might be a price worth paying if you’re in control of both client and server (for an internal application, say), but for many this will be unpalatable.

The no-model option

[Stick with me – this apparent digression makes metadata-driven clients easier to introduce!]

Dynamic languages make it possible to support expression like

app.users[user_id].articles.new

even if your app object doesn’t actually have a users member (method, attribute or property) and articles and new exist nowhere either. See [1] and [2] to read how (generally) this can be done in Ruby and Python, and [3] for a description of path-to [4], an actual client that can work this way.

Even without these dynamic features, a similar effect can be achieved with operator overloading. This, for example, is perfectly valid Java:

app / users / member(user_id) / articles / _new

The objects returned by such expressions can easily have a URI representation, request methods and so on.

But beware! Suppose a user resource defines an “up” link. Take for example

app.users["dojo"].up

To which of these URIs (below) does this expression refer?

http://www.example.com/users/dojo/up
http://www.example.com/users

We’ll come back to this issue later.

Metadata-driven clients

You can take the no-model approach and validate each navigation against it, so that for a non-existent path foo,

app.foo

raises an error just as the model-driven version would have done. To your client application code, this implied object model looks no different to the generated one, even though (in a way) it doesn’t really exist! I like to think of this as the model-driven approach turned inside-out – instead of running code generated from a model, we run code that interprets a model.

If that metadata comes from the server (so much better than a build-time configuration step), you’re getting closer to that RESTful goal of application interaction driven by links, or

Hypertext as the engine of application state (HATEOAS) [5]

But before claiming this goal genuinely you will need to address a couple of issues that we’ve glossed over so far:

  1. how to generate URIs for collection members, for example turning users["dojo"] into http://example.com/users/dojo
  2. how to generate URIs for navigations like up and first

The cleanest answer to the first issue is the URI Template (a parameterised URI with a standardised syntax), sourced (naturally) from the server. With Fielding’s quote in mind it’s clear that the resolution to the second issue must lie also in server-provided information. In fact we’ll see both issues come together as we understand that the problem really isn’t one of describing URI structure but instead one of describing resource relationships and their manifestations in links.

URI Templates and Resource Templates

A URI Template that maps users["dojo"] into http://example.com/users/dojo might look this this:

http://example.com/users/{user_id}

The syntax isn’t that important (the draft standard looks set to change significantly), and yes, we’ve seen them here already without introduction. They’re pretty self-documenting and (with a good library implementation) easy to use. If you stick to a standard there’s nothing unRESTful about them – having your application create a URI from template is really no different from a browser creating a URI as the result of GET-based HTML form.

In more complex cases – in particular where there is a mixture of mandatory and optional parameters – it’s advantageous to wrap the URI Template in some basic metadata. WADL [6] is a well-supported example of this idea (well almost – see the comments [7]) ; another is my own Resource Template format [8] (actually more a logical schema than a format since it’s available in JSON, YAML and XML).

WADL and Resource Templates typically describe an entire application, each element describing a class of resource, its relationships, its URI template (so that relationships can be turned into links), parameters, supported HTTP methods and so on.

Integrated with Rails, Resource Templates are generated by the server at run-time from the application’s routes, information already available. Although (most typically) they can be generated and consumed for the whole application in one go, they can be generated for a single resource already pre-populated with that specific resource’s known parameters.

At last: links!

From those pre-populated, resource-specific Resource Templates we can generate links – as HTML or XML elements or as HTTP headers – that our client can consume and navigate, resource by self-described resource. Every resource becomes a potential entry point to a virtual application that can easily span multiple servers. Finally, we got there: true hyperlink-based client/server interaction, and where the framework support exists, for very little effort too.

Discussion

In this article I have described the journey I travelled myself over a period of weeks following my decision to expose a complex existing application to scripting via HTTP. For me, technology-neutrality was a determining principle, and selecting HTTP and REST was an easy decision, first to make and then to sell. A proof of concept and a good demo based on a hand-crafted object model came quickly; metadata and templates were the result of some serious refactoring. Resource Templates came after I left the project, as did Rails integration (not a project requirement) and the path-to client in both Ruby and Python.

At the time of writing, link generation is supported on the server side but not yet on the client. The purist in me likes the fact that it is theoretically possible and will strive to keep it that way; the engineer in me is for the moment content with slurping up a whole application’s worth of metadata in one go and thereafter restricting interactions and payloads to the application level. Even as a purist, I see elegance in a simple metadata format, avoidance of hand-coded generation of URIs, respect for the power of HTTP and the orthogonality of formats; and if you believe as I do that RESTfulness lies in how clients and servers interact and not just in how server resources support the HTTP methods, I’ve probably succeeded in taking it further than most.

References

  1. Dynamically extending object behaviour in Ruby and Python [positiveincline.com]
  2. Programmatically adding methods to classes and objects: more Ruby/Python comparisons [positiveincline.com]
  3. path-to is born: nice client-side APIs to web applications in Ruby, also articles tagged path-to [positiveincline.com]
  4. path-to (Ruby) and path-to-py (Python) [github.com]
  5. Representational State Transfer (REST) (Chapter 5 of Architectural Styles and the Design of Network-based Software Architectures) [www.ics.uci.edu]
  6. Web Application Description Language [wikipedia.com]
  7. Partial template expansion in described_routes (comments)” [positiveincline.com]
  8. described_routes: hierarchical, framework-neutral and machine-readable descriptions of Rails routes [positiveincline.com], described_routes (Ruby) and described_routes-py (Python) [github.com]

June 25, 2009

Resolutions

Filed under: Web Integration — Tags: , , , , — Mike @ 1:49 pm

In my previous post I was casting around for an alternative to type as the name of a link header attribute, since in the standard this refers to a content type, not the “logical” type of the resource pointed to by the link.

I have decided to go with role, borrowed from XLink. I know that we’re not dealing with XML here and XLink isn’t wildly popular, but it’s documented, accepted and understood, so why reinvent the wheel?

Consistent with both XLink and the existing rel attributes, I’ve made these role attributes contain URIs, as in this example:

Link: <http://example.com/users/dojo>; rel="self"; role="http://example.com/described_routes#user",
           <http://example.com/described_routes/user>; rel="describedby"; role="http://example.com/described_routes#described_route",
           <http://example.com/described_routes/user?user_id=dojo>; rel="describedby"; role="http://example.com/described_routes#ResourceTemplate",
           <http://example.com/users>; rel="up"; role="http://example.com/described_routes#users",
           <http://example.com/users/dojo/edit>; rel="edit"; rel="http://example.com/described_routes/user#edit"; role="http://example.com/described_routes#edit_user",
           <http://example.com/users/dojo/articles>; rel="http://example.com/described_routes/user#articles"; role="http://example.com/described_routes#user_articles"

You can think of this in terms of entities and relationships in the ER model:

  role="http://example.com/described_routes#user_articles"

identifies a target entity (“user_articles” in this example, named after its Rails route) within the overall schema, and

  rel="http://example.com/described_routes/user#articles"

identifies a relationship (“articles”) within the description of the source entity (“user”).

These URIs are globally unique, and even if they are never dereferenced by the client (i.e. the client isn’t interested in the ResourceTemplate metadata there – shame on them!), these values can be used by clients to select links reliably.

Postscript

One of my sources of both good practice and inspiration has been Tim Berners-Lee’s Design Issues. I hadn’t got to the Metadata Architecture article until after drafting the above, and it’s quite gratifying to read it now.  Tim makes the ER comparison too, but rather than rehash that I will quote some of his key principles:

Metadata about one document can occur within the document, or within a separate document, or it may be transferred accompanying the document.

Link headers and link elements pointing to ResourceTemplate documents exemplify all three possibilities.

The URL space is an appropriate space for the definition of attribute names

Not only appropriate, but a lot more powerful than arbitrary tokens.  My initial plan to use simple names for rel and type attributes were definitely misguided.

As much as possible of the syntax and semantics should be able to be acquired by reference from a metadata document.

Within the scope of defining navigations around a web application I think this is achieved successfully.  A lot can be done by the client even without the metadata document, though clients will still need help in constructing URIs for members of collections.  I think there would a be place for the ResourceTemplate format (or something very much like it) even if there was a standard for URI Templates in link headers, but that’s a whole new article, perhaps my next one.

June 23, 2009

Link header dilemmas

Filed under: Web Integration — Tags: , , , , — Mike @ 12:07 pm

I received a very reasonable answer to my question (thank you Phil), but as a result of trawling through 12 months of archive came to realise that I’d misunderstood the guidance (or rather the relative lack thereof) regarding the use of the type link attribute.  The spec cites RFC 4287 The Atom Syndication Format and it appears that I have abused it by populating it with resource template names (sourced from Rails route names).  My fault entirely, lesson learned.

I’ll need to choose a more appropriate name for it therefore – either that or remove it altogether!  Possibilities:

  • name?  I quite like this, but is it too generic?
  • resourcename? Is this any better than name?
  • routename? Accurate when driven from Rails, but otherwise too implementation-specific I think
  • linkname? I quite link this too, but why not call the linkname on a link just name?
  • target?  Already taken, or at least too reminiscent of the target frame/window attribute.  Perhaps I’m on a better track here though? targettype maybe?

Aarrgghh!  We know that naming is hard; naming a name is doubly so.  I’ll sit on this for a while and hope that either inspiration or feedback comes…

June 19, 2009

Link headers, link elements, and REST

Filed under: Web Integration — Tags: , , , , , — Mike @ 3:14 pm

The house move looms (still no date yet, frustratingly) so I’ve had less time for programming (or blogging for that matter!).  I do however have an experimental and unreleased enhancement to the Ruby version of described_routes that generates link elements (for the <head> section of your html) or the yet-to-be-standardized link headers completely automatically.  Integrating them into your Rails application requires just a one-liner per layout or controller (or you can do all controllers in one go).  Drop me a line if you would like to play with it.

I have one question outstanding on the new spec (see it here on the ietf-http-wg list archives) but I have to say that I’m pretty happy with it all.  It takes the ideas of Partial template expansion in described_routes and adds a standardised way to relate content and metadata, making it even easier clients to navigate web applications without necessarily slurping up the entire site’s schema in one go.  It allows interaction to be 100% RESTful (HATEOAS and everything), and even if you prefer not to go that way as a client you can at least be confident that the application will be transparent, self-documenting and well-behaved.

June 4, 2009

Alternative URI Templates implementation in Python; fun with decorators

Filed under: Programming,Web Integration — Tags: , , — Mike @ 8:20 am

I’ve added uri_templates.py to the described_routes-py repository.  Feature-wise, it differs from Joe’s in that it supports a partial expansion mode (see this post for motivation).  Implementation-wise, it’s all done with regexps – no special parsers required.

There are a couple of missing features (I own up to them in the docstring) but they probably won’t get fixed. Not only do I not need them, but we’re expecting the spec to undergo some some significant change soon and it would probably be wasted effort.

Fun with decorators

For a bit of Python newbie fun, I used a decorator to populate the operator dispatch table:

def operator(name):
    def save_operator(func):
        operators[name] = func
        return func
    return save_operator

@operator('opt')
def op_opt(arg, variables, params, encoding, partial):
    etc

They then get called by:

    operators[operator](arg, variables, params, encoding, partial)

Easy! Perhaps this is overkill, but it seems nicer than techniques like calling functions via constructed function names and it might allow new operators to be added from outside the module.

This must be the simplest example of parameterised decorators you’re ever likely to find as there’s no function wrapping involved. The one thing to understand is that operator('opt') returns a new function that “remembers” the name ‘opt’.

May 29, 2009

New on github: described_routes-py

Filed under: Web Integration — Tags: , , , , — Mike @ 6:51 pm

described_routes-py – a Python port of the core metadata classes ResourceTemplate and ResourceTemplates.  It’s been fun learning Python and I’m pleased with the result – it looks cleaner somehow and I’m seriously considering treating this as the reference version.

I need to add support for partial template expansion to Joe Gregorio’s URI Template parser (to be found here) and add some industrial-strength tests. I’m putting off XML support (perhaps indefinitely), and coming round to the conclusion that I needn’t add explicit JSON and YAML support as it’s trivially easy – using your favourite JSON or YAML module – to go to or from the hash and dict representations already supported.   So that’s the core of it done really.

That leaves client and server integration. Unless I’m deluding myself, I think my Python is up to a port of the path-to client, though it will need some different metaprogramming tricks to the Ruby version. On the server side, I have the challenge of learning Django without (still!) knowing whether its routing datastructures are open to the same treatment I gave to Rails. I guess it will be fun trying! Worst case would be some hand crafting, not too terrible.

May 20, 2009

Latest gems, URI Template update

Filed under: Web Integration — Tags: , , , — Mike @ 3:39 pm

Bob Aman (sporkmonger) has released the latest version of the excellent addressable to RubyForge so that it’s no longer necessary to get it from github.  This incorporates amongst other things (my minor bug fixes included) Bob’s cool new partial URI Template expansion feature that is the foundation of the changes described in my previous post.  I’ve updated both described_routes and path-to so that installing either of these will pull in addressable automatically.

So:

$ sudo gem install described_routes path-to
Successfully installed addressable-2.1.0
Successfully installed described_routes-0.4.1
Successfully installed path-to-0.4.1
3 gems installed

and you’re done.

Meanwhile, the uri@w3.org list awoke this week in response to a request from Joe Gregorio for examples of real-world usage of URI Templates and feedback on whether simplification was warranted.  Bob and I were united in asking that the advance features not be withdrawn – they are genuinely useful!

May 13, 2009

Partial template expansion in described_routes

Filed under: Web Integration — Tags: , , , , — Mike @ 9:36 pm

In between a trip to the local dump, another meeting with HR (role officially redundant now, though still on the payroll for a few weeks) and getting our signatures witnessed for our house sale, I added partial template expansion to described_routes today.  This is potentially a lot more important than it sounds, but before I go into that, an explanation of the functionality.

What it does

Any query parameters passed to the included Rails controller will be used to pre-populate the generated resource templates and their URI templates.  In this example, the <code>article_id</code> and <code>format</code> parameters have been replaced with actual values, leaving the <code>article_id</code> unchanged:

$ curl "http://localhost:3000/described_routes/user.text?format=json&user_id=dojo"
user                 user                 GET, PUT, DELETE       http://localhost:3000/users/dojo.json
  edit               edit_user            GET                    http://localhost:3000/users/dojo/edit.json
  articles           user_articles        GET, POST              http://localhost:3000/users/dojo/articles.json
    new_user_article new_user_article     GET                    http://localhost:3000/users/dojo/articles/new.json
    recent           recent_user_articles GET                    http://localhost:3000/users/dojo/articles/recent.json
    {article_id}     user_article         GET, PUT, DELETE       http://localhost:3000/users/dojo/articles/{article_id}.json
      edit           edit_user_article    GET                    http://localhost:3000/users/dojo/articles/{article_id}/edit.json
  profile            user_profile         GET, PUT, DELETE, POST http://localhost:3000/users/dojo/profile.json
    edit             edit_user_profile    GET                    http://localhost:3000/users/dojo/profile/edit.json
    new              new_user_profile     GET                    http://localhost:3000/users/dojo/profile/new.json

More typically, JSON, YAML or XML format would be requested.  Here’s a JSON example (after pretty printing – the original output was all on one line):

$ curl "http://localhost:3000/described_routes/user_articles.yaml?user_id=dojo&format=json"
{
   "name":"user_articles",
   "rel":"articles",
   "path_template":"/users/dojo/articles.json",
   "uri_template":"http://localhost:3000/users/dojo/articles.json",
   "options":["GET", "POST"],
   "resource_templates":[
      {
         "name":"new_user_article",
         "options":["GET"],
         "path_template":"/users/dojo/articles/new.json",
         "uri_template":"http://localhost:3000/users/dojo/articles/new.json",
         "rel":"new_user_article"
      },
      {
         "name":"recent_user_articles",
         "options":["GET"],
         "path_template":"/users/dojo/articles/recent.json",
         "uri_template":"http://localhost:3000/users/dojo/articles/recent.json",
         "rel":"recent"
      },
      {
         "name":"user_article",
         "resource_templates":[
            {
               "name":"edit_user_article",
               "options":["GET"],
               "path_template":"/users/dojo/articles/{article_id}/edit.json",
               "uri_template":"http://localhost:3000/users/dojo/articles/{article_id}/edit.json",
               "rel":"edit",
               "params":["article_id"]
            }
         ],
         "options":["GET", "PUT", "DELETE"],
         "path_template":"/users/dojo/articles/{article_id}.json",
         "uri_template":"http://localhost:3000/users/dojo/articles/{article_id}.json",
         "params":["article_id"]
      }
   ]
}

Putting it to use, and why it’s important

Up to now, we’ve focussed on describing whole applications, providing client applications with metadata that helps them navigate the application effectively, even driving client APIs such as those generated dynamically by path-to.  This is an efficient way of doing things and it maps well onto the way familiar web APIs work now, but can we unify it with the RESTful concepts around hypertext (HATEOAS and all that)?  I think we can.

Imagine putting in the <head> section of the HTML representation of John Doe’s article collection at http://localhost:3000/users/dojo/articles a <link> element that references its own specific metadata at http://localhost:3000/described_routes/user_articles.json?user_id=dojo&format=json. A client reading that document can follow that link and now has in its possession the recipe for accessing all of the resources that belong to that collection – whether through complete URIs (e.g. for recent articles) or through parameterised templates (e.g. for specific articles, identified by id).

We can improve on this by returning these resource-specific links in HTTP headers. This makes them independent of the representation format (<head> and <link> are HTML-specific) and will work even on requests that return no body at all, HEAD in particular.  At the cost of one header (easily suppressed for clients that don’t need it), every resource is uniquely enhanced, minimising the amount of state that needs to be retained in the client.  For very lightweight clients (e.g. JavaScript libraries embedded in the browser) or for applications lacking a stable overall structure, this could be very useful indeed.

A mini path-to app centered on Mr Doe’s article collection could look something like this:

require "path-to/described_routes"
articles = PathTo::DescribedRoutes.bootstrap(
               "http://localhost:3000/users/dojo/articles")
articles.recent.get.first
articles["hateoas-and-other-7-letter-acronyms"].get
articles.post("title" => "The power of reflection", ...)

Here the bootstrap method creates an articles object that has an API that follows the structure given in the metadata discovered via headers found at http://localhost:3000/users/dojo/articles. Easy!

Older Posts »

Powered by WordPress