I node.js how to update the value of an already required module

Issue

I am a little step away of a very simple database cache system with nodejs. I don’t want to install a complete data handling solution like Redis, I just need to read and cache data and sometimes reload it from DB, without any handling feature, so I implemented this:

  • In my cache module, for the first evaluation I query DB and return the result in module.exports
  • So all following require for this module will return the cached value instead of querying the DB again >> I made it so far and I divided by 5 the consumed time.
  • Then I need a function to reload the module >> I failed there…

I feel like I am a very little step away to succeed, here is what I implemented (I run node.js v14.15.1, I simplified all code to make it more readable…):

Firstly I made the module to export the permissions by itself, it works, I got the list of permissions when I require the module:

/** FILE cached_permissions.js **/        
const permissionModel = require("../models/permission.model")

getPermissionsAtInit = async () => {
  return await permissionModel.find().exec()
}
module.exports = getPermissionsAtInit()

Then, the classic route file:

/** FILE permissions.route.js **/    

// Permission service
const PermissionService = require("../services/permission.service")

// Routes to get permissions from DB
app.get("/api/permissions", async (req, res) => { 
    res.send(await PermissionService.getPermissionsFromDB()} )

// Route to get permission from cache
app.get("/api/cached-permissions", async (req, res) => { 
    res.send(await PermissionService.getCachedPermissions()})

// Route to reset permission cache
app.get("/api/reset-cache", async (req, res) => { 
    await PermissionService.resetCache()
    res.send("cache reseted")})

Here is the permission service module

/** FILE permission.service.js */
// Module with cached permissions
const cachedPermissions = require("./cached_permissions.js")

// Module to query the db
const permissionModel = require("../models/permission.model")

class PermissionService {

  static getPermissionsFromDB = async () => {
    // Mongoose query to get data from DB
    return await permissionModel.find().exec()
  }

  static getCachedPermissions = () => { 
    // Directly returns value from cached module
    return cachedPermissions 
  }

  static resetCache = async () => {
    // firstly we delete the cached module
    delete require.cache[require.resolve("./cached_permissions.js")]
    
    // then we require again the module to force the re-cache
    require("./cached_permissions.js")
  }
}

module.exports = PermissionService

So I can run the following:

  • http call /api/permissions >> returns the list of permissions from DB ~50ms
  • http call /api/cached-permissions >> returns the same list of permissions ~10ms
  • I manually update data in mongoDB
  • 2nd http call to /api/permissions >> returned updated data
  • 2nd http call to /api/cached-permissions >> returned first set of data

So for so good, this is the expected behavior

  • Then http call to /api/reset-cache
  • 3rd call to /api/cached-permissions >> still return not updated data

I know the /api/reset-cache is done because I console.log Object.keys(require.cache), and I can see it deleted after the delete require.cache[...] and I can see it appear again after the new require("./cached_permissions.js").

But anyway, when I call again /api/cached-permissions, the value has not been updated. It feels like the const cachedPermissions = require("./cached_permissions.js") from my file permission.service.js has been loaded only once when the server starts when the permissions.route.js file require the permission.service.js file, and not each time a route is called, even if I reload a specific module.

Do you know a way I could reload the module in already loaded file, or update the value of a module in an alreay loaded file or some other way to make this work? I feel like I am a very little step away of a very simple DB cache system…

Solution

It feels like the const cachedPermissions = require("./cached_permissions.js") from my file permission.service.js has been loaded only once when the server starts, and not each time a route is called

Well that’s exactly what your code is written to do. The cachedPermissions is a constant in your permission.service.js module, and you never update that constant. If instead you would do

static getCachedPermissions = () => { 
  // Directly returns value from cached module
  return require("./cached_permissions.js"); 
}

it would load the module (from the module cache, or evaluate the module code if fresh) on every request.


But really there’s no reason to use the module cache for this. A simple variable in your permission service module would suffice:

/* permission.service.js */

const permissionModel = require("../models/permission.model"); // Module to query the db

async function getPermissionsFromDB() {
  // Mongoose query to get data from DB
  return await permissionModel.find().exec()
}

let cachedPermissions = getPermissionsFromDB();

module.exports.getPermissionsFromDB = getPermissionsFromDB;

module.exports.getCachedPermissions = () => cachedPermissions;

module.exports.resetCache = () => {
  cachedPermissions = getPermissionsFromDB();
};

Answered By – Bergi

Answer Checked By – Mary Flores (AngularFixing Volunteer)

Leave a Reply

Your email address will not be published.