Skip to main content
The Bun.file and Bun.write APIs documented on this page are heavily optimized and represent the recommended way to perform file-system tasks using Bun. For operations that are not yet available with Bun.file, such as mkdir or readdir, you can use Bun’s nearly complete implementation of the node:fs module.

Reading files (Bun.file())

Bun.file(path): BunFile Create a BunFile instance with the Bun.file(path) function. A BunFile represents a lazily-loaded file; initializing it does not actually read the file from disk.
const foo = Bun.file('foo.txt'); // relative to cwd
foo.size; // number of bytes
foo.type; // MIME type
The reference conforms to the Blob interface, so the contents can be read in various formats.
const foo = Bun.file('foo.txt');

await foo.text(); // contents as a string
await foo.stream(); // contents as ReadableStream
await foo.arrayBuffer(); // contents as ArrayBuffer
await foo.bytes(); // contents as Uint8Array
File references can also be created using numerical file descriptors or file:// URLs.
Bun.file(1234);
Bun.file(new URL(import.meta.url)); // reference to the current file
A BunFile can point to a location on disk where a file does not exist.
const notreal = Bun.file('notreal.txt');
notreal.size; // 0
notreal.type; // "text/plain;charset=utf-8"
const exists = await notreal.exists(); // false
The default MIME type is text/plain;charset=utf-8, but it can be overridden by passing a second argument to Bun.file.
const notreal = Bun.file('notreal.json', {type: 'application/json'});
notreal.type; // => "application/json;charset=utf-8"
For convenience, Bun exposes stdin, stdout and stderr as instances of BunFile.
Bun.stdin; // readonly
Bun.stdout;
Bun.stderr;

Deleting files (file.delete())

You can delete a file by calling the .delete() function.
await Bun.file('logs.json').delete();

Writing files (Bun.write())

Bun.write(destination, data): Promise<number> The Bun.write function is a multi-tool for writing payloads of all kinds to disk. The first argument is the destination which can have any of the following types:
  • string: A path to a location on the file system. Use the "path" module to manipulate paths.
  • URL: A file:// descriptor.
  • BunFile: A file reference.
The second argument is the data to be written. It can be any of the following:
  • string
  • Blob (including BunFile)
  • ArrayBuffer or SharedArrayBuffer
  • TypedArray (Uint8Array, et. al.)
  • Response
All possible permutations are handled using the fastest available system calls on the current platform.
OutputInputSystem callPlatform
filefilecopy_file_rangeLinux
filepipesendfileLinux
pipepipespliceLinux
terminalfilesendfileLinux
terminalterminalsendfileLinux
socketfile or pipesendfile (if http, not https)Linux
file (doesn’t exist)file (path)clonefilemacOS
file (exists)filefcopyfilemacOS
fileBlob or stringwritemacOS
fileBlob or stringwriteLinux
To write a string to disk:
const data = `It was the best of times, it was the worst of times.`;
await Bun.write('output.txt', data);
To copy a file to another location on disk:
const input = Bun.file('input.txt');
const output = Bun.file('output.txt'); // doesn't exist yet!
await Bun.write(output, input);
To write a byte array to disk:
const encoder = new TextEncoder();
const data = encoder.encode('datadatadata'); // Uint8Array
await Bun.write('output.txt', data);
To write a file to stdout:
const input = Bun.file('input.txt');
await Bun.write(Bun.stdout, input);
To write the body of an HTTP response to disk:
const response = await fetch('https://bun.com');
await Bun.write('index.html', response);

Incremental writing with FileSink

Bun provides a native incremental file writing API called FileSink. To retrieve a FileSink instance from a BunFile:
const file = Bun.file('output.txt');
const writer = file.writer();
To incrementally write to the file, call .write().
const file = Bun.file('output.txt');
const writer = file.writer();

writer.write('it was the best of times\n');
writer.write('it was the worst of times\n');
These chunks will be buffered internally. To flush the buffer to disk, use .flush(). This returns the number of flushed bytes.
writer.flush(); // write buffer to disk
The buffer will also auto-flush when the FileSink’s high water mark is reached; that is, when its internal buffer is full. This value can be configured.
const file = Bun.file('output.txt');
const writer = file.writer({highWaterMark: 1024 * 1024}); // 1MB
To flush the buffer and close the file:
writer.end();
Note that, by default, the bun process will stay alive until this FileSink is explicitly closed with .end(). To opt out of this behavior, you can “unref” the instance.
writer.unref();

// to "re-ref" it later
writer.ref();

Directories

Bun’s implementation of node:fs is fast, and we haven’t implemented a Bun-specific API for reading directories just yet. For now, you should use node:fs for working with directories in Bun.

Reading directories (readdir)

To read a directory in Bun, use readdir from node:fs.
import {readdir} from 'node:fs/promises';

// read all the files in the current directory
const files = await readdir(import.meta.dir);

Reading directories recursively

To recursively read a directory in Bun, use readdir with recursive: true.
import {readdir} from 'node:fs/promises';

// read all the files in the current directory, recursively
const files = await readdir('../', {recursive: true});

Creating directories (mkdir)

To recursively create a directory, use mkdir in node:fs:
import {mkdir} from 'node:fs/promises';

await mkdir('path/to/dir', {recursive: true});

Benchmarks

The following is a 3-line implementation of the Linux cat command.
/icons/typescript.svgcat.ts
// Usage
// bun ./cat.ts ./path-to-file

import {resolve} from 'path';

const path = resolve(process.argv.at(-1));
await Bun.write(Bun.stdout, Bun.file(path));
To run the file:
terminal
bun ./cat.ts ./path-to-file
It runs 2x faster than GNU cat for large files on Linux.
Cat screenshot

Reference

interface Bun {
	stdin: BunFile;
	stdout: BunFile;
	stderr: BunFile;

	file(path: string | number | URL, options?: {type?: string}): BunFile;

	write(
		destination: string | number | BunFile | URL,
		input: string | Blob | ArrayBuffer | SharedArrayBuffer | TypedArray | Response,
	): Promise<number>;
}

interface BunFile {
	readonly size: number;
	readonly type: string;

	text(): Promise<string>;
	stream(): ReadableStream;
	arrayBuffer(): Promise<ArrayBuffer>;
	json(): Promise<any>;
	writer(params: {highWaterMark?: number}): FileSink;
	exists(): Promise<boolean>;
}

export interface FileSink {
	write(chunk: string | ArrayBufferView | ArrayBuffer | SharedArrayBuffer): number;
	flush(): number | Promise<number>;
	end(error?: Error): number | Promise<number>;
	start(options?: {highWaterMark?: number}): void;
	ref(): void;
	unref(): void;
}
I