fs — File System
The node:fs module reads and writes files. Prefer the promise API (fs/promises) over callbacks; stream large files; never block the event loop with *Sync on hot paths.
Read, write, streams, watch
EXAMPLE
import { readFile, writeFile, stat, mkdir, readdir, rm, rename, access, constants } from 'node:fs/promises';
import { createReadStream, createWriteStream, watch } from 'node:fs';
import path from 'node:path';
import { pipeline } from 'node:stream/promises';
// 1) Whole-file I/O
const body = await readFile('config.json', 'utf8');
await writeFile('out.txt', 'Hello\n');
// 2) JSON
const cfg = JSON.parse(await readFile('cfg.json', 'utf8'));
await writeFile('cfg.json', JSON.stringify(cfg, null, 2));
// 3) Check existence (don't race; just try the op)
try {
await access('foo.txt', constants.R_OK);
} catch {
// missing or not readable
}
// 4) Streams — for big files (gigabytes)
await pipeline(
createReadStream('big.log'),
createWriteStream('big.log.bak'),
);
// Gzip on the fly
import { createGzip } from 'node:zlib';
await pipeline(
createReadStream('big.log'),
createGzip(),
createWriteStream('big.log.gz'),
);
// 5) Directory ops
await mkdir('out/data', { recursive: true });
const entries = await readdir('out', { withFileTypes: true });
for (const e of entries) {
if (e.isDirectory()) console.log('dir', e.name);
if (e.isFile()) console.log('file', e.name);
}
// 6) Walk a tree (modern: fs.opendir / glob)
import { glob } from 'node:fs/promises';
for await (const file of glob('src/**/*.ts')) {
// process file
}
// 7) Atomic write (avoid half-written files on crash)
import { randomUUID } from 'node:crypto';
async function atomicWrite(target, data) {
const tmp = `${target}.${randomUUID()}.tmp`;
await writeFile(tmp, data);
await rename(tmp, target); // atomic on same filesystem
}
// 8) Stat + delete
const st = await stat('foo.txt');
console.log(st.size, st.mtime, st.isFile());
await rm('outdir', { recursive: true, force: true });
// 9) Watch — react to changes (debounce in practice)
watch('src', { recursive: true }, (eventType, filename) => {
console.log(eventType, filename);
});
// 10) Read line by line — for huge logs
import readline from 'node:readline';
const rl = readline.createInterface({ input: createReadStream('events.ndjson') });
for await (const line of rl) {
const evt = JSON.parse(line);
// ...
}
// 11) Avoid these in long-running servers
// readFileSync, writeFileSync, statSync, existsSync — block the event loop
// Fine in: scripts, CLI tools, app startup.
// 12) Symlinks + permissions
import { symlink, chmod, chown } from 'node:fs/promises';
await symlink('/etc/app/config.yml', './config.yml');
await chmod('out.txt', 0o644);
Why it matters
Reach for streams + pipeline() whenever the file size could be larger than RAM. readFile/writeFile are fine for configs, fatal for gigabyte logs.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
import { writeFile, readFile } from 'node:fs/promises';
await writeFile('hello.txt', 'hi');
console.log(await readFile('hello.txt', 'utf8'));
Try it Yourself »
Exercise
Promise-based fs lives under…
import { readFile } from 'node:fs/
';
Eight letters.
Discussion
Loading…