skip to Main Content

I’m using Express.js, and I want to initialize a cache on every initial request to a route, and on the subsequent ones use the cached value.

For now, I do something like this:

let hasRun = false;

app.get('/my-route', async (req, res) => {
  if (!hasRun) {
    await redisClient.set('key', {});
    hasRun = true;
  }

  const data = await redisClient.get('key');
  res.send(data );
});

But if I have multiple routes, it becomes kind of messy.
What do you suggest?

Note: Even though Redis allows checking if a value exists, I want to ensure the cache is always refreshed and initialized on the first access.

2

Answers


  1. make middleware that checks a Redis cache to run initialization logic only once per route fetch and store data in Redis then serve cached data on future requests.

    Example

    const express = require('express');
    const Redis = require('ioredis');
    const axios = require('axios');
    
    const app = express();
    const redis = new Redis();
    
    // middleware initialization
    function runOnceForRoute(routeKey, initializationLogic) {
        return async (req, res, next) => {
            const isInitialized = await redis.get(routeKey);
    
            if (!isInitialized) {
                await initializationLogic(req, res);
    
                await redis.set(routeKey, Date.now().toString());
            }
    
            next();
        };
    }
    
    async function fetchAndCacheData(apiUrl, cacheKey) {
        console.log(`Fetching data from ${apiUrl} and caching it...`);
    
        // fetch data
        const response = await axios.get(apiUrl);
    
        // cache the data in redis
        await redis.set(cacheKey, JSON.stringify(response.data));
    
        console.log(`Data cached under key: ${cacheKey}`);
    }
    
    app.use('/route1', runOnceForRoute('route1:initialized', async (req, res) => {
        await fetchAndCacheData('https://jsonplaceholder.typicode.com/posts/1', 'route1:data');
    }));
    
    app.use('/route2', runOnceForRoute('route2:initialized', async (req, res) => {
        await fetchAndCacheData('https://jsonplaceholder.typicode.com/posts/2', 'route2:data');
    }));
    
    //handlers to return cached data
    app.get('/route1', async (req, res) => {
        const cachedData = await redis.get('route1:data');
        res.send(cachedData ? JSON.parse(cachedData) : 'No data available');
    });
    
    app.get('/route2', async (req, res) => {
        const cachedData = await redis.get('route2:data');
        res.send(cachedData ? JSON.parse(cachedData) : 'No data available');
    });
    
    app.listen(3000, () => {
        console.log('Server is running on port 3000');
    });
    
    Login or Signup to reply.
  2. // Middleware to initialize cache once per route
    function runOnceForRoute(routeKey, initializationLogic) {
      return async (req, res, next) => {
        const isInitialized = await redis.get(routeKey);
        if (!isInitialized) {
          await initializationLogic(req, res);
          await redis.set(routeKey, Date.now().toString());
        }
        next();
      };
    }
    
    // Route 1
    app.get('/route1', runOnceForRoute('/route1', async (req, res) => {
      await fetchAndCacheData('https://api.example.com/data1', 'route1-data');
      const cachedData = await redis.get('route1-data');
      res.send(JSON.parse(cachedData));
    }));
    
    // Route 2
    app.get('/route2', runOnceForRoute('/route2', async (req, res) => {
      await fetchAndCacheData('https://api.example.com/data2', 'route2-data');
      const cachedData = await redis.get('route2-data');
      res.send(JSON.parse(cachedData));
    }));
    

    The runOnceForRoute middleware checks if the cache has been initialized for a specific route key. If not, it calls the provided initializationLogic function to fetch and cache the data, and sets a flag in Redis to indicate that the initialization has been completed.
    For each route, we use the runOnceForRoute middleware to ensure that the cache is initialized only once. Inside the initialization logic, we call fetchAndCacheData to fetch the data and cache it in Redis, and then retrieve the cached data and send it as the response.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search