Bun APIs
Reported version: Bun.version is 1.4.4-browser. Labels are explained on the overview.
The Bun global
Section titled “The Bun global”| API | Status | Notes |
|---|---|---|
Bun.serve | Partial | fetch, routes, WebSockets, streaming. See below. |
Bun.file | Partial | A Blob: size, type, slice, stream, text, json, bytes, arrayBuffer, plus exists, name, lastModified, write, writer, delete. Reads are live, but new Response(file) sends the file as it was when Bun.file() was called. No file descriptors. |
Bun.write | Partial | Destination is a path, file: URL, BunFile, Bun.stdout or Bun.stderr (file descriptors aren’t). Accepts strings, BunFile, Blob, Response, ArrayBuffer, typed arrays. Creates parent folders. |
Bun.build | Partial | Bun’s real bundler, as wasm (~7.5 MB, loaded on first use). Supports entrypoints, outdir, target, format, minify, splitting, sourcemap, naming, publicPath, external, define, conditions, banner, footer, throw. plugins are ignored with a warning; loader, env, packages, drop, compile, root aren’t supported. |
Bun.env | Works | Same object as process.env; starts as { NODE_ENV: "development" }. .env files aren’t loaded. |
Bun.version | Works | Bun.revision is missing. |
Bun.semver | Works | order and satisfies, from Bun’s own semver code. |
Bun.sleep | Works | sleepSync is missing. |
Bun.nanoseconds | Works | Browser clock resolution. |
Bun.inspect, Bun.deepEquals, Bun.escapeHTML | Works | inspect is node:util’s, without colors. |
Bun.main, Bun.argv | Works | Bun.main is the entry’s path; Bun.argv is ["bun", main], as process.argv. |
Bun.stdout, Bun.stderr | Partial | Work as Bun.write destinations and print to the console. No writer(). |
Bun.which | Stub | Returns null: there are no executables. |
Bun.gc | Stub | Does nothing. |
Bun.spawn, Bun.spawnSync, Bun.$ | Missing | No processes in the browser. |
Bun.CryptoHasher | Works | md5, ripemd160, sha1, sha2, sha3, blake2; HMAC with a key; copy, static hash. No md4 or shake. |
Bun.hash | Partial | wyhash (the default), adler32, crc32, xxHash32, xxHash64, murmur32v2, murmur32v3, murmur64v2, all matching Bun’s output. cityHash32, cityHash64, xxHash3 and rapidhash throw. |
Bun.password | Missing | Use Bun.CryptoHasher, node:crypto or crypto.subtle. |
Bun.Glob, Bun.Transpiler | Missing | |
Bun.resolveSync, Bun.stdin | Missing | |
Bun.sql, Bun.redis, Bun.s3 | Missing | |
Bun.connect, Bun.listen, Bun.udpSocket, Bun.dns | Missing | No raw sockets in the browser. |
Bun.gzipSync, Bun.gunzipSync, Bun.deflateSync, Bun.inflateSync | Partial | level is the only option read. Deflate is raw, as in Bun. |
Bun.zstd* | Missing | |
Bun.CookieMap, Bun.YAML, Bun.TOML, Bun.plugin, Bun.FileSystemRouter, Bun.mmap | Missing | |
HTMLRewriter | Missing |
Try a few of them:
await Bun.write("notes.txt", "written with Bun.write\n");
const file = Bun.file("notes.txt");
console.log(file.size, await file.text());
console.log(Bun.semver.satisfies("1.2.3", "^1.0.0"), Bun.semver.order("1.0.0", "2.0.0"));
console.log(new Bun.CryptoHasher("sha256").update("bun").digest("hex"));
console.log(Bun.hash("bun"));
const zipped = Bun.gzipSync("hello hello hello hello");
console.log(zipped.length, "bytes:", new TextDecoder().decode(Bun.gunzipSync(zipped)));
console.log(Bun.inspect({ deep: { map: new Map([["a", 1]]) } }));Bun.serve
Section titled “Bun.serve”Servers answer requests from the preview, not from the network.
| Feature | Status | Notes |
|---|---|---|
fetch(req, server) | Works | |
routes | Works | Exact paths, :param (in req.params), trailing *, per-method handlers, static Responses. Globs in the middle of a path (/a/*/b) don’t match. |
HTML imports in routes | Works | import page from "./index.html" is bundled with Bun’s bundler. Assets are served under /_bun/. Rebuilt after any file change. No HMR. |
| WebSockets | Works | server.upgrade(req, { data }), open/message/close/drain, send, publish, subscribe, cork, close. ping/pong do nothing. Only the preview page can connect. |
server.publish, subscriberCount, pendingWebSockets | Works | |
server.reload, server.stop | Works | |
server.url, port, hostname | Works | port: 0 picks a port from 40001. |
export default { fetch } | Works | Served automatically, as in Bun (Hono, Elysia). |
| Streaming responses, Server-Sent Events | Works | |
| Cookies | Works | Kept in a cookie jar on the page. cookies in the console lists them. |
| TLS | Missing | |
error, idleTimeout, maxRequestBodySize, unix, reusePort | Missing | A throwing handler returns a plain-text 500. |
server.requestIP, server.timeout, server.ref/unref | Missing |
Routes with parameters, JSON and a streamed response. Send requests from the bar above the preview:
const server = Bun.serve({
routes: {
"/": new Response("Try /hello/you, /json or /stream"),
"/hello/:name": (req) => new Response(`Hello, ${req.params.name}!`),
"/json": () => Response.json({ ok: true, at: Date.now() }),
"/stream": () =>
new Response(
new ReadableStream({
async start(controller) {
for (const word of ["streamed ", "one ", "chunk ", "at a time"]) {
controller.enqueue(new TextEncoder().encode(word));
await Bun.sleep(300);
}
controller.close();
},
}),
),
},
});
console.log(`Listening on ${server.url}`);bun:* modules
Section titled “bun:* modules”| Module | Status | Notes |
|---|---|---|
bun:sqlite | Partial | Built on sql.js. Database with query, prepare, run, transaction, serialize, close; statements with all, get, values, run, iterate. File databases are saved to the project. Missing: Statement.as(), Database.deserialize, loadExtension, safeIntegers, SQLiteError. |
bun:test | Partial | Run with bun test in the console. See below. |
bun:ffi, bun:jsc | Missing | |
bun | Works | import { serve } from "bun" and the default export are the Bun global. |
import { Database } from "bun:sqlite";
const db = new Database(":memory:");
db.run("create table todos (id integer primary key, title text, done integer)");
const insert = db.prepare("insert into todos (title, done) values (?, ?)");
const addAll = db.transaction((rows: [string, number][]) => rows.forEach((r) => insert.run(...r)));
addAll([["write docs", 1], ["ship embeds", 0]]);
console.log(db.query("select * from todos where done = ?").all(0));bun:test
Section titled “bun:test”-
test/it/describewith.skip,.only,.todo,.each,.if,.skipIf,.todoIf -
beforeAll,beforeEach,afterEach,afterAll,setDefaultTimeout -
mock(),spyOn(),jest.fn,jest.spyOn -
expectwith.not,.resolves,.rejects, asymmetric matchers (anything,any,objectContaining,arrayContaining,stringContaining,stringMatching) and the common matchers (toBe,toEqual,toStrictEqual,toThrow,toMatchObject,toHaveProperty,toHaveBeenCalledWith, …), plus Bun’s extras (toBeEmpty,toBeOneOf,toSatisfy,toStartWith,toEndWith,toInclude,toBeArray,toBeString,toBeNumber,toBeFunction,toHaveReturned*, …) - Snapshots, fake timers,
setSystemTime,mock.module,jest.mock,expect.extend,expect.assertions, coverage,test.concurrent,test.failing - Unknown matchers throw “is not supported here”.
bun testtakes name filters but no flags.
Type bun test in the console to run this one:
import { describe, expect, mock, test } from "bun:test";
const add = (a: number, b: number) => a + b;
describe("add", () => {
test("sums", () => expect(add(1, 2)).toBe(3));
test.each([[1, 1, 2], [2, 3, 5]])("%d + %d = %d", (a, b, sum) => expect(add(a, b)).toBe(sum));
test("mocks", () => {
const fn = mock(add);
fn(2, 2);
expect(fn).toHaveBeenCalledWith(2, 2);
});
});import.meta and globals
Section titled “import.meta and globals”| API | Status | Notes |
|---|---|---|
import.meta.url, dirname, filename | Works | file:///<path> in the project. |
import.meta.require | Partial | String literals only (import.meta.require("x")). |
import.meta.dir, file, path, main, env | Works | env is process.env. In browser code (HTML imports), only url, dirname and filename are filled in. |
import.meta.resolve | Missing | The browser’s own returns the served URL, not a project path. |
import(expr) with a computed path | Works | |
fetch("file:///…") | Works | Reads project files. Relative URLs throw. Other URLs go to the network, subject to CORS. |
process, Buffer, global, timers | Works | See Node built-ins. |