Deep in Software with Hugo

Subscribe
Archives
October 28, 2025

How to clear module imports in Node.js

Peer under the hood of the Node.js module systems to understand how we can force modules to be re-evaluated.

Node.js supports two primary module systems: CommonJS and ES Modules (ESM). Both systems cache imported modules, ensuring that a module's code runs only once, even if it's imported multiple times.

When writing tests it can be useful to bypass caching for example if the code being tested is executed at the module scope (outside of exported functions).

While most test frameworks handle this for you eg. with jest.mock/vi.mock, when using a more barebones testing framework like node:test (built-in Node.js test runner) or in rare exceptions in application code.

CommonJS: clearing module cache

In the CommonJS module system, which uses require, the cache is available on the require.cache object. This object stores the cached modules and can be manipulated. For instance, if you want to force a module to re-run its code upon re-import, you can simply delete the module from require.cache like this:

delete require.cache[require.resolve('./path/to/module')];

This clears the cache for the specified module, allowing it to be reloaded and re-executed upon the next require call.

However, things work differently in the ES Modules (ESM) system.

ESM: import with Cache-Busting query parameter

ESM doesn't expose a module cache that you can manipulate directly. As a result, you can't delete a module from the cache as you can in CommonJS. Instead, in ESM we can force (re-)evaluation of a module by using dynamic imports and a query parameter to achieve "cache-busting".

The most common method to force an import to re-run in ESM is by using a dynamic import() statement combined with a query string that changes with each import. For example:

// this will run the module again
const mod = await import(`./path/to/file.js?t=${Date.now()}`);

Note that the query string doesn't need to be dynamic, so we could've used ./path/to/file.js?bust=my-key as well.

By appending a query string with a unique value, such as the current epoch, you effectively bypass the cache. This tricks Node.js into treating the import as a request for a new module, thus re-executing the module's code.

This is the theory of how to use this. You can see it in action with node:test (Node.js test runner) at Practical Example: Node.js test runner, testing an ES module with Cache-Busting Imports.

Don't miss what's next. Subscribe to Deep in Software with Hugo:
GitHub Bluesky
Powered by Buttondown, the easiest way to start and grow your newsletter.