Intermediate Node.js Interview QuestionsIntermediateConcept
Node.js · Question 34
What is the difference between CommonJS and ES modules in Node.js?
Direct answer
CommonJS uses require/module.exports with synchronous load and runtime resolution; ES modules use import/export with static analysis, async top-level await, and live bindings — Node supports both; package.json "type" and .mjs/.cjs extensions pick the default.
Node evolved from CommonJS (CJS) as its original module system to native ECMAScript modules (ESM) aligned with browser JavaScript. AceDevHub's monorepo uses ESM in apps/api and apps/web with "type": "module" or .mjs where needed.
Why interviewers ask
| Aspect | CommonJS | ES Modules |
|---|---|---|
| Syntax | require(), module.exports | import / export |
| Loading | Synchronous at require time | Async graph; import hoisted |
| Analysis | Dynamic — require(variable) | Static — imports must be top-level strings |
| Default in Node | .js when "type": "commonjs" or absent | .js when "type": "module" |
| Interop | createRequire, dynamic import() | import() for CJS default export |
- When to use ESM — new Node projects, shared isomorphic code with browsers, top-level await in scripts.
- When CJS remains — legacy packages, some tooling configs, gradual migration paths.
cjs-vs-esm
// CommonJS (utils.cjs)
const path = require("node:path");
module.exports = { join: path.join };
// ES Module (utils.mjs)
import path from "node:path";
export const join = path.join;
// ESM importing CJS
import pkg from "legacy-cjs-package";
const { createRequire } = await import("node:module");
const require = createRequire(import.meta.url);