Skip to content
Run BunYour friendly Bun playground
Canary build: these docs may describe unreleased changes. Read this page on docs.runbun.dev

Bun APIs

Reported version: Bun.version is 1.4.4-browser. Labels are explained on the overview.

APIStatusNotes
Bun.servePartialfetch, routes, WebSockets, streaming. See below.
Bun.filePartialA 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.writePartialDestination 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.buildPartialBun’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.envWorksSame object as process.env; starts as { NODE_ENV: "development" }. .env files aren’t loaded.
Bun.versionWorksBun.revision is missing.
Bun.semverWorksorder and satisfies, from Bun’s own semver code.
Bun.sleepWorkssleepSync is missing.
Bun.nanosecondsWorksBrowser clock resolution.
Bun.inspect, Bun.deepEquals, Bun.escapeHTMLWorksinspect is node:util’s, without colors.
Bun.main, Bun.argvWorksBun.main is the entry’s path; Bun.argv is ["bun", main], as process.argv.
Bun.stdout, Bun.stderrPartialWork as Bun.write destinations and print to the console. No writer().
Bun.whichStubReturns null: there are no executables.
Bun.gcStubDoes nothing.
Bun.spawn, Bun.spawnSync, Bun.$MissingNo processes in the browser.
Bun.CryptoHasherWorksmd5, ripemd160, sha1, sha2, sha3, blake2; HMAC with a key; copy, static hash. No md4 or shake.
Bun.hashPartialwyhash (the default), adler32, crc32, xxHash32, xxHash64, murmur32v2, murmur32v3, murmur64v2, all matching Bun’s output. cityHash32, cityHash64, xxHash3 and rapidhash throw.
Bun.passwordMissingUse Bun.CryptoHasher, node:crypto or crypto.subtle.
Bun.Glob, Bun.TranspilerMissing
Bun.resolveSync, Bun.stdinMissing
Bun.sql, Bun.redis, Bun.s3Missing
Bun.connect, Bun.listen, Bun.udpSocket, Bun.dnsMissingNo raw sockets in the browser.
Bun.gzipSync, Bun.gunzipSync, Bun.deflateSync, Bun.inflateSyncPartiallevel 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.mmapMissing
HTMLRewriterMissing

Try a few of them:

index.ts
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]]) } }));

Servers answer requests from the preview, not from the network.

FeatureStatusNotes
fetch(req, server)Works
routesWorksExact 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 routesWorksimport page from "./index.html" is bundled with Bun’s bundler. Assets are served under /_bun/. Rebuilt after any file change. No HMR.
WebSocketsWorksserver.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, pendingWebSocketsWorks
server.reload, server.stopWorks
server.url, port, hostnameWorksport: 0 picks a port from 40001.
export default { fetch }WorksServed automatically, as in Bun (Hono, Elysia).
Streaming responses, Server-Sent EventsWorks
CookiesWorksKept in a cookie jar on the page. cookies in the console lists them.
TLSMissing
error, idleTimeout, maxRequestBodySize, unix, reusePortMissingA throwing handler returns a plain-text 500.
server.requestIP, server.timeout, server.ref/unrefMissing

Routes with parameters, JSON and a streamed response. Send requests from the bar above the preview:

index.ts
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}`);
ModuleStatusNotes
bun:sqlitePartialBuilt 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:testPartialRun with bun test in the console. See below.
bun:ffi, bun:jscMissing
bunWorksimport { serve } from "bun" and the default export are the Bun global.
index.ts
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));
  • test/it/describe with .skip, .only, .todo, .each, .if, .skipIf, .todoIf
  • beforeAll, beforeEach, afterEach, afterAll, setDefaultTimeout
  • mock(), spyOn(), jest.fn, jest.spyOn
  • expect with .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 test takes name filters but no flags.

Type bun test in the console to run this one:

math.test.ts
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);
  });
});
APIStatusNotes
import.meta.url, dirname, filenameWorksfile:///<path> in the project.
import.meta.requirePartialString literals only (import.meta.require("x")).
import.meta.dir, file, path, main, envWorksenv is process.env. In browser code (HTML imports), only url, dirname and filename are filled in.
import.meta.resolveMissingThe browser’s own returns the served URL, not a project path.
import(expr) with a computed pathWorks
fetch("file:///…")WorksReads project files. Relative URLs throw. Other URLs go to the network, subject to CORS.
process, Buffer, global, timersWorksSee Node built-ins.