Menu

Node.js fs/promises: async/await File System API

fs/promises is the promise version of Node's fs module: readFile, writeFile, readdir, stat, mkdir, rm and rename all return promises you await, and errors are caught with try/catch.

This page includes runnable editors. Edit, run, and see output instantly.

fs/promises is the promise-based version of Node's fs module. Its functions (readFile, writeFile, readdir, stat, mkdir, rm, rename and the rest) return a promise instead of taking a callback, so you await them and catch errors with try/catch. Import it with require('fs/promises') or import fs from 'fs/promises'.

It prints [ 'write docs', 'ship it' ]: each await waits for one operation before the next line runs, so the append always happens after the first write. In an ES module await works at the top level; in CommonJS, wrap the code in an async function.

The functions you will use most

The names match the callback API, minus the callback. The readFile and writeFile pages cover those two in detail.

FunctionDoesResolves to
readFile(path, 'utf8')Reads a whole fileThe contents
writeFile(path, data)Creates or replaces a fileundefined
appendFile(path, data)Adds to the endundefined
readdir(path)Lists a folderAn array of names
stat(path)Size, dates, file or folderA Stats object
mkdir(path, { recursive: true })Creates foldersThe first folder created, or undefined
rename(from, to)Moves or renamesundefined
rm(path, { recursive: true, force: true })Deletes a file or folderundefined

List a folder with readdir and stat

readdir returns names only. Call stat on each one to get its size or to tell files from folders, or pass { withFileTypes: true } to get entries that already know their type.

The loop prints README.md 6 bytes, notes.txt 3 bytes and src folder. stat rejects with ENOENT for a path that does not exist, which makes it a quick existence check when you also want the details.

Rename, move and delete

rename both renames and moves a file (within the same drive). rm deletes; it needs recursive: true for a folder that has contents, and force: true to not fail when the path is already gone.

The file moves from drafts to published under its new name, and after rm the blog folder lists only published. Remove force: true from the second rm and it rejects with ENOENT.

Handle errors with try/catch

A failed operation rejects its promise. With await, that rejection becomes an exception you catch like any other. The error's code tells you what went wrong.

The first call finds no file and returns []; the second prints Ada. Codes to know: ENOENT (no such file), EEXIST (already exists), EISDIR (a folder where a file was expected), ENOTEMPTY (a folder still has contents) and EACCES (no permission).

A promise you do not await and do not .catch() becomes an unhandled rejection when it fails, and Node exits with an error. Await every call, or return its promise to a caller that does.

Read several files at once with Promise.all

A for loop with await reads files one after another. When the reads do not depend on each other, start them all and wait once with Promise.all: the disk works on them at the same time.

Promise.all keeps the order of the input array, so the result is [ 'alpha', 'beta', 'gamma' ] no matter which read finished first, and the joined length is 14. Rename 'b.txt' in the array to 'x.txt' and the whole await rejects with ENOENT.

If you need every result even when some reads fail, use Promise.allSettled; the promises page covers both.

Common mistakes

Mixing up the two modules. require('fs').readFile wants a callback; require('fs/promises').readFile returns a promise. Calling the callback version without a callback throws The "cb" argument must be of type function.

Forgetting await. const text = fs.readFile(...) is a pending promise, and text.split throws text.split is not a function.

Parallel writes to one file. Promise.all with several writes to the same path can leave mixed contents. Run those in sequence.

Frequently Asked Questions

What is the difference between fs.promises and fs/promises?

None. require('fs').promises and require('fs/promises') are the same object. The fs/promises path (or node:fs/promises) is the usual way to import it today.

Is there an existsSync in fs/promises?

No. The promise API has access, which rejects when the path is missing, but the usual pattern is to call the operation you need and catch ENOENT. existsSync from the plain fs module still works for quick checks.

Is fs/promises slower than the callback API?

Slightly, because of the promise overhead, but it is negligible next to disk time. Choose by readability; the promise API is the one most new code uses.

How do I turn a callback fs function into a promise?

You rarely need to, since fs/promises covers the file functions. For other callback APIs, util.promisify(fn) returns a version that returns a promise.

Coddy programming languages illustration

Learn to code with Coddy

Get started