URLs are core to how the web works. They should be first class citizens in your web applications.
At Swipely, we have a large client-side application. YUI has helped us keep our codebase nice and modular. But we didn’t love the way our route declarations were dispersed amongst a number of controller-like router objects, each dispatching for a subset of the paths available to the user.
Centralize your routes
We believe routes should be defined in one place, and declared separately from the logic that handles the consequences of visiting a particular route. Some benefits of this approach:
- application URL hierarchy is easy to grok at a glance
- controllers/route targets are not bloated with route setup and logic
- you can be confident about multiple actions occurring in a specific order for any route
Abstract what's browser-specific
The different ways browsers handle URLs and history should not be top of mind when you are writing application code.
So with Aviator, you write your hrefs in your links normally, and Aviator will figure out whether or not it needs to use hash-based routing. Same goes for navigating to a route in code as the result of some action.
Aviator doesn't care how you render your views, how you load your data, or what libraries and frameworks you do it with. Its single responsibility is to call the right method on the right object.
We call these objects route targets, and it's entirely up to you what they look like. At Swipely, they serve as controllers; they instantiate views and models and load the data.
It’s fine if one route target receives all the requests, and that might make sense for a small application. But Aviator really shines by making it easy to delegate the route handling to different targets, keeping each one small and focused.
Set up Aviator
Set up is declarative. Call
Aviator.setRoutes with a nested object. The object lays out all routes and corresponding actions to take for those routes.
Let's say you have an application where users upload documents and keep track of their document revisions.
Given this setup, Aviator knows that when a user goes to
/docs/2013_potluck_guest_list/revisions/20130722140801?readonly=true, the steps to take are:
This really lends itself to specializing the responsibilities of your route targets. You could imagine one route target that tracks metrics, and gets called on every request, or another that handles a sidebar, but only on some requests. It's easy to be explicit about these things with the nested object setup.
show functions, you get a request object as a parameter. So you have access to the documentID, the revisionID, and the readonly flag through
request.params. You can also always get the URI, the query string, and the route that mapped to this function.
Navigate the World
Aviator exposes a small API. Besides
setRoutes for configuration, it handles functionality that really should be taken care of by a router rather than by application code.
To name a few, the
Aviator object abstracts navigating to a route, updating the URL silently, serializing query params and accessing existing params.
Try it out
Aviator is open source! We’d love to hear how it plugs in to your application. Check it out on github.