Working with Files
Node can read and write files on your machine. The modern API lives in node:fs/promises and returns promises you can await.
Reading a File
import { readFile } from "node:fs/promises";
const text = await readFile("notes.txt", "utf8");
console.log(text);
Always pass an encoding like "utf8". Without it you get a Buffer of raw bytes.
Writing a File
import { writeFile } from "node:fs/promises";
await writeFile("out.txt", "Hello, file!", "utf8");
writeFile replaces the whole file. To append instead:
import { appendFile } from "node:fs/promises";
await appendFile("log.txt", "new line\n");
Checking and Removing
import { access, mkdir, rm } from "node:fs/promises";
await access("config.json"); // throws if missing
await mkdir("data", { recursive: true }); // create nested dirs safely
await rm("old.txt", { force: true }); // delete, ignore if absent
Building Paths
Never join paths with "/" — Windows uses "\\". Use node:path:
import path from "node:path";
const file = path.join("content", "posts", "hello.md");
const abs = path.resolve(file);
import.meta.dirname gives the current file’s folder in ESM:
const data = path.join(import.meta.dirname, "data.json");
Handling Errors
File operations can fail. Catch and handle:
import { readFile } from "node:fs/promises";
try {
const config = await readFile("config.json", "utf8");
console.log(JSON.parse(config));
} catch (err) {
if (err.code === "ENOENT") {
console.error("config.json not found");
} else {
throw err;
}
}
Big Files: Streams
readFile loads everything into memory. For large files, stream them:
import { createReadStream } from "node:fs";
const stream = createReadStream("big.log");
stream.on("data", (chunk) => process.stdout.write(chunk));
Best Practices
- Use
node:fs/promises— Avoid callback-stylefs. - Use
node:path— Cross-platform paths, always. - Set an encoding —
"utf8"for text. - Stream large files — Do not buffer gigabytes in memory.
Common Mistakes
- Assuming the file exists — Wrap in
try/catch. - String-concatenating paths — Breaks on Windows.
- Forgetting
await— You get a pending Promise, not the content. - Using relative paths carelessly — They resolve from the process’s working directory.