devnotes.

Writing Your Own Custom Modules

Core and third-party modules cover code written by others. Custom modules are the code you write, split across files, and wire back together — the basic building block of any real application's structure.

Exporting from a file

There are two common patterns. The first, exporting a single object with multiple functions:

// inventory.js
function addStock(current, amount) {
  return current + amount;
}

function removeStock(current, amount) {
  if (amount > current) throw new Error('Not enough stock');
  return current - amount;
}

module.exports = { addStock, removeStock };
// app.js
const inventory = require('./inventory');
console.log(inventory.addStock(50, 10)); // 60

The second pattern exports a single value directly — useful when a file really only does one thing:

// generateId.js
let counter = 0;

module.exports = function generateId() {
  counter += 1;
  return `ID-${counter}`;
};
// app.js
const generateId = require('./generateId');
console.log(generateId()); // ID-1
console.log(generateId()); // ID-2

Notice counter keeps its value between calls — Node.js caches a module after its first require(), so every file that requires generateId.js shares the exact same instance rather than getting a fresh copy.

Organizing a small project into modules

A typical small app splits naturally along responsibility lines:

project/
  routes/
    users.js
    orders.js
  services/
    email.js
    payment.js
  utils/
    formatDate.js
  app.js
// app.js
const users = require('./routes/users');
const orders = require('./routes/orders');

Each file stays focused on one responsibility, and app.js becomes a short, readable summary of how the pieces connect, rather than a dumping ground for every function the app needs.

A subtlety worth knowing: relative paths

require('./file') looks in the same folder as the file doing the requiring. require('../file') looks one folder up. Getting this wrong is one of the most common early mistakes — if you move a file to a different folder, every relative require() pointing to it (and every require() inside it pointing elsewhere) needs re-checking.

Why bother splitting things up at all

A single 2,000-line file and the same logic split across ten well-named 200-line files run identically — Node.js doesn't care. The difference is entirely for the humans reading it later: finding a bug, onboarding a new developer, or testing one piece in isolation are all dramatically easier when related code lives together in a clearly-named module, and unrelated code doesn't.

Writing Your Own Custom Modules | DevNotes