The File System (fs) Module
node:fs is how Node reads, writes, and manages files and directories. It's one of the oldest core modules, and it exposes the same operations in three distinct flavors — promises, callbacks, and synchronous — so the first thing to learn isn't a function, it's which API to reach for. Pick wrong and you'll either block your server or drown in callbacks.
Three APIs, one set of operations
API | Import | Shape | Use for |
|---|---|---|---|
Promises |
|
| Default — modern async code |
Callback |
|
| Legacy, or perf-critical streaming callbacks |
Sync |
|
| Scripts & startup only — it blocks |
// 1. Promises — preferred
import { readFile } from 'node:fs/promises'
const text = await readFile('a.txt', 'utf8')
// 2. Callback
import { readFile as readFileCb } from 'node:fs'
readFileCb('a.txt', 'utf8', (err, text) => { /* … */ })
// 3. Sync — blocks the event loop
import { readFileSync } from 'node:fs'
const text2 = readFileSync('a.txt', 'utf8')The common operations
Task | Function | Page |
|---|---|---|
Read a file |
| |
Write / overwrite |
| |
Append |
| |
Delete / rename |
| |
Directories |
| |
Metadata |
| |
Watch for changes |
|
Text vs binary
Pass an encoding (like 'utf8') and fs gives you a string. Omit it and you get a Buffer — raw bytes — which is what you want for images, archives, or any non-text data:
import { readFile } from 'node:fs/promises'
const text = await readFile('notes.txt', 'utf8') // → string
const bytes = await readFile('photo.jpg') // → Buffer (no encoding)
console.log(bytes.length, 'bytes')Always handle errors
import { readFile } from 'node:fs/promises'
try {
const text = await readFile('maybe-missing.txt', 'utf8')
console.log(text)
} catch (err) {
if (err.code === 'ENOENT') console.error('File not found')
else throw err // re-throw the unexpected
}