Node + Express + Redis: How to Cache Data

Updated: January 7, 2024 By: Guest Contributor Post a comment

Introduction

In the bustling world of web development, where speed is king and data is queen, a triumvirate has arisen to ensure your applications run as swiftly as the Mississippi River current. This tutorial embarks you on a journey into the heart of caching using Node.js, Express, and Redis, offering a paddle to navigate through the sometimes murky waters of efficient data handling.

Getting Started

To set the scene, imagine a quaint little server, earnestly delivering data to each and every request. But as the crowd hankers for more, the server’s sweat beading, it craves for some reprieve. Caching, my dear friends, caching is that sweet lemonade on a sultry summer day.

const express = require('express');
const fetch = require('node-fetch');
const redis = require('redis');
const app = express();
const PORT = process.env.PORT || 5000;
const REDIS_PORT = process.env.REDIS_PORT || 6379;
const client = redis.createClient(REDIS_PORT);
app.listen(PORT, () => console.log(`Server started on port ${PORT}`));

In the code above, we have laid the foundation of our little application, inviting Express, Redis and Fetch to the soiree.

Basic Caching Strategy

To start off simply, let’s implement a basic caching mechanism for a route that fetches data.

app.get('/data', (req, res) => {
  const fetchData = async () => {
    const response = await fetch('https://api.yourdata-source.com');
    const data = await response.json();
    client.setex('dataKey', 3600, JSON.stringify(data));
    res.send(data);
  };
  client.get('dataKey', (error, data) => {
    if (error) throw error;
    if (data !== null) {
      res.send(JSON.parse(data));
    } else {
      fetchData();
    }
  });
});

Mid-Stream Finesse: Middleware Caching

Once we’re comfortable with the basic cache setup, it’s time to refine our application with middleware, allowing the riverboat of data to flow smoothly past caching checkpoints.

const cache = (req, res, next) => {
  const { id } = req.params;
  client.get(id, (err, data) => {
    if (err) throw err;
    if (data !== null) {
      res.send(JSON.parse(data));
    } else {
      next();
    }
  });
};
app.get('/data/:id', cache, (req, res) => {
  //...handling fetching and responding
});

Advanced Caching Tactics

With a heart as fiery as an entrepreneur struck with inspiration, it’s time to dive into the realm of advanced caching tactics.

// Set data with expiry and dealing with invalidation
client.set('my_data', 'value', 'EX', 10, (err) => {
  if (err) { /* handle error */ }
});

// Hashes for complex data
client.hmset('my_hash', 'field1', 'value1', 'field2', 'value2', (err) => {
  if (err) { /* handle error */ }
});

// Automatically refresh cache after expiration by listening to Redis events
client.on('message', (channel, message) => {
  if (channel === 'expired') {
    // Refresh the cache for the provided message key
  }
});
client.subscribe('__keyevent@0__:expired');

Integrating Data Persistence

As the importance of your data grows as tall as a cottonwood tree, integrating data persistence in your caching strategy comes into play.

// Using Redis' native persistence:
// RDB snapshots or AOF logs configurations
// Ensure in your redis.conf:
save 60 10000
appendonly yes

Conclusion

In this odyssey along the technological river, we’ve outfitted our steamboat with the lore of caching using Node.js, Express, and Redis. Armed with this knowledge, your applications can saunter down the mighty river of user requests with the ease of a calm breeze, delivering data with the grace and speed of an antebellum debutante. Remember, caching is the art of remembering what’s worth remembering; a well-cached application is a sign of any clever programmer’s upbringing and breeding.