Skip to content
JavaScript Basics: Load CommonJS from an ES Module with createRequire

JavaScript Basics: Load CommonJS from an ES Module with createRequire

October 27, 2026

An ES module may need to integrate with an existing CommonJS helper. createRequire constructs a require function anchored to a filename or file URL. This fixture creates a local helper and demonstrates resolution, caching, and a missing module.

Prepare a scratch directory

Tested on Linux with Node.js v24.19.0. You need the named runtime, Bash, a text editor, and the standard mktemp, rm, and rmdir utilities. The .mjs extension selects ES modules. Assertions check the demonstrated behavior; no npm packages or remote services are needed.

Keep using the same terminal. Stop if setup fails, before saving the source:

start_dir="$PWD"
work_dir="$(mktemp -d)" || exit 1
cd -- "$work_dir" || exit 1

Run the example

Save this as demonstrate-create-require.mjs:

import assert from 'node:assert/strict';
import { createRequire } from 'node:module';
import { mkdtemp, writeFile, rm } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import path from 'node:path';

const directory = await mkdtemp(path.join(tmpdir(), 'require-demo-'));
try {
  await writeFile(path.join(directory, 'local-helper.cjs'),
    'module.exports = { count: 1 };\n', 'utf8');
  const require = createRequire(path.join(directory, 'anchor.mjs'));
  const first = require('./local-helper.cjs');
  first.count += 1;
  const second = require('./local-helper.cjs');
  assert.equal(first, second);
  assert.equal(second.count, 2);
  console.log('Same cached exports object:', first === second);
  console.log('Cached count:', second.count);
  assert.equal(path.basename(require.resolve('./local-helper.cjs')), 'local-helper.cjs');
  console.log('Resolved basename:', path.basename(require.resolve('./local-helper.cjs')));
  assert.throws(() => require('./missing-helper.cjs'), { code: 'MODULE_NOT_FOUND' });
  console.log('Missing helper: rejected');
} finally {
  await rm(directory, { recursive: true, force: true });
}

Run it:

node demonstrate-create-require.mjs

Observed output:

Same cached exports object: true
Cached count: 2
Resolved basename: local-helper.cjs
Missing helper: rejected

Read the result

The anchor determines how relative module requests resolve; it does not have to be an existing executable entry file. Both require calls reach the same resolved helper and return its cached exports object, so the second call sees the mutation. require.resolve reports where a request would resolve. The missing helper is handled through its error code rather than a machine-specific error message.

Apply it carefully

Loading a module executes its code, so use trusted module locations and avoid constructing requests directly from untrusted input. An anchored require uses CommonJS resolution and caching rules; it does not turn every ES module into a synchronously loadable module. Prefer normal imports for new ES module code and use this bridge where interoperability requires it. The fixture’s cache dies with this short process; deleting files is not a cache-reset strategy in a long-running application.

The official reference describes the API and its options. The checks above cover the specific fixture, not every possible input or environment.

Clean up

Remove the source and then the empty practice directory:

rm -- demonstrate-create-require.mjs
cd -- "$start_dir" || exit 1
rmdir -- "$work_dir"
unset start_dir work_dir

If the directory is not empty, inspect the remaining files rather than deleting unknown contents.

Acknowledgements

This post was created with assistance from AI.

Last updated on