fsPromises.readFile()
Reads an entire file into a string or Buffer and resolves a promise with the contents.
fsPromises.readFile(path[, options])- Added in Node.js v10.0.0
Parameters
| Parameter | Type | Description |
|---|---|---|
$path | string | Buffer | URL | FileHandle | Filename to read, or an already-open FileHandle. |
$optionsoptional | Object | string | Options object, or a string that is taken as the encoding. |
$options.encodingoptional | string | null= null | Character encoding to decode the bytes with. With null you get a Buffer. |
$options.signaloptional | AbortSignal= undefined | Allows 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
encodingyou get aBuffer, andJSON.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 wantrequire('node:fs/promises'). - Passing a
FileHandleafter 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
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.
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.
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.
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