Writing Files
writeFile creates a file (or overwrites an existing one) with the data you give it. It's simple — but writing has sharp edges reading doesn't: it destroys existing content, it can be interrupted mid-write leaving a corrupt file, and large writes need streams. This page covers writing data correctly and safely.
Write or overwrite a file
import { writeFile } from 'node:fs/promises'
await writeFile('out.txt', 'Hello, world\n') // string
await writeFile('data.bin', Buffer.from([1, 2, 3])) // Buffer
await writeFile('config.json', JSON.stringify(cfg, null, 2)) // JSON, prettyWrite flags
Flag | Behavior |
|---|---|
| Create or truncate, then write |
| Like |
| Create or append to the end |
| Like |
| Read/write without truncating (overwrite from the start) |
import { writeFile } from 'node:fs/promises'
// Fail rather than overwrite an existing file:
try {
await writeFile('once.lock', 'locked', { flag: 'wx' })
} catch (err) {
if (err.code === 'EEXIST') console.log('already exists — not overwriting')
}Pretty-printing JSON
import { writeFile } from 'node:fs/promises'
const data = { name: 'app', version: '1.0.0', tags: ['a', 'b'] }
// Third arg = indent spaces → human-readable, diff-friendly
await writeFile('app.json', JSON.stringify(data, null, 2) + '\n')The atomic-write pattern
Safe atomic write
import { writeFile, rename } from 'node:fs/promises'
async function writeAtomic(path, data) {
const tmp = `${path}.${process.pid}.tmp`
await writeFile(tmp, data) // write fully to a temp file
await rename(tmp, path) // atomic swap into place
}
await writeAtomic('state.json', JSON.stringify(state))Ensure the directory exists
import { writeFile, mkdir } from 'node:fs/promises'
import { dirname } from 'node:path'
async function writeSafe(path, data) {
await mkdir(dirname(path), { recursive: true }) // ensure parent dirs
await writeFile(path, data)
}Large or streaming output
When data arrives over time or is too big to hold in memory, write through a stream — it applies backpressure so you never buffer more than necessary:
import { createWriteStream } from 'node:fs'
const out = createWriteStream('big-export.csv')
out.write('id,name\n')
for (const row of rows) out.write(`${row.id},${row.name}\n`)
out.end() // signal "no more data"
out.on('finish', () => console.log('written'))
out.on('error', (err) => console.error(err))Writable streams and backpressure are covered in Writable Streams and Backpressure.
The rules
writeFileoverwrites — useappendFile/flag: "a"to add,flag: "wx"to avoid clobbering.Use the atomic temp-then-rename pattern for files you cannot afford to corrupt.
Create parent directories with
mkdir({ recursive: true })before writing.For large/continuous output, use a write stream, not
writeFile.Always handle errors — disks fill, permissions deny, paths vanish.