tl;dr: Use Backbone.Hoard to prevent duplicate server requests.
As an application developer at Conductor, I spend a lot of my time developing Searchlight, a web presence management platform that contains all sorts of reports to help our users gain actionable insights from raw data. Our users can customize each one of these reports, creating a “widget”, and add it to a “workspace”, which can have many different types of widgets from all parts of the application. Since any given widget may or may not be related to other widgets in a workspace, each one is responsible for loading its own data. This high degree of customization leads to application uncertainty and makes it extremely easy for similar widgets to repeatedly request the exact same data from the server. These duplicate requests slow the page by blocking other resources, and they force the server to return the same results over and over again. When you consider how expensive some of our reports can be to generate, the costs of this behavior really start to add up.
Backbone.Hoard is Here to Manage Your Server Requests
With dozens of types of widgets and hundreds of ways to customize each one, the applications team has decided to treat each widget as its own self-contained micro application, demonstrating separation of concerns and affording us some much-needed sanity. Unfortunately, this architectural choice is what results in the aforementioned duplicate requests. So what’s to be done about this seemingly irreconcilable struggle between good user experience and developer happiness? Enter Backbone.Hoard, a Backbone plugin I wrote to de-dupe XHRs and prevent future requests by caching responses, all without changing the API of Backbone’s Models and Collections.
Backbone.Hoard works by intercepting calls to Backbone.sync, Backbone’s method for interacting with the server. When it receives a sync request for a particular model, Hoard will check its cache to find any data matching that model. If the model is cached, Hoard will immediately return that cached value, preventing an entire round-trip call to the server. If the model is not in the cache, Hoard will carry through with a request to the server. While that request is underway, Hoard will intercept other calls for the same endpoint, preventing them from going to the server, and responding with the same result as the original request. This particular feature is driven by our need to intercept many calls at once, and it is the reason we were unable to benefit from existing Backbone plugins, such as backbone-fetch-cache.
Backbone.Hoard is Believed When Seen
It helps to see Backbone.Hoard in action in order to understand its benefits. In the example below, clicking the ‘Fetch’ button will simulate a call to the server, while clicking ‘Fetch x3’ will simulate 3 calls at the same time. You can unleash the power of Hoard by clicking the checkbox.
Without Backbone.Hoard enabled, you can see that every single fetch call goes to the server. This is how our workspaces used to work, which made them sluggish when users tweaked with filter settings. When you enable Backbone.Hoard, you can see that only one call ever makes its way to the server. All early requests wait for the response from the first request, and subsequent requests will get data from the cache. By playing with the example, you can see that Backbone.Hoard prevents many extraneous server requests when you know that your endpoint’s response will remain constant.
Backbone.Hoard is a Second Chance
Backbone.Hoard isn’t the first project we’ve used to address the issues of duplicate XHRs. In the past, we used something we called the PageDataProvider. Unlike Hoard, which caches server responses at a Backbone-agnostic data layer, the PageDataProvider would cache an actual Backbone Collection or Model, returning the same instance to anything that requested it. This worked great for a while, but as the complexity of our application grew, and as user-customization increased, we found that a veritable horde of events were acting on the same collection, resulting in invalid page states as the collection tried to represent an inconsistent state. When we decided to overhaul and modernize our entire front-end architecture, I saw a chance to replace the battle-worn PageDataProvider with something new.
Backbone.Hoard is Easy to Use
And so Backbone.Hoard came to be. But in addition to de-duping XHRs and caching responses, Hoard needed to be easy to use. It wouldn’t be a powerful tool for our developers if it caused them to curse my name whilst shaking their fists at the sky. To that end, Hoard works by replacing the sync method of a Backbone Model or Collection with a Hoard-aware version. After that, a model or collection can be used exactly the same as before— Hoard will take care of all de-duping and caching behind the scenes.
Backbone.Hoard Can be Configured Like Woah
The Control is the main entry point for Hoard. Out of the box, it caches data using localStorage, clears the cache when it’s full, and responds sensibly for standard RESTful behavior. But another design goal for Hoard was for it to be as configurable as possible, ensuring that you can bend it to your will to meet your application’s specific needs. For Searchlight, we decided that our current client-server infrastructure wasn’t ready to deal with the possibility of a stale cache and opted to avoid persisting data in localStorage. We instead implemented a small in-memory cache to ensure fresh data on every page load and delay the need for more complicated cache control on the server.
While Hoard defaults to using the synchronous localStorage, it’s important to note that Hoard’s storage layer is entirely asynchronous. This enables the use of IndexedDB, PouchDB, a remote Redis instance, or any other data store with a small amount of configuration. The storage layer is configurable on a per-Hoard.Control basis, which allows you to start with localStorage and change strategies as your data evolves. So even if your application doesn’t need Hoard’s XHR de-duping behavior, you can use it to provide caching to the storage engine of your choice.
Backbone.Hoard is Available Now!
Conductor engineers have already developed other open-source projects to tackle some of our challenges, and our widget-born woes offered an occasion to put some code out there to help other developers as well. Between its XHR de-duping, automatic caching, and high configurability, Backbone.Hoard has a lot to offer. It’s simple enough to just drop into an existing Backbone project, so I encourage you to try it out and see what benefits your project can get from it. I have plans to eventually extend Hoard to the non-Backbone world, but if you want to try it out in your current framework, start by overriding Hoard.sync and see where that takes you. If you have any questions, ideas, or problems, feel free to open an issue, message me in gitter, or leave a comment below.