Files and paths in Node

Async file I/O with promises, streams for large data, and building paths that work on every platform.

Reading and writing

import { readFile, writeFile, mkdir } from "node:fs/promises";
import path from "node:path";

const file = path.join(process.cwd(), "data", "config.json");

const text = await readFile(file, "utf8");
const config = JSON.parse(text);

await mkdir(path.dirname(file), { recursive: true });
await writeFile(file, JSON.stringify(config, null, 2), "utf8");
⚠️
Avoid the *Sync variants in a server. They block the event loop for every request for the duration of the disk call, which is precisely what Node is designed to avoid.

Paths done right

import path from "node:path";
import { fileURLToPath } from "node:url";

const here = path.dirname(fileURLToPath(import.meta.url));
const target = path.join(here, "..", "public", "index.html");

path.extname(target);        // '.html'
path.basename(target);       // 'index.html'
path.resolve("a", "b");      // absolute
path.normalize("a/../b");    // 'b'
  • Use path.join rather than string concatenation — it inserts the right separator per platform.
  • In ES modules there is no __dirname; derive it from import.meta.url.
  • Never build a filesystem path from raw user input without validating it — that is path traversal.

Streams for large data

Reading a multi-gigabyte file into memory with readFile will exhaust the heap. Streams process it in chunks with constant memory.

import { createReadStream, createWriteStream } from "node:fs";
import { pipeline } from "node:stream/promises";
import { createGzip } from "node:zlib";

await pipeline(
  createReadStream("access.log"),
  createGzip(),
  createWriteStream("access.log.gz")
);
💡
pipeline propagates errors and destroys every stream on failure — the modern replacement for hand-wired .pipe() chains that leak on error.

FAQ

Why is my write not finished when the program exits?
You did not await it. File operations are asynchronous; await the promise or the stream's finish event.
How do I watch a file for changes?
fs.watch for simple cases, or a polling approach in development containers where inotify events are unreliable.

Node.js: getting started Building an HTTP server

Last refreshed 2026-09-17.