fsPromises.writeFile()

Writes data to a file, creating it or truncating it first, and resolves when the write completes.

Signature
fsPromises.writeFile(file, data[, options])
  • Added in Node.js v10.0.0

Parameters

ParameterTypeDescription
$filestring | Buffer | URL | FileHandleFilename to write, or an already-open FileHandle.
$datastring | Buffer | TypedArray | DataView | AsyncIterable | Iterable | StreamThe content to write.
$optionsoptionalObject | stringOptions object, or a string that is taken as the encoding.
$options.encodingoptionalstring | null= 'utf8'Encoding used when data is a string.
$options.modeoptionalinteger= 0o666File mode applied when the file is created.
$options.flagoptionalstring= 'w'File system flag; 'w' truncates, 'wx' fails if the file exists, 'a' appends.
$options.signaloptionalAbortSignal | undefined= undefinedAllows aborting an in-progress writeFile.
$options.flushoptionalboolean= falseIf true, the underlying file descriptor is flushed prior to closing it.

Return value

Promise<undefined>Fulfills with undefined upon success.

Errors

Rejects with ENOENT if the parent directory does not exist, EEXIST when flag is 'wx' and the file is already there, EACCES on permission failures, and ERR_INVALID_ARG_TYPE if data is not a supported type.

Changelog

  • PHP v21.1.0, v20.10.0The flush option is now supported, forcing an fsync before the descriptor is closed.
  • PHP v15.14.0, v14.18.0The data argument supports AsyncIterable, Iterable and Stream.
  • PHP v14.0.0The data parameter no longer coerces unsupported input to strings — passing a plain object now throws instead of writing "[object Object]".

fsPromises.writeFile() replaces a file's entire contents in one call. If the file does not exist it is created; if it does, it is truncated to zero length first and then written. That truncate-then-write is the whole behaviour worth remembering.

When to reach for it

Reach for it when you are producing a complete artifact: a generated config, a JSON dump, a build manifest, a rendered page. If you are adding to something that already exists, you want fsPromises.appendFile() — or flag: 'a', which turns writeFile into exactly that.

What trips people up

  • It does not create parent directories. Writing to build/out.json when build/ is missing rejects with ENOENT; run fsPromises.mkdir(dir, { recursive: true }) first.
  • It is not atomic. A crash mid-write leaves a truncated file. For anything that must survive a crash intact, write to a temporary name and rename over the target.
  • Since v14 an unsupported data value throws instead of being coerced, so JSON.stringify() your objects yourself.
  • Concurrent writeFile calls to the same path interleave; there is no locking.

Example

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

When to use it

  • Persist a config or state object as JSON after it changes.
  • Write a generated report or build manifest to disk.
  • Create a lock or marker file that must not clobber an existing one, using flag: 'wx'.
  • Dump a Buffer of binary data such as a decoded image.

Frequently asked questions

Does fsPromises.writeFile append or overwrite?

It overwrites — the default flag 'w' truncates the file to zero bytes before writing. Pass { flag: 'a' } or use fsPromises.appendFile() to add to the end instead.

Why does writeFile fail with ENOENT when the file does not exist yet?

The ENOENT is about the parent directory, not the file — writeFile creates files but never folders. Call fsPromises.mkdir(dirname, { recursive: true }) before writing.

How do I write an object to a JSON file in Node?

Serialize it yourself with JSON.stringify() and pass the resulting string. Since Node 14 writeFile no longer coerces objects, so passing one directly throws ERR_INVALID_ARG_TYPE.

More examples

Refuse to overwrite with flag 'wx'

Prints lock taken, then second attempt: EEXIST, then contents still: owner=1. The 'wx' flag makes create-or-fail a single filesystem operation, so two processes racing for the lock cannot both win.

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

await fs.writeFile('claim.lock', 'owner=1', { flag: 'wx' });
console.log('lock taken');

try {
  await fs.writeFile('claim.lock', 'owner=2', { flag: 'wx' });
} catch (err) {
  console.log('second attempt:', err.code);
}

console.log('contents still:', await fs.readFile('claim.lock', 'utf8'));
await fs.rm('claim.lock');

Create the directory first

Prints no parent directory: ENOENT, then written: hello. This is the single most common writeFile bug report, and mkdir with recursive: true is the one-line fix.

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

try {
  await fs.writeFile('dist/report.txt', 'hello');
} catch (err) {
  console.log('no parent directory:', err.code);
}

await fs.mkdir('dist', { recursive: true });
await fs.writeFile('dist/report.txt', 'hello');
console.log('written:', await fs.readFile('dist/report.txt', 'utf8'));

await fs.rm('dist', { recursive: true });

Write-then-rename for a file readers must never see half-written

Prints the JSON, then temp file left behind: false. rename() is atomic within one filesystem, so a reader sees either the old file or the complete new one — never a truncated middle state.

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

const target = 'state.json';
const tmp = `${target}.tmp`;

await fs.writeFile(tmp, JSON.stringify({ visits: 42 }), { flush: true });
await fs.rename(tmp, target);

console.log(await fs.readFile(target, 'utf8'));
console.log('temp file left behind:', (await fs.readdir('.')).includes(tmp));

await fs.rm(target);

Discussion

  • Be the first to comment on this lesson.