Node.js Modules & File System: The Power of Organization
In Node.js, your code isn't just a single giant script. It's an ecosystem of specialized "Modules." These modules allow you to organize your logic into reusable parts and access the powerful features of the operating system—most importantly, the File System.
This chapter explores how to structure your applications using the module system and how to read, write, and manage files safely and efficiently.
Why This Topic Matters
Mastering modules and the file system is what turns a JavaScript coder into a Systems Developer. It allows you to:
- Organize Large Codebases: Break 10,000 lines of code into 50 logical files.
- Manage Persistence: Save user data, logs, or configuration settings directly to the hard drive.
- Build Tools: Create CLI utilities that process local files (like a custom Markdown converter).
- Control the Environment: Detect CPU count, memory, and OS type to optimize your server.
The Module System: CommonJS vs. ES Modules
Node.js supports two ways of sharing code between files.
1. CommonJS (Legacy/Default)
Uses require() and module.exports. It is synchronous and was the only option for many years.
// math.cjs
module.exports.add = (a, b) => a + b;
// app.cjs
const { add } = require("./math.cjs");
2. ES Modules (Modern Standard)
Uses import and export. It is the same standard used in the browser. To use it in Node, set "type": "module" in your package.json.
// math.js
export const add = (a, b) => a + b;
// app.js
import { add } from "./math.js";
Rule of Thumb: Prefer ES Modules for all new projects.
The path Module: Cross-Platform Safety
Never manually concatenate file paths like folder + "/" + file. Windows uses backslashes (\), while macOS/Linux use forward slashes (/). The path module handles this for you.
import path from "path";
// Joins parts using the correct separator for the OS
const fullPath = path.join("data", "users", "profile.json");
console.log(fullPath); // "data/users/profile.json" (on Mac/Linux)
// Gets the file extension
console.log(path.extname(fullPath)); // ".json"
The fs Module: Interacting with the Disk
The fs (File System) module provides three ways to perform operations. Choosing the right one is critical for performance.
1. Synchronous (readFileSync)
Blocks the entire program until the file is read. Use only for simple scripts or during startup.
2. Asynchronous / Promises (fs/promises)
The modern standard. It doesn't block the program, allowing the server to handle other requests while the disk is busy.
Practical Example: Reading and Writing
import fs from "fs/promises";
async function manageConfig() {
try {
// Reading
const data = await fs.readFile("config.json", "utf-8");
const config = JSON.parse(data);
// Modifying
config.lastModified = new Date().toISOString();
// Writing
await fs.writeFile("config.json", JSON.stringify(config, null, 2));
console.log("Config updated!");
} catch (error) {
console.error("File error:", error.message);
}
}
Environmental Awareness with os
The os module lets your code know what kind of machine it is running on.
os.platform(): "darwin" (Mac), "win32" (Windows), "linux".os.cpus(): Get info about every CPU core (useful for scaling).os.freemem(): Check available RAM before starting heavy tasks.
Common Mistakes & Pitfalls
- Forgetting Encoding: Reading a file without
"utf-8"returns a Buffer (raw bytes) instead of a string. - Relative Path Confusion:
fs.readFile("./data.txt")uses the directory where you ran the command (process.cwd()), not where the script is located. (Useimport.meta.urlor__dirnameto fix). - Blocking the Event Loop: Using
fs.writeFileSyncinside a high-traffic web server. This makes the server unresponsive for all other users. - Permissions: Forgetting that Node might not have permission to write to certain system folders.
Mini Exercises
- The Logger: Create a script that appends the current timestamp to a file named
log.txtevery time it is run. Usefs.appendFile. - The Metadata Tool: Write a script that takes a file path as a command-line argument and logs its size and extension.
- Cross-Platform Path: Use
path.jointo create a path forassets/icons/home.svgand log it. - System Check: Create a script that logs "Safe to run" if the free memory is greater than 1GB, and "Warning: Low memory" otherwise.
- Module Refactor: Take a project using
requireand refactor it to useimport/export.
Review Questions
- What is the main difference between CommonJS and ES Modules?
- Why is it better to use
fs/promisesinstead of synchronousfsmethods in a server? - What happens if you don't specify an encoding like
"utf-8"when reading a text file? - How does the
pathmodule help with writing cross-platform code? - What is the
node_modulesfolder, and why should it be ignored in Git?
Reference Checklist
- I can export and import code using ES Modules.
- I understand how to use
path.joinfor OS-safe file paths. - I can read and write files using
async/await. - I know how to check if a file exists before trying to read it.
- I understand why
utf-8is necessary for text files. - I can use the
osmodule to inspect the host machine.