How to Cache data with Hapi/catbox

Introduction to hapi.js and Catbox:

Hapi.js is an open-source framework for building web applications and services. It provides a solid set of features that make it easy to develop scalable and secure server-side applications. One of the key features of hapi.js is its plugin system, which allows developers to extend the functionality of their applications by adding pre-built modules.

Catbox is one of the plugins that was built for hapi.  Catbox is a multi-strategy key-value object store built specifically for hapi.js. It is one of the most popular caching tools used with hapi.js and will be covered in this article. Catbox comes with built-in support for various caching strategies, including memory cache, Redis, and Memcached. Additionally, the npm Registry offers a wide range of community-provided providers for even more caching options.

Caching and Its Importance:

Caching is a technique used in computer systems to store frequently accessed data in a temporary storage location called a cache. The purpose of caching is to improve the performance and efficiency of an application by reducing the time required to retrieve data from slower or more resource-intensive sources.

Why Should You Cache Data?

There are several reasons why caching is essential in web development:

  1. Improved Performance: Caching allows you to serve data quickly without having to perform complex calculations or retrieve information from slow external sources. This results in faster response times and a smoother user experience.

  2. Reduced Server Load: By serving cached data instead of generating it dynamically for each request, you can reduce the load on your server and improve its scalability. This is especially important in high-traffic applications where handling a large number of concurrent requests can strain server resources.

  3. Cost Savings: Caching can help optimize resource utilization, leading to potential cost savings. By reducing the amount of processing power and network bandwidth required to serve data, you can lower infrastructure costs and improve the overall efficiency of your application.

When Should You Cache Data?

Determining when to cache data depends on various factors specific to your application:

  1. Frequency of Data Access: If certain data is accessed frequently by users, caching can be beneficial as it allows you to serve that data quickly without repeatedly fetching it from its original source.

  2. Data Stability: If the data being accessed doesn’t change frequently or remains relatively static over a certain period, caching can be effective in serving the same content repeatedly without the need for regeneration.

  3. Expensive Operations: If your application performs computationally expensive operations or accesses slow external resources, caching the results can significantly reduce processing time and improve overall performance.

In summary, caching data is crucial for improving performance, reducing server load, and optimizing resource utilization. It is most effective when applied to frequently accessed or expensive-to-generate data. By understanding your application’s specific requirements and considering factors such as data access frequency and stability, you can determine the appropriate scenarios for implementing caching in your application.

Installing Catbox into your hapi.js application

  1. Install Catbox: with either yarn or npm.
  2. Install Cache Engine: Decide if you want to use Memory, Redis, or Memcached as a caching engine.  To make things simple in this article I’ll be using Memory caching with Catbox-memory.
PowerShell
npm install @hapi/catbox @hapi/catbox-memory
// or 
yarn add @hapi/catbox @hapi/catbox-memory
  1. Create a Catbox Cache Instance:  There are two types of caches that catbox offers: Client or Policy.
  • Client: is a low level caching strategy and a bit easier to use.
  • Policy: is more high level and gives users more customization and options for caching.
  • Check the docs to see which caching strategy you’ll need, in this article we’ll go over using the Client cache.
JavaScript
const Catbox = require('@hapi/catbox');
const CatboxMemory = require('@hapi/catbox-memory');
...
const cache = new Catbox.Client(CatboxMemory.Engine, { partition: 'my-fun-cache' }). 

// to use any of the caching functions you'll have to start the cache server
cache.start()

// if you have a cleanup method you can also stop the cache server manually
if (cleanupTime) {
    cache.stop()
}
  1. Create the Client: We are first assigning the Client to the variable cache.  The Catbox Client takes a required first argument: engine, which in our case is the Memory engine.  The second parameter is for any options.  If you have multiple Clients you can create different partitions for each and data can be cached separately for Redis/Memcached or a different caching strategy.
  2. Start The Cache Server: Next you’ll want to start the cache server so that cached data can persist and we can access the caching functions.  This should be placed somewhere near the startup of your application, or maybe in a singleton class so it stays active throughout the lifecycle of your server

Caching data

Now that you have the cache Client created lets start caching some data.  In our case we used dependency injection to pass this cache object to the modules that need it, but I’ll leave the architecture up to you.  Once you have your cache object and the cache server has been started you’re ready to start caching real data.

  1. Get Some Data To Cache: from either a database query or api call and massage it the way you want it to be stored.
  2. Set The Cached Data: The Client set function takes 3 arguments
  • Key :  A key object must contain a segment and id key.  See below for further explanation.
  • Data: data to cache.
  • Time To Live: how long the cache should live (ms).

Key arguments per the docs:

  • segment:  a caching segment name string. Enables using a single cache server for storing different sets of items with overlapping ids.
  • id – a unique item identifier string (per segment). Can be an empty string.

Keep in mind that when you lookup your data that you just cached, you’ll need to use the exact same key to get the data we just cached.  So it may be smart to make a dictionary or db table of segment/id combos if you plan on caching a bunch of data. A solution we used was using a date string as an ID so that we know the cache is being set on a particular days data.

This data should now be cached, and almost instant to lookup in the future.

JavaScript
const timeToLive = 60000;
const myData = await getBigData();
await cache.set(
    { segment: 'mySegment', id:'uniqueId' },
    myData, 
    timeToLive
);
  1. Get the Cached Data: All you need to do now is use the Client .get(key) method using the same key that we initially used to set the cache.  But wait. What if we try to get this data before it’s been cached? Luckily it’s easy to check if the data exists.  You can simply check if the return value from the get function is null – meaning that no data was found for that key.  Using Client you can also access the time left to live on the cache, and the timestamp of when it was originally cached.
JavaScript
const myData = await cache.get({ segment: 'mySegment', id:'uniqueId' });

if (myData) { 
    const data = myData.item //cached data stored in item field
    const timeLeftToLive = myData.ttl;
    const timestampOfInitialCache = myData.stored;
} else {
    //get and cache data 
}
  1. Celebrate! It’s that simple, the docs have some more info and I definitely recommend you check them out to see what more advanced options and functions you can use.  In the meantime this should get you moving forward with a basic example of setting and getting cached data.

Conclusion

When working with large data and external api’s it can be crucial to cache data for improved performance and reduced server load – especially if you don’t have to access it often or if that data tends to not change very often.  This was a basic example on how to get and set cached data with hapi.js and Catbox. Hopefully this helped and you can implement it into your app where needed.  If you have any questions, comments, or concerns please feel free to let me know down below.  Cheers and Happy Coding!

Leave a Comment

Your email address will not be published. Required fields are marked *