ReST, HTML, JavaScript, and URIs
We created data-
attributes in HTML5 so servers could embed
application-specific information in the structural representation of a resource.
The IANA also provides a directory of meaningful link relationship types for use
in HTML documents. If we comply with ReST over HTTP, then we should use those to
contain the information that our JavaScript uses. Otherwise, we end up breaking
the core tenets of ReST and our applications become difficult to maintain and
extend.
It always starts with a conversation
I had a short conversation, today, with my good friend Bryan and newer acquaintance Dave that centered around the contents of a file named router.js that we include with require.js.
In our project, we use amplify to provide in-browser
pub/sub. This provides developers with a way to make modular and composeable
pages. It turns out, though, that the inclusion of amplify has led to the
use of its amplify.request
API, as well. And, here’s where it gets a little
hairy.
In the file router.js, we find entries like the following:
1 | amplify.request.define('Create_SomeResource', 'ajax', { |
And, this is where I find fault with what we wrote: we partly hard-coded a URI template into a JavaScript file.
Why is a URI template in a JavaScript file bad?
If you want a clearer understanding of ReST, you can read Dr. Fielding’s article or, if you trust me, you can read my own post.
An application using the ReST architecture starts at a URI. (For us, we’ll just say URL from now on because that’s what we type in browsers.) Some server sends a representation of the resource to which the URL points, normally a nice HTML document. Hypermedia FTW. Now, here comes the important part:
The returned representation contains all links that define transitions valid for the current state.
That means that when you go to curtis.schlak.com, the links that you see are
the only valid transitions for you to take. You may notice, for example, that
my blog’s URLs take the form /yyyy/mm/dd/some-text.html
. Somehow, you know
the title for a post I wrote four years ago. You could infer a valid URL from
that information, type it into the address bar, and transition to a new state
outside the ones presented to you as part of the links in the HTML document.
That’s bad. Because endpoints change. Because URLs can point to transient resources. Because the URL specification clearly states that a URL is opaque to humans. That human-readable aspect does not need to exist.
So, if it’s not in the HTML document somewhere, then we shouldn’t assume that anything we type into the address bar would take us to a valid place on the Wild, Wild Web.
Finding URLs in the JavaScript breaks that rule of meaningful link semantics in the resource representation sent to the browser.
But, Curtis, JavaScript comes from the server, too!
True, fine reader. In our HTML document, we would have something like
1 | <script src="/scripts/app/controller/view.js"></script> |
That is a valid URL that comes from the resource representation. Then, the browser downloads the JavaScript representation of that URL and everything just works. So, what’s the big deal, you think.
The big deal: code-on-demand
I spend all this time talking about ReST as an architecture and URIs as ways to find resources and methods of getting representations of those resources, I sometimes forget to write about another important part of ReST: code-on-demand.
You’ve written a Web application and your code sends some HTML back to the browser and all is good. But, the Web application has to be shiny. So, to “power” the interactivity of the application, you use some JavaScript to make AJAX calls to provide a better user experience. Your application sends and receives JSON documents that contain all the data necessary to fulfill the functionality of the Web app and all is well in the world.
What purpose did JavaScript serve in this case?
Those JSON documents that you send and receive need application-level understanding to interpret the contents of the document. ReST allows for an “optional constraint” called code-on-demand that the client uses to extend its understanding of media types within its purview. Browsers don’t know how to interpret the content of a document with the MIME type application/json. To allow that to happen, your HTML loads JavaScript code to interpret the contents of those documents.
JavaScript provides extensibility to the native functionality provided by the browser, the client in the Web-based ReST architecture. Because of that, the JavaScript in our applications should interpret documents loaded in the browser, sent to an endpoint, or retrieved from the server. It should not carry meaningful links because it extends the functionality of the browser on the already-loaded resource representations.
Putting that all together
Back to the original example:
1 | amplify.request.define('SomeResource', 'ajax', { |
How do we get that hard-coded URI template out of there and replace it with something that conforms to the ReST standard? In our HTML, for example, we could go to the Link Relations list maintained by the IANA and found the “create-form” relation. Then, our HTML could contain
1 |
|
Then, the JavaScript would read like this:
1 | amplify.request.define('Create_SomeResource', 'ajax', { |
That demonstrates that the HTML should own the links (in link
or a
tags or
in data-
attributes) that the JavaScript can then act upon. Need to change
your controller name? Want to move to another URL creation scheme? The
JavaScript will work unchanged, at this point, and you can go to the place that
generates the paylaod for the resource representation where all of the data
should live!