Dugan Chen's Homepage

Various Things

Single Page Web App Architecture Done Right

2011-07-26

The holy grail of web application architecture is not the single-page application. It is the single-page application that works as a multi-page application if you don’t have Javascript enabled. Here’s the right way to write one.

It’s simple to describe. You first make it work as a multi-page application, preferably without Javascript. Then you enhance it to work as a single-page application if the web browser will support one. This approach has a name. It’s called Hijax.

An application using this architecture cannot use the URL hash. The hash, after all, is not sent to the server and will not be seen without Javascript. That means that if you do use the URL hash, then bookmarks to parts of your site will break when transferred between Javascript-enabled and non-Javascript-enabled browsers. The application must instead use the HTML 5 history API, which includes the famous pushState verb.

Furthermore, we can maintain DRYness and reduce the amount of duplicate code by using the same templates for both the client-side code and the server-side code. This is the approach recommended by both of the following conference talks:

Accomplishing this requires a templating engine that has support for both Javascript and for whatever language you’re using for the backend. This probably means mustache, which has support for most server-side languages. If your backend is Java, then Google’s Closure Templates are another option. You will also need a backend framework that gives you the freedom to use the templating language or “view engine” of your choice.

On the client side, you’ll need to be able to listen for changes in the location (not the hash, the location) and, again, to easily choose your templating engine.

In this example, we’ll use Django on the server and Backbone on the client.

There’s nothing about Django that requires using the template language.

Backbone is agnostic with respect to your preferred method of HTML templating.

For the client-side code, we’ll first detect whether the browser supports the HTML5 history API. If not, we’ll do nothing. Otherwise, we’ll load the templates from the server and start listening for location changes. We’ll also add handlers to everything that would send a request to the server: all internal links and submit buttons. Each handler puts the application into the same state that the call to the server would, and then cancels the event.