The file-system module

Reference for node:fs and node:fs/promises in Cruft: reading and writing files, stat and permissions, directories and glob, watching, and fd-level I/O, in synchronous, callback, and promise forms. States which methods are real operations and which are shape-only stubs. All access is capability-gated.

Filesystem access in Cruft, exposed the way Node exposes it: synchronous (…Sync), Node-style callback, and promise (node:fs/promises) forms of the same operations. Per the compatibility map this module is at the focused depth tier: read/write, stat, readdir, glob, watch, and fd-level I/O are implemented as real operations; binary reads return a real Node Buffer (ArrayBuffer.isView is true, instanceof Uint8Array is true, and constructor.name is "Buffer"). Errors on the ordinary missing-file path carry Node-style .code (for example ENOENT), plus errno, syscall, and path where applicable. All operations are capability-gated through the runtime's capability layer. This page enumerates what is actually installed; anything not listed here is not present.

Import forms

All four specifiers resolve, in both module systems:

// ESM
import fs from "node:fs";
import fs from "fs";
import { readFile } from "node:fs/promises";
import { readFile } from "fs/promises";

// CJS
const fs = require("node:fs");        // or require("fs")
const fsp = require("node:fs/promises"); // or require("fs/promises")

node:fs/promises is a distinct namespace (the promise API, not all of fs); it is also reachable as fs.promises.

Reading and writing files

FunctionSemantics
readFileSync(path[, options])Read a file. With an encoding, returns a string; without, returns a real Node Buffer (instanceof Uint8Array is true, constructor.name is "Buffer").
readFile(path[, options], cb)Callback form; (err, data).
writeFileSync(path, data[, options])Write a file (string or bytes).
writeFile(path, data[, options], cb)Callback form.
appendFileSync(path, data[, options]) / appendFile(path, data, cb)Append.
copyFileSync(src, dest[, mode]) / copyFile(src, dest, cb)Copy one file.
cpSync(src, dest[, options]) / cp(src, dest, cb)Copy; { recursive: true } copies a directory tree.
truncateSync(path[, len]) / truncate(path, cb)Truncate a file.
renameSync(old, new) / rename(old, new, cb)Rename/move.
openAsBlob(path)Promise of a Blob-shaped object: size, type, text(), arrayBuffer(), bytes().
createReadStream(path[, options])Event-emitter read stream: on/once, data/end/error events, pipe, close, destroy, pause, resume, bytesRead.
createWriteStream(path[, options])Event-emitter write stream: write, end, destroy, bytesWritten, open/finish/drain events.

Round-trip:

import fs from "node:fs";
fs.writeFileSync("/tmp/fsdemo/hello.txt", "hello cruft\n");
console.log(JSON.stringify(fs.readFileSync("/tmp/fsdemo/hello.txt", "utf8")));
$ cruft /tmp/demo.mjs
"hello cruft\n"

Binary reads return a real Node Buffer:

import fs from "node:fs";
const buf = fs.readFileSync("/tmp/fsdemo/hello.txt");
console.log(buf.constructor.name);
console.log(buf instanceof Uint8Array);
console.log(ArrayBuffer.isView(buf));
console.log(Array.from(buf.slice(0, 5)));
$ cruft /tmp/demo.mjs
Buffer
true
true
[ 104, 101, 108, 108, 111 ]

Metadata and permissions

FunctionSemantics
statSync(path[, options]) / stat(path, cb)Stats object: size, mode, uid, gid, mtimeMs, atimeMs, ctimeMs, birthtimeMs, date fields, and isFile() / isDirectory() / isSymbolicLink() predicates. isBlockDevice/isCharacterDevice/isFIFO/isSocket are present and return false.
lstatSync(path) / lstat(path, cb)Stat without following symlinks.
fstatSync(fd) / fstat(fd, cb)Stat an open fd.
statfsSync(path) / statfs(path, cb)Filesystem stats (fixed-shape object: type, bsize, blocks, bfree, bavail, files, ffree).
existsSync(path) / exists(path, cb)Existence check.
accessSync(path[, mode]) / access(path, cb)Throws/errors if not accessible.
chmodSync / chmod, fchmodSync / fchmod, lchmodSync / lchmodMode changes (path, fd, no-follow).
chownSync / lchownSync / fchownSync (+ chown, lchown, fchown callback forms)Ownership changes; the callback forms wrap the …Sync impls (their callbacks fire with real results/errors).
utimesSync / utimes, lutimesSync / lutimes, futimesSync / futimesTimestamps.
realpathSync(path) / realpath(path, cb)Canonicalize; both carry a .native sub-property as in Node.
readlinkSync(path) / readlink(path, cb)Read a symlink target.
linkSync / link, symlinkSync / symlinkHard/symbolic links.
constantsPOSIX constants: F_OK/R_OK/W_OK/X_OK, O_* open flags, S_IF* and S_I* mode bits, COPYFILE_*, UV_DIRENT_*.
import fs from "node:fs";
const st = fs.statSync("/tmp/fsdemo/hello.txt");
console.log(st.size, st.isFile(), st.isDirectory());
$ cruft /tmp/demo.mjs
12 true false

Directories and glob

FunctionSemantics
mkdirSync(path[, options]) / mkdir(path, cb)Make a directory; { recursive: true } makes the whole path.
readdirSync(path[, options]) / readdir(path, cb)List names; { withFileTypes: true } yields Dirent objects with name and type predicates.
rmSync(path[, options]) / rm(path, cb)Remove; { recursive: true } removes a tree.
rmdirSync(path) / rmdir(path, cb)Remove an empty directory.
unlinkSync(path) / unlink(path, cb)Remove a file.
mkdtempSync(prefix) / mkdtemp(prefix, cb)Temporary directory. mkdtempDisposableSync / mkdtempDisposable also present.
opendirSync(path) / opendir(path, cb)Dir object with a read() cursor over Dirents.
globSync(pattern) / glob(pattern, cb)Shell-pattern match, resolved against and returned relative to the cwd; * stays within a segment, ** crosses segments.
import fs from "node:fs";
// cwd is /tmp/fsdemo
console.log(fs.globSync("*.txt").sort());
console.log(fs.globSync("**/*.txt").sort());
$ cruft /tmp/demo.mjs
[ 'h2.txt', 'hello.txt', 'p.txt', 'w.txt' ]
[ 'h2.txt', 'hello.txt', 'p.txt', 'sub/a.txt', 'sub/b.txt', 'w.txt' ]

Watching

FunctionSemantics
watch(path[, options][, listener])Returns a watcher object with close, on, ref, unref. Changes are detected by mtime/size polling and delivered as macrotasks.
watchFile(path[, options], listener)Stat-polling variant.
unwatchFile(path)Deregisters watchers for a path.
import fs from "node:fs";
const w = fs.watch("/tmp/fsdemo");
console.log(typeof w.close, typeof w.on, typeof w.ref);
w.close();
$ cruft /tmp/demo.mjs
function function function

fd-level I/O

FunctionSemantics
openSync(path[, flags[, mode]]) / open(path, cb)Integer fd into the runtime's fd table.
readSync(fd, buffer, offset, length, position) / read(fd, …, cb)Fill a buffer from an fd; returns bytes read.
writeSync(fd, buffer, …) / write(fd, …, cb)Write via an fd.
readvSync(fd, buffers) / writevSync(fd, buffers)Scatter-gather I/O; sync, top-level callback (readv/writev), and promise forms all work (callbacks wrap the …Sync impls).
closeSync(fd) / close(fd, cb)Release the fd.
fsyncSync / fsync, fdatasyncSync / fdatasync, ftruncateSync / ftruncatefd-level sync/truncate.
import fs from "node:fs";
const fd = fs.openSync("/tmp/fsdemo/hello.txt", "r");
const buf = new Uint8Array(5);
const n = fs.readSync(fd, buf, 0, 5, 0);
fs.closeSync(fd);
console.log(n, Array.from(buf));
$ cruft /tmp/demo.mjs
5 [ 104, 101, 108, 108, 111 ]

Promises API (node:fs/promises)

Every callback-form method above is mirrored as a promise on fs/promises (and fs.promises), plus constants:

Promise methods
accessappendFilechmodchownclosecopyFilecpfdatasyncfchmodfchownfsyncfstatftruncatefutimeslchmodlchownlinklstatlutimesmkdirmkdtempmkdtempDisposableopendirreadreadFilereaddirreadlinkreadvrealpathrenamermrmdirstatstatfssymlinktruncateunlinkutimeswritewriteFilewritevglob
constants

Differences from Node worth knowing:

  • open(path, flags) resolves a minimal FileHandle: fd, stat(), close(). Deeper FileHandle methods (read, write, readFile, createReadStream, …) are not on the handle.
  • glob(pattern) returns an async iterator (Node-faithful): iterate it with for await (… of glob(…)); it is not an array, so Array.isArray is false and it has no Symbol.iterator.
  • promises.watch returns an event-emitter watcher (close, on, off, ref, unref) that holds the loop open — not an async iterator (no Symbol.asyncIterator), and not undefined. Its ref/unref are no-ops: the watcher keeps the loop alive until you close() it, unref notwithstanding.
import { readFile, writeFile, glob } from "node:fs/promises";
await writeFile("/tmp/fsdemo/p.txt", "promise data\n");
console.log(JSON.stringify(await readFile("/tmp/fsdemo/p.txt", "utf8")));
const matches = [];
for await (const m of glob("*.txt")) matches.push(m); // async iterator, cwd /tmp/fsdemo
console.log(matches.includes("p.txt"));
$ cruft /tmp/demo.mjs
"promise data\n"
true

Not implemented / gaps

This list reflects the installed API surface. Unlisted APIs are not present. Do not assume Node parity beyond what this page documents.

  • Callback wrappers over the …Sync impls: top-level chown, lchmod, lchown, readv, and writev callback forms are functional (their callbacks fire with real results/errors) rather than throw-on-call stubs.
  • Class stubs: Stats, Dirent, Dir are present but not constructable (constructor throws). ReadStream, WriteStream, FileReadStream, FileWriteStream, Utf8Stream exist as shape-only constructor stubs; use createReadStream / createWriteStream for real streams.
  • FileHandle is minimal: fd, stat(), close() only (see above).
  • No BigInt stats: { bigint: true } options are not honored; stats fields are numbers.
  • No fs.Dirent recursion options beyond what is documented; readdirSync { recursive: true } behavior beyond names/Dirents should be probed before relying on it.
  • promises.watch returns an event-emitter watcher (close/on/off/ref/unref) with no async-iterator support, not undefined.
  • Device/FIFO/socket stat predicates always return false.
  • openAsBlob reports an empty type ("").