Read, write, and manage files. Use fs/promises, build safe paths with the path module, understand __dirname vs cwd, and reach for fs-extra, globby, and chokidar.
Why: the built-in "fs" (file system) module reads and writes files. Use the promises version (node:fs/promises) so you can use async/await. Pass "utf8" to get text instead of raw bytes.
// app.js
import { readFile, writeFile } from 'node:fs/promises'
// write text to a file (creates or overwrites it)
await writeFile('hello.txt', 'Hi there!', 'utf8')
// read it back
const text = await readFile('hello.txt', 'utf8')
console.log(text) // Hi there!Why: Windows uses \ and Mac/Linux use / in file paths. The "path" module joins paths correctly on every operating system, so never glue paths together by hand with string + "/".
// app.js
import path from 'node:path'
const full = path.join('folder', 'sub', 'file.txt')
console.log(full) // folder/sub/file.txt (or folder\sub\file.txt on Windows)
console.log(path.extname('photo.png')) // .png
console.log(path.basename('/a/b/c.txt')) // c.txtWhy: process.cwd() is the folder you were in when you typed the command — it changes.
// app.js
// where the user ran "node" from
console.log(process.cwd())Why: beyond reading files you often need to list a folder, create one, or check whether something exists. mkdir with recursive:true creates nested folders in one call.
// app.js
import { mkdir, readdir, stat } from 'node:fs/promises'
await mkdir('logs/2024', { recursive: true }) // make nested folders
const files = await readdir('.') // list current folder
console.log(files)
const info = await stat('hello.txt')
console.log(info.size, 'bytes', info.isDirectory())Why: fs-extra is a popular package that adds convenient one-liners the built-in fs lacks, like copying whole folders or removing a directory and its contents safely.
$ pnpm add fs-extra// app.js
import fs from 'fs-extra'
await fs.copy('src', 'backup') // copy a whole folder
await fs.remove('temp') // delete a folder and everything in it
await fs.ensureDir('cache') // create folder if it does not existWhy: a "glob" is a pattern with wildcards, like **/*.js meaning "every .js file in any subfolder". globby finds all files matching such patterns — great for build scripts.
$ pnpm add globby// app.js
import { globby } from 'globby'
// every .js file under src/, at any depth
const files = await globby('src/**/*.js')
console.log(files)Why: chokidar watches files and folders and tells you when they change, are added, or deleted — the engine behind many "live reload" tools. More reliable across operating systems than the built-in watcher.
$ pnpm add chokidar// app.js
import chokidar from 'chokidar'
chokidar.watch('src').on('change', (file) => {
console.log(file, 'changed — rebuild!')
})