Menu

Node.js fs.readFile: Read Files (Sync, Async, Promises)

Read a file in Node.js with fs.readFile (callback), fs.readFileSync or await readFile from fs/promises. Pass 'utf8' to get a string, handle ENOENT when the file is missing.

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

fs.readFile(path, 'utf8', callback) reads a whole file and passes its contents to the callback as a string. fs.readFileSync(path, 'utf8') returns the string directly and waits for the read to finish, and await readFile(path, 'utf8') from fs/promises does the same without blocking. Leave out 'utf8' and you get a Buffer of raw bytes instead of text.

readFile called prints first: readFile only starts the read, and the callback runs once the contents are ready. Delete 'utf8', and run it again: the callback gets <Buffer 42 75 79 20 6d 69 6c 6b 0a 43 61 6c 6c 20 41 64 61>, the raw bytes of the file.

readFile vs readFileSync vs fs/promises

All three read the same file. They differ in how you get the result and whether the rest of the program waits.

The sync line prints first because nothing else runs until it returns. The other two finish after the main code, in whichever order their reads complete.

readFileSyncreadFile (callback)fs/promises readFile
ReturnsThe contentsundefinedA promise of the contents
Blocks the programYesNoNo
ErrorsThrown, use try/catchFirst callback argumentRejected promise, try/catch with await
Use it inStartup code and scriptsOlder callback codeNew code

Use readFileSync when reading a config at startup or in a short script. In a server, a sync read stops every other request while the disk works, so use the promise version there. The fs/promises page covers the rest of that API.

Handle a missing file (ENOENT)

When the file does not exist, the error has code: 'ENOENT' ("error, no entry"). Check err.code to tell a missing file apart from other failures, like a permission problem.

The first call finds no file and falls back to default settings; the second reads dark mode. Rethrowing the other errors keeps real problems visible instead of quietly using defaults.

fs.existsSync: why to just try the read

fs.existsSync(path) returns true or false, and it is tempting to check before reading:

if (fs.existsSync('data.txt')) {
    const text = fs.readFileSync('data.txt', 'utf8');
}

That is two steps where one is enough, and the file can be deleted between them, so the read can still throw. Try the read and handle ENOENT, as above. existsSync is fine when you only need to know whether something exists and will not open it, for example to decide whether to create a folder.

Read a JSON file

Read the text, then parse it with JSON.parse. A file with a syntax error throws a SyntaxError, so keep it inside the same try.

It prints 3001 and boolean: after parsing, the values have their JSON types. require('./config.json') also works, but it caches the result, so a second require returns the old object even after the file changes.

Read a file line by line

For a file that fits in memory, read it whole and split on the newline character. Filter out the empty string that a final newline leaves behind.

The split gives 4 items, the last one empty, so the loop prints three numbered lines. Files written on Windows end lines with \r\n; split on /\r?\n/ to handle both.

A log of several gigabytes does not fit in memory. Stream it instead, so only one line at a time is held:

const fs = require('fs');
const readline = require('readline');

async function main() {
    const rl = readline.createInterface({
        input: fs.createReadStream('big.log'),
        crlfDelay: Infinity,
    });
    for await (const line of rl) {
        console.log(line);
    }
}
main();

Common mistakes

Using the result outside the callback. const text = fs.readFile(...) is undefined. The contents exist only inside the callback, or after await.

Forgetting the encoding. Without 'utf8' you get a Buffer, and data.split('\n') throws data.split is not a function. Pass the encoding whenever you want text.

Paths relative to the wrong folder. 'data.txt' is looked up where node was started. Use path.join(__dirname, 'data.txt') for a file that ships next to your script.

Frequently Asked Questions

Why does fs.readFile return undefined?

fs.readFile does not return the contents. It starts the read and calls your callback later with (err, data). Use the data inside the callback, call readFileSync, or await the promise version.

Where does Node look for a relative path like 'data.txt'?

In the current working directory, the folder you ran node from, not the folder of the script. To read a file next to the script, build the path with path.join(__dirname, 'data.txt').

Can fs.readFile read a file from a URL?

No, it reads from the file system only. To get a file over HTTP, use fetch and then await res.text().

How big a file can readFile handle?

It loads the whole file into memory, so it is fine for configs, templates and small data files. For files of hundreds of megabytes, read them in pieces with fs.createReadStream.

Coddy programming languages illustration

Learn to code with Coddy

Get started