fsPromises.readFile()

Reads an entire file into a string or Buffer and resolves a promise with the contents.

Signature
fsPromises.readFile(path[, options])
  • Added in Node.js v10.0.0

Parameters

ParameterTypeDescription
$pathstring | Buffer | URL | FileHandleFilename to read, or an already-open FileHandle.
$optionsoptionalObject | stringOptions object, or a string that is taken as the encoding.
$options.encodingoptionalstring | null= nullCharacter encoding to decode the bytes with. With null you get a Buffer.
$options.signaloptionalAbortSignal= undefinedAllows aborting an in-progress readFile.

Return value

Promise<string | Buffer>Fulfills with the contents of the file: a Buffer when no encoding is given, otherwise a string.

Errors

Rejects with ENOENT if the path does not exist, EISDIR if it is a directory, EACCES on permission failures, and an AbortError if the supplied signal is aborted.

Changelog

  • PHP v15.14.0, v14.18.0Reading through a FileHandle and the surrounding promise APIs were broadened; a FileHandle passed as path is read from its current position, not from byte 0.
  • PHP v10.0.0Added in v10.0.0 as part of the require('node:fs/promises') surface.

fsPromises.readFile() is the promise form of the classic fs.readFile() callback function. Same syscalls, same options, but it resolves a promise instead of taking a callback, so it composes with await, try/catch and Promise.all().

When to reach for it

Use it whenever the whole file comfortably fits in memory and you want it in one shot: config files, JSON fixtures, templates, a Markdown post. It reads the file into a single allocation, so it is the wrong tool for multi-gigabyte input — use createReadStream() or a FileHandle for that.

What trips people up

  • Forgetting the encoding. With no encoding you get a Buffer, and JSON.parse() only appears to work on it because Buffers stringify. Pass 'utf8'.
  • Assuming the callback and promise versions come from the same import. require('node:fs') gives you the callback one; you want require('node:fs/promises').
  • Passing a FileHandle after already reading from it — the read starts at the handle's current position, so you get an empty string.

Errors reject rather than arriving as a first argument, so an un-awaited call produces an unhandled rejection instead of a visible crash.

Example

Try it yourself
Loading editor…
Press Run to execute the code.

When to use it

  • Load a JSON config file at startup and parse it.
  • Read a template or Markdown file before rendering it.
  • Slurp a small binary file as a Buffer to hash or inspect it.
  • Read many small files concurrently with Promise.all().

Frequently asked questions

Why does fsPromises.readFile return a Buffer instead of a string?

Because no encoding was passed — the default is null, which means raw bytes. Call fs.readFile(path, 'utf8') or fs.readFile(path, { encoding: 'utf8' }) to get a string back.

What is the difference between fs.readFile and fsPromises.readFile?

They do the same work; fs.readFile (from node:fs) takes an error-first callback, while fsPromises.readFile (from node:fs/promises) returns a promise you can await. There is no performance difference worth choosing between.

How do I read a large file without loading it all into memory?

Do not use readFile — it allocates the whole file at once. Use fs.createReadStream() from node:fs, or open a FileHandle with fsPromises.open() and iterate handle.readLines() or handle.createReadStream().

More examples

Read and parse JSON

Prints ada, then true 1. The encoding argument is what makes JSON.parse() safe here; drop it and you are parsing a Buffer's stringified form by accident.

Example · javascript
import fs from 'node:fs/promises';

await fs.writeFile('users.json', JSON.stringify([{ id: 1, name: 'ada' }]));

const raw = await fs.readFile('users.json', 'utf8');
const users = JSON.parse(raw);
console.log(users[0].name);
console.log(Array.isArray(users), users.length);

await fs.rm('users.json');

Failure modes: missing file, directory, aborted read

Prints missing: ENOENT, directory: EISDIR, aborted: AbortError ABORT_ERR. Each rejection carries a machine-readable code, which is what you branch on rather than matching the message text.

Example · javascript
import fs from 'node:fs/promises';

await fs.writeFile('big.txt', 'x'.repeat(50));

try {
  await fs.readFile('does-not-exist.txt', 'utf8');
} catch (err) {
  console.log('missing:', err.code);
}

try {
  await fs.readFile('.', 'utf8');
} catch (err) {
  console.log('directory:', err.code);
}

const ac = new AbortController();
ac.abort();
try {
  await fs.readFile('big.txt', { signal: ac.signal });
} catch (err) {
  console.log('aborted:', err.name, err.code);
}

await fs.rm('big.txt');

Read several files concurrently

Prints [ 'file 0', 'file 1', 'file 2' ]. Promise.all() keeps the results in the order of the input array even though the reads finish in whatever order the filesystem returns them.

Example · javascript
import fs from 'node:fs/promises';

const names = ['a.txt', 'b.txt', 'c.txt'];
await Promise.all(names.map((n, i) => fs.writeFile(n, `file ${i}`)));

const contents = await Promise.all(names.map((n) => fs.readFile(n, 'utf8')));
console.log(contents);

await Promise.all(names.map((n) => fs.rm(n)));

Discussion

  • Be the first to comment on this lesson.