Skip to content

/flags/nodejs/node-import-require-preload

node --import and -r (--require): preload a module before the app starts

verified onenvironmentlocal · windows 10.0.26200 arm64 · GNU bash 5.3.15 (Git Bash) · v24.20.0updated
tool
Node.js
command
node
flag
-r, --require MODULE; --import MODULE
default
none (nothing is preloaded)

node -r ./setup.cjs app.js (long form --require) loads a CommonJS module before the entry point runs; node --import ./setup.mjs app.js does the same for an ES module. Preloading is how tools such as APM agents, OpenTelemetry, dotenv/config, source map support and TypeScript loaders hook into an application without changing its code. Both flags can be repeated, and they also work inside NODE_OPTIONS.

Preload order

bash
cat > setup.cjs <<'EOF'
console.log("setup.cjs (require)");
globalThis.startedAt = "preload";
EOF
cat > setup.mjs <<'EOF'
console.log("setup.mjs (import)");
EOF
cat > app.js <<'EOF'
console.log("app.js, startedAt =", globalThis.startedAt);
EOF
echo "--- no preload"
node app.js
echo "--- -r and --import"
node -r ./setup.cjs --import ./setup.mjs app.js
echo "--- through NODE_OPTIONS"
NODE_OPTIONS="--require ./setup.cjs" node app.js
echo "--- missing preload file"
node -r ./missing.cjs app.js 2>&1 | grep -m1 "Error:"

Output

--- no preload
app.js, startedAt = undefined
--- -r and --import
setup.cjs (require)
setup.mjs (import)
app.js, startedAt = preload
--- through NODE_OPTIONS
setup.cjs (require)
app.js, startedAt = preload
--- missing preload file
Error: Cannot find module './missing.cjs'

--require modules are loaded first, then --import modules, then the entry point. A preload that cannot be found stops the process before any application code runs.

Comparison

Aspect-r, --require--import
Module systemCommonJS (and ES modules through require(esm) in Node.js 22.12+)ES modules, also CommonJS files
Top-level awaitNoYes
Typical usedotenv/config, ts-node/register, older APM agentsmodule.register() loader hooks, OpenTelemetry ESM, tsx
Allowed in NODE_OPTIONSYesYes
Path resolutionLike require() from the current directoryLike import from the current directory; relative paths need ./
Available sinceEarly Node.js versionsNode.js 20.6.0 and 18.18.0

Common commands

bash
node -r dotenv/config app.js                 # load .env through the dotenv package
node --import tsx app.ts                     # TypeScript through the tsx loader
node --import ./instrumentation.mjs app.js   # OpenTelemetry or another ESM instrumentation
NODE_OPTIONS="--require ./tracing.cjs" npm start

A bare name like dotenv/config is resolved from node_modules in the current directory. A relative path must start with ./, otherwise Node.js looks for a package of that name and fails with Cannot find module.

Pitfalls

  • Flags after the script name belong to the script: node app.js -r ./setup.cjs does not preload anything.
  • The legacy --loader / --experimental-loader flag is discouraged; register hooks from a file passed to --import with module.register() instead.
  • A preload in NODE_OPTIONS also runs in child processes and tools started with node (test runners, build scripts), which may load an agent twice or slow every command down.
  • An ES module preload with -r on older releases fails with ERR_REQUIRE_ESM; use --import there. The differences between the two module systems are covered in require vs import.

FAQ

Should I use --require or --import?

Use --import for ES modules and for loader hooks registered with module.register(); use -r for CommonJS setup files. Both can be combined, and preloads run in the order they appear on the command line.

How do I preload a module for every node process?

Put the flag in NODE_OPTIONS, for example NODE_OPTIONS='--require ./tracing.cjs'. Every node process that inherits the variable, including child processes and npm scripts, loads the file.

Why does my preload run but my app does not see its variables?

Preloads share the process, not the module scope. Set values on globalThis or process.env, or export them and import the module again from the app; local variables in the preload are not visible.

Sources