10LOC
Bunintermediate

Use Bun.write for zero-dependency file output

Published August 6, 2027

import { write } from "bun";

export const saveJsonReport = (path: string, report: Record<string, unknown>) =>
  write(path, JSON.stringify(report, null, 2) + "\n");

export const mirrorResponseToDisk = async (url: string, path: string) => {
  const response = await fetch(url);
  return write(path, response);
};

export const overwriteExistingConfig = (path: string, contents: string) =>
  write(path, contents, { createPath: false });

What

Bun.write(destination, data) is Bun's single function for writing a string, a Blob/BunFile, a TypedArray, an ArrayBuffer, or a fetch Response body straight to a file — no node:fs boilerplate, no third-party file-writing package.

Why it matters

In Node, writing a file means picking the right function for what you're writing — writeFile for a buffer or string, manual stream piping for a fetch response body — and importing node:fs/promises to get there. Bun.write collapses that into one function that inspects what you handed it and picks the fastest way to write it, choosing platform-specific system calls (like copy_file_range on Linux or clonefile on macOS) when the source is itself a file. mirrorResponseToDisk below is the clearest example: handing write a Response directly streams the body to disk as it arrives, without a single line of manual stream-piping code.

How it works

  • saveJsonReport writes a string — the simplest case, and the one node:fs's writeFile also covers, included here as the baseline.
  • mirrorResponseToDisk awaits a fetch() call and passes the resulting Response straight to write. Bun reads the response body and writes it to disk incrementally, which matters once the response is larger than you'd want to hold fully in memory first.
  • overwriteExistingConfig passes { createPath: false } — the options object every write call accepts as a third argument alongside destination and data.
  • Every write() call returns Promise<number> — the byte count actually written, useful for verifying a write matched the expected size or just for logging.

Gotchas

  • write() creates missing parent directories by default (createPath: true, since Bun v1.0.16) — a typo'd path silently creates a new directory tree instead of failing loudly. Pass { createPath: false } wherever a missing directory should be an error, not a silent mkdir -p, as overwriteExistingConfig does.
  • Passing a Response from a failed request still writes whatever body came back — write() doesn't check response.ok. Check the status before writing if a 404 error page shouldn't end up saved as your file.
  • write() overwrites the destination with no confirmation by default — there's no separate "fail if exists" flag the way node:fs/promises' cp has with errorOnExist. Check for existence first, via a BunFile's .exists(), if that matters.

Related

  • Bun.file() — the read-side counterpart; returns a lazily-opened BunFile that can be passed right back into write() as a source.
  • node:fs/promises' cp — for directory-level copies rather than single-file or stream writes; a different tool for an adjacent problem.