Ladda – A New Library for Client-Side API Caching

In an ideal world, caching wouldn’t be something we have to care about. However, with more and more mobile users on slow and limited data plans, as well as more advanced applications, we can’t escape reality. We need caching. As a response to this we have invested quite some time in Ladda – a dependency-free client side library for caching, invalidation of caches and handling different representations of the same data. Ladda is implemented using JavaScript (ES2015), framework agnostic (works equally well with React, Vue, Angular or vanilla JavaScript) and designed to make it easy for you to implement a caching solution without increasing the complexity of your application code.

Read on to learn how Ladda can be useful for you, how it helps you implement a sophisticated caching solution, and for a comparison of Ladda with other popular solutions for client-side API caching.

Scenarios Where Ladda Can Help You

There’s no such thing as a free lunch. Caching speeds up your application, but it comes at a cost: it increases the complexity of your application code. The following examples will show you how Ladda can help you to reduce this cost in some common scenarios.

Just Caching

The most straightforward usage of a cache is simply to cache a value, and if it has been previously cached, return it directly from the cache. Consider that you make an API call “getUsers”. The most straightforward solution for implementing caching would look something like:

const getUsers = () => {
   if (!inCache(key) || hasExpired(ttl, key)) {
       const res = api.user.getUsers();
       putInCache(key, res);
   }
   return fromCache(key);
}

When using Ladda your application code would look like:

const getUsers = api.user.getUsers;

Note how we separate what we want to do (getting the users) from the caching logic, which is just an optimization. This is a pretty simple example, which might not be a sufficient motivation to add a library to your application, but it quickly gets quite complicated as we start to manipulate our data.

Cache Invalidation

Stale data is your new enemy as soon as you introduce caching. Consider the example of users again. You are getting all users, but then you spot a typo in one user’s surname and correct it. Now you are left with two choices: either you update the cache used by “getUsers”, or you remove the cache and refetch the data the next time someone calls “getUsers”. Let’s consider the latter option first. It could look like:

const updateUser = (modifiedUser) => {
    api.user.updateUser(modifiedUser);
    clearGetUserCache();
}

With Ladda it would look like:

const updateUser = api.user.updateUser;

Ladda would clear the cache for you, you just need to tell Ladda what to invalidate in a configuration, which lives outside of your application. However, by default Ladda will pick the harder option, it will update the cache for you. This comes with the benefit that after updating your user, you can call “getUsers” and get all the users directly from the cache, with your updated user of course.

Ladda has more to offer, but I’ll leave that for you to read about. You’ve heard a lot of promises and seen some simple code. But as you might have suspected, you still need to specify things such as the TTL (time to live), what to invalidate, and which function is updating the user and which one is retrieving users somewhere.

How does it work

The first claim, that Ladda allows you to add caching without making your application code more complex, is achieved by separating your application code from your caching logic. Ladda allows you in a concise and declarative way to express what TTL you want for a specific entity, such as user, and what you want to invalidate when something happens. Going back to the simple updateUser example, where you simply invalidate the “getUsers” cache, it would look like:

{
    user: {
        api: userApi,
        invalidates: ['user']
    }
}

Of course, you don’t even have to specify that ‘user’ invalidates its own cache, since Ladda will update the cache in place for you, so you can simply write:

{
    user: {
        api: userApi
    }
}

And rely on Ladda to always ensure that “getUsers” gives you an up-to-date list of users. Now, the only thing left is to create “userApi”. But this is something that you probably already had, it is just a bunch of functions communicating with your user endpoints. Let’s pretend that you have a file:

export function getUsers() { return doHttpGetRequestAndReturnPromise(); }

export function updateUser(user) { doHttpPutRequestAndReturn200(user); }

Ladda only requires you to specify the CRUD-operations:

getUsers.operation = 'READ';
export function getUsers() { return doHttpGetRequestAndReturnPromise(); }

updateUser.operation = 'UPDATE';
export function updateUser(user) { doHttpPutRequestAndReturn200(user); }

That is everything, just adding metadata directly to your functions and putting your entity in a configuration object. There are, of course, plenty more options, such as one mentioned already, TTL. You will find them all in the documentation. You’ll also find complete examples in the repo to make it easy for you to get started. Don’t forget to have a look at Search Hacker News with Ladda and this contact list (which uses all the supported CRUD operations) for examples that you can play around with.

Before we move on, let’s just have a quick look at a final example and what HTTP-requests it will result in:

api.user.getUsers() 
  // GET-request was sent
  .then(() => api.user.updateSurname(user)) 
  // PUT-request was sent
  .then(api.user.getUsers); 
  // No request was made! Directly from the cache.

A good caching solution tries to maximize the number of cache hits, Ladda is no exception.

Ladda Release

Fig 1. Sequence diagram showing the result of calling getUsers followed by updating a user and then calling getUser again. Note that we do not make a HTTP-request for the final getUsers call.

Ladda is not the first attempt to make caching simple, I believe that it can be the best choice in some cases, but it is important to look into all available options. Let’s do a brief comparison between Ladda and some other popular caching solutions.

Comparison With Other Solutions

First off, keep in mind that I’m not an expert in the other technologies, but I’ve tried to make the comparisons in an objective manner. One very popular solution is Relay. The big difference between Ladda and Relay is that Relay is built for GraphQL. Hence, Ladda and Relay are not really two alternatives to compare, since if you have a GraphQL backend, Relay is without doubt the better choice, but otherwise it isn’t a choice.

Another solution is redux-query. One key difference is already revealed in the name, it is specifically designed for use with Redux. Ladda can be used with any framework as well as without a framework. But let’s assume we are using React and Redux to make a viable comparison. The most prominent difference is that redux-query influences how you write your application code. This means that it has a greater buy-in than Ladda, but it also means that it can handle more things for you. If you want a more complete solution and don’t mind the buy-in, redux-query might be the best choice. But if you have your own solutions in place and just want to speed up your application by caching, then Ladda is probably a better choice. You can potentially add or remove Ladda without changing a single line of application code.

But perhaps more importantly, it’s about which code style you prefer and which library can offer the features you need. Ladda lets you stay with simple function calls that are “magically” very quick sometimes (when you hit the cache). To get users you simply call “getUsers()”. Other solutions tend to use a more declarative approach, where you fetch your data by creating an object describing what data you want.

There are a bunch of other caching libraries in JavaScript, for example js-cache (https://github.com/dirkbonhomme/js-cache). These are more generic than Ladda. They don’t support automatic invalidation logic, views of entities, or many other pieces of functionality that are often required in a sophisticated caching solution.

Conclusion

We hope that you will find Ladda useful and keep it in mind next time you need client-side caching for your API layer. Ladda is dependency-free, only 14KB, has high test coverage and allows you to specify your caching logic in a declarative and very simple way. Give it a shot and let us know what you think!