fs.readFileSync()
Reads a whole file and returns its contents, blocking the event loop until done.
fs.readFileSync(path[, options])- Added in Node.js v0.1.8
Parameters
| Parameter | Type | Description |
|---|---|---|
$path | <string> | <Buffer> | <URL> | <integer> | Filename or file descriptor. Relative paths resolve against process.cwd(). |
$optionsoptional | <Object> | <string>= { encoding: null, flag: 'r' } | A string is treated as the encoding. As an object: encoding <string>|<null> (default null), flag <string> (default 'r'), and buffer <Buffer>|<TypedArray>|<DataView>|<Function> to read into a caller-supplied buffer (added in v26.4.0). |
Return value
string | BufferThe complete contents of the file: a string when an encoding is given, otherwise a Buffer.
Errors
Throws a system error such as ENOENT when the file does not exist, EACCES when permissions deny it, or EISDIR when the path is a directory. Wrap the call in try/catch and inspect err.code.
Changelog
- PHP v26.4.0Added support for the buffer option, allowing reads into a buffer you already allocated.
- PHP v7.6.0path may be a WHATWG URL object using the file: protocol.
- PHP v5.0.0path may be a file descriptor.
fs.readFileSync() is the blocking twin of the callback version: it returns the contents directly instead of delivering them later, and it throws instead of passing an error along. That makes it the shortest correct way to read a file in a script, a build step, a test, or module top-level code.
When blocking is fine, and when it is not
Blocking is fine before your server starts listening — nothing is waiting on the event loop yet. It is not fine inside a request handler, a socket callback, or any hot path: every concurrent request stalls for the whole duration of the disk read, and on a slow or network-mounted filesystem that stall is unbounded.
Errors are exceptions, not arguments
A missing file throws rather than returning undefined or null, so an unguarded call crashes the process. Wrap it in try/catch and branch on err.code: treat ENOENT as “use the default” and rethrow everything else, so a permissions problem is not silently swallowed as a missing file.
As with the asynchronous version, the encoding argument decides the return type: omit it and you get a Buffer, pass 'utf8' and you get a string. There is no partial read — you either get the entire file or an exception.
Example
When to use it
- Read a config or .env file during startup, before the server begins accepting connections.
- Load a fixture or snapshot file at the top of a test file.
- Read a template or version file inside a CLI that exits immediately afterwards.
Frequently asked questions
Is fs.readFileSync bad for performance?
Only where it blocks work that matters — inside request handlers or any hot path it stalls the entire event loop for the duration of the disk read. At process startup, in CLI scripts, and in tests it is the simpler, perfectly appropriate choice.
How do I check if a file exists before reading it synchronously?
Do not check first — just read inside try/catch and handle err.code === 'ENOENT'. Testing with fs.existsSync() adds a second syscall and leaves a race window in which the file can disappear between the check and the read.
Does fs.readFileSync return null when the file does not exist?
No, it throws an Error with code 'ENOENT'. There is no return value on failure, so an uncaught call will crash the process.
More examples
Buffer length and string length differ
The same file yields 6 as a Buffer and 5 as a string, because 'é' is two bytes in UTF-8 but one character. If you are computing offsets or sizes, decide up front whether you are counting bytes or characters.
const fs = require('node:fs');
fs.writeFileSync('payload.txt', 'héllo');
const buf = fs.readFileSync('payload.txt');
console.log('Buffer? ', Buffer.isBuffer(buf));
console.log('byte length:', buf.length);
const str = fs.readFileSync('payload.txt', { encoding: 'utf8' });
console.log('string? ', typeof str === 'string');
console.log('char length:', str.length);
fs.unlinkSync('payload.txt');Fall back to defaults when the file is missing
The first call parses the real file; the second hits ENOENT and returns the fallback object. Rethrowing anything that is not ENOENT keeps a permissions error or a malformed-JSON SyntaxError from being disguised as a missing file.
const fs = require('node:fs');
fs.writeFileSync('settings.json', '{"retries": 3, "timeout": 500}');
function loadSettings(file, fallback) {
try {
return JSON.parse(fs.readFileSync(file, 'utf8'));
} catch (err) {
if (err.code === 'ENOENT') return fallback;
throw err;
}
}
console.log(loadSettings('settings.json', { retries: 0 }));
console.log(loadSettings('missing.json', { retries: 0, timeout: 100 }));
fs.unlinkSync('settings.json');
Discussion