Q&A
How to import and export using the module object in Node.js
Overview
Node.js wraps every file in a module so its variables don't leak globally. The module object represents the current module, and its exports property is what other files receive when they require() your file. Understanding module.exports vs the exports shorthand - and how require consumes them - is core Node knowledge.
graph LR
A[file: math.js] -->|module.exports = {...}| B[exported value]
B -->|require('./math')| C[file: app.js]
C --> D[uses the imported functions]
style B fill:#1e1b4b,color:#fff
Exporting (CommonJS)
The module.exports object is what gets returned by require. Assign to it whatever you want to expose:
Export multiple values (object)
// math.js
function add(a, b) { return a + b; }
function subtract(a, b) { return a - b; }
module.exports = { add, subtract }; // export an object of functions
Export a single value (function/class)
// logger.js
module.exports = function log(msg) {
console.log(`[LOG] ${msg}`);
};
The exports shorthand
exports is just a reference to module.exports. You can add properties to it:
// utils.js
exports.greet = name => `Hello, ${name}`;
exports.bye = name => `Bye, ${name}`;
But reassigning exports directly does not work - it breaks the reference:
exports = function () {}; // ❌ does NOT export (exports now points elsewhere)
module.exports = function () {}; // ✓ this is the correct way to export a single thing
Rule of thumb: use exports.foo = ... to add properties, but use module.exports = ... to replace the whole export.
Importing with require()
// app.js
const math = require("./math"); // local file (note ./)
console.log(math.add(2, 3)); // 5
const { add, subtract } = require("./math"); // destructure specific exports
console.log(subtract(5, 2)); // 3
const log = require("./logger"); // single-export module
log("started");
const fs = require("fs"); // built-in core module (no ./)
const express = require("express"); // npm package (from node_modules)
require resolves: core modules by name, npm packages from node_modules, and your files by relative/absolute path (./, ../).
How require Works
- It loads and executes the target file once, then caches the result. Subsequent
requires of the same module return the cachedmodule.exports(the file isn't re-run). - It's synchronous - the module is fully loaded before
requirereturns. - The cache means a module's top-level code runs a single time per process, which is useful for singletons (e.g. a shared DB connection).
ES Modules (the modern alternative)
Node also supports ES Module syntax (in .mjs files or with "type": "module" in package.json):
// math.mjs
export function add(a, b) { return a + b; }
export default function multiply(a, b) { return a * b; }
// app.mjs
import multiply, { add } from "./math.mjs";
ESM uses import/export (static, asynchronous-friendly) instead of module.exports/require. You generally shouldn't mix the two styles in one file.
Why This Matters in Interviews
Node interviews check whether you understand the module system. Key points: module.exports is the exported object and require returns it; the exports shorthand adds properties but can't be reassigned; require is synchronous and caches modules (so top-level code runs once - a common singleton mechanism); and how require resolves core vs npm vs local paths. Knowing the CommonJS-vs-ESM distinction (require vs import) demonstrates current awareness.
Common Pitfalls
- Reassigning
exports = ...and finding nothing is exported (usemodule.exports = ...). - Forgetting
./for local files (require("math")looks in node_modules, not your folder). - Expecting code to re-run on each
require(it's cached - runs once). - Mixing
requireandimportin the same file or misconfiguring"type": "module". - Circular
requires returning a partially-populatedexports.
References
- Node.js Modules (CommonJS): https://nodejs.org/api/modules.html
- Node.js ECMAScript Modules: https://nodejs.org/api/esm.html