Node historically used CommonJS: require() loads modules synchronously; module.exports defines what they expose. Millions of npm packages still use this style.
Export and import (CommonJS)
// greeter.cjs
module.exports = function greet(name) {
return `Hello, ${name}`;
};
// app.cjs
const greet = require('./greeter.cjs');
console.log(greet('Ada'));
Built-in require
const path = require('node:path');
const fs = require('node:fs');
No import at top level in pure CommonJS files unless using dynamic import().
Playground note
Runnable snippets use ESM. To compare CommonJS in the runner, use createRequire from node:module (see editor code).
Important interview questions and answers
- Q: require vs import?
A:requireis sync CommonJS;importis ESM (static, async loading at graph build). Node supports both with configuration. - Q: __dirname in ESM?
A: Not defined—derive fromimport.meta.urlwithfileURLToPathandpath.dirname.
Self-check
- What does
module.exportsdo? - Why is
requiresynchronous?
Pitfall: Mixing require and top-level await in the same file causes confusion—pick ESM (import) or CommonJS (require) per file in new projects.