Bun.* API 概览
本教程共 34 篇 · 第 9 篇 · 更新于 2026-08-06
本节目标:
- 理解 Bun 内置 API 的设计取向:能用 Web 标准就用 Web 标准,只在服务端无标准处新增
Bun.*。- 建立一张”心智地图”,知道 HTTP 服务、文件、子进程、哈希、数据库等能力分别落在哪个 API 上。
- 通过最小片段上手最常用的几个:
Bun.serve、Bun.file、Bun.write、Bun.spawn、Bun.$。- 区分
Bun全局对象和bun:前缀内置模块两类入口。- 知道怎样拿到完整的 TypeScript 类型提示,以及这些 API 和
node:模块如何共存。
9.1 设计取向:先标准,后自研
Bun 在 Bun 全局对象和若干内置模块上实现了一整套原生 API。它们是经过重度优化的、“Bun 风格”的标准做法。但在动手记 API 之前,先理解它的取舍逻辑,会让后面的学习省很多力气:
Bun 尽一切可能实现 Web 标准 API。 fetch、Request、Response、Headers、URL、Blob、ReadableStream、TextEncoder、crypto、WebSocket、AbortController——这些在浏览器里怎么用,在 Bun 里就怎么用。
只有在服务端任务缺少标准的地方,Bun 才引入新 API。 典型的就是文件 I/O 和启动 HTTP 服务器——Web 平台从来没有为它们定义过标准。即便是这些自研 API,Bun 也依然建立在 Blob、URL、Request 这些标准原语之上。
看一眼最经典的例子就明白了:
// server.ts
Bun.serve({
fetch(req: Request) {
return new Response("Success!");
},
});
Bun.serve 是 Bun 自己的 API,但它的入参是标准 Request、返回值是标准 Response。你写的处理函数几乎可以原样搬到 Cloudflare Workers 或 Deno 上。这就是 Bun 内置 API 的一贯风格:外壳是 Bun 的,内核是标准的。
这条取向带来一个直接的学习收益:你不需要背一整套私有对象模型,只需要记住”入口在哪里”,剩下的都是你已经熟悉的 Web API。
9.2 两类入口
Bun 的内置能力通过两种方式暴露:
第一类:Bun 全局对象。 不需要 import,直接用:
Bun.version; // 当前 bun CLI 的版本号字符串
Bun.file("a.txt");
Bun.serve({ /* ... */ });
第二类:bun: 前缀的内置模块。 需要显式导入:
import { Database } from "bun:sqlite";
import { test, expect } from "bun:test";
import { dlopen } from "bun:ffi";
bun: 前缀是 Bun 内置模块的命名空间,和 Node.js 的 node: 前缀是同一个思路——明确标识”这是运行时自带的,不是 npm 包”。这个前缀的解析规则会在下一章展开。
还有少数 API 是”两栖”的,比如 Bun Shell 既可以从 bun 包导入,也挂在全局对象上:
import { $ } from "bun";
// 等价于 Bun.$
9.3 分类速查表
下面这张表按主题列出 Bun.* 与内置模块的主要 API,作为你后续查阅的索引。不必现在全部记住,知道”有这么个东西”就够了。
| 主题 | API |
|---|---|
| HTTP 服务 | Bun.serve |
| Shell | Bun.$(也可 import { $ } from "bun") |
| 打包器 | Bun.build |
| 文件 I/O | Bun.file、Bun.write、Bun.stdin、Bun.stdout、Bun.stderr |
| 子进程 | Bun.spawn、Bun.spawnSync |
| TCP 套接字 | Bun.listen、Bun.connect |
| UDP 套接字 | Bun.udpSocket |
| WebSocket | new WebSocket()(客户端)、Bun.serve(服务端) |
| 转译器 | Bun.Transpiler |
| 文件系统路由 | Bun.FileSystemRouter |
| 流式 HTML 处理 | HTMLRewriter |
| 哈希与密码 | Bun.password、Bun.hash、Bun.CryptoHasher、Bun.sha |
| CSRF 防护 | Bun.CSRF.generate、Bun.CSRF.verify |
| SQLite | bun:sqlite |
| SQL 客户端 | Bun.SQL、Bun.sql |
| Redis(Valkey)客户端 | Bun.RedisClient、Bun.redis |
| FFI(外部函数接口) | bun:ffi |
| DNS | Bun.dns.lookup、Bun.dns.prefetch、Bun.dns.getCacheStats |
| 测试 | bun:test |
| Worker 线程 | new Worker() |
| 模块加载器插件 | Bun.plugin |
| Glob 匹配 | Bun.Glob |
| Cookie | Bun.Cookie、Bun.CookieMap |
| Node-API | Node-API 原生插件支持 |
| 模块元信息 | import.meta |
| 运行时信息 | Bun.version、Bun.revision、Bun.env、Bun.main |
| 休眠与计时 | Bun.sleep()、Bun.sleepSync()、Bun.nanoseconds() |
| 随机与 UUID | Bun.randomUUIDv7() |
| 系统环境 | Bun.which() |
| 比较与检视 | Bun.peek()、Bun.deepEquals()、Bun.deepMatch、Bun.inspect() |
| 字符串处理 | Bun.escapeHTML()、Bun.stringWidth()、Bun.indexOfLine |
| URL 与路径 | Bun.fileURLToPath()、Bun.pathToFileURL() |
| 压缩 | Bun.gzipSync()、Bun.gunzipSync()、Bun.deflateSync()、Bun.inflateSync()、Bun.zstdCompressSync()、Bun.zstdDecompressSync() 等 |
| 流处理工具 | Bun.readableStreamToText()、Bun.readableStreamToBytes()、Bun.readableStreamToJSON() 等 |
| 内存与缓冲区 | Bun.ArrayBufferSink、Bun.allocUnsafe、Bun.concatArrayBuffers |
| 模块解析 | Bun.resolveSync() |
| 解析与格式化 | Bun.semver、Bun.TOML.parse、Bun.markdown、Bun.color、Bun.Image |
| 底层/内部 | Bun.mmap、Bun.gc、Bun.generateHeapSnapshot、bun:jsc |
Tip这张表最实用的读法不是”从头背到尾”,而是遇到需求时反查:想跑个子进程 → 看到
Bun.spawn;想算个密码哈希 → 看到Bun.password;想读环境变量 → 看到Bun.env。
9.4 高频 API 最小片段
下面挑五个最常用的,各给一段可直接运行的最小代码。它们各自的完整用法会在第六篇「内置 API 精选」里展开。
Bun.serve —— 启动 HTTP 服务
// server.ts
const server = Bun.serve({
port: 3000,
routes: {
// 静态响应
"/api/status": new Response("OK"),
// 动态路由参数
"/users/:id": req => new Response(`Hello User ${req.params.id}!`),
// 按 HTTP 方法分发
"/api/posts": {
GET: () => new Response("List posts"),
POST: async req => {
const body = await req.json();
return Response.json({ created: true, ...body });
},
},
},
// 未匹配路由的兜底
fetch(req) {
return new Response("Not Found", { status: 404 });
},
});
console.log(`Server running at ${server.url}`);
运行:
bun run server.ts
Note
routes选项需要 Bun v1.2.3 及以上;在更老的版本上必须提供fetch处理函数。当前基线 v1.3.14 两者都支持,fetch作为兜底仍然很有用。
端口的默认值依次取自$BUN_PORT、$PORT、$NODE_PORT,都没有时为3000;把port设为0可随机选取一个可用端口,随后从server.port或server.url读回。
Bun.file —— 惰性读取文件
Bun.file(path) 返回一个 BunFile,它不会立刻读盘,只是创建了一个引用:
const foo = Bun.file("foo.txt"); // 相对于当前工作目录
foo.size; // 字节数
foo.type; // MIME 类型
BunFile 遵循 Blob 接口,所以可以用你熟悉的方式取内容:
const foo = Bun.file("foo.txt");
await foo.text(); // 字符串
await foo.json(); // 解析为 JSON 对象
foo.stream(); // ReadableStream
await foo.arrayBuffer(); // ArrayBuffer
await foo.bytes(); // Uint8Array
文件不存在也可以创建引用,用 .exists() 判断:
const notreal = Bun.file("notreal.txt");
notreal.size; // 0
const exists = await notreal.exists(); // false
标准输入输出同样以 BunFile 的形式暴露:
Bun.stdin; // 只读
Bun.stdout;
Bun.stderr;
Bun.write —— 万能写入
Bun.write(destination, data) 返回写入的字节数。它对”目标”和”数据”都很宽容——字符串、Blob、BunFile、Response、TypedArray 都能作为数据源:
await Bun.write("output.txt", "Hello Bun!");
// 直接把一个 Response 的响应体落盘
await Bun.write("bun.html", await fetch("https://bun.com"));
// 文件复制:源和目标都是 BunFile
await Bun.write(Bun.file("copy.txt"), Bun.file("output.txt"));
Tip
Bun.file/Bun.write覆盖的是”读写单个文件”这件事。像mkdir、readdir、stat这类目录操作,直接用node:fs即可——Bun 对node:fs的实现相当完整,两者可以自由混用。
Bun.spawn —— 启动子进程
命令以字符串数组形式传入,返回一个 Bun.Subprocess:
const proc = Bun.spawn(["bun", "--version"]);
console.log(await proc.exited); // 退出码,0 表示成功
第二个参数用于配置子进程:
const proc = Bun.spawn(["bun", "--version"], {
cwd: "./path/to/subdir", // 工作目录
env: { ...process.env, FOO: "bar" }, // 环境变量
onExit(proc, exitCode, signalCode, error) {
// 退出回调
},
});
proc.pid; // 子进程 PID
读取子进程输出:
const proc = Bun.spawn(["echo", "hello"]);
const text = await proc.stdout.text();
console.log(text); // "hello\n"
需要阻塞式调用时用 Bun.spawnSync(第 8 章嵌入 Git 哈希的宏就用了它)。
Bun.$ —— Bun Shell
Bun Shell 是一个跨平台的类 bash shell,用模板字符串直接写命令:
import { $ } from "bun";
await $`echo "Hello World!"`; // 输出 Hello World!
它的几个关键特性值得单独记住:
- 跨平台:Windows / Linux / macOS 行为一致,
ls、cd、rm等常用命令由 Bun 原生实现,不再需要rimraf、cross-env这类兼容包。 - 默认转义:所有插值字符串默认被转义,从机制上避免 shell 注入。
- JavaScript 互操作:
Response、Blob、ArrayBuffer、Bun.file(path)都能当作 stdin / stdout 使用。
import { $ } from "bun";
const response = await fetch("https://example.com");
// 把 Response 直接当作 stdin
await $`cat < ${response} | wc -c`;
静默执行与取文本输出:
import { $ } from "bun";
await $`echo "Hello World!"`.quiet(); // 不打印
const out = await $`echo "Hello"`.text(); // 取字符串,text() 会自动 quiet
Bun.Glob —— 文件匹配
import { Glob } from "bun";
const glob = new Glob("**/*.ts");
// 递归扫描当前目录
for await (const file of glob.scan(".")) {
console.log(file);
}
// 也可以只做字符串匹配
new Glob("*.ts").match("index.ts"); // true
9.5 类型提示
要在编辑器里获得完整的 Bun.* 类型补全,安装官方类型包:
bun add -d @types/bun
然后在 tsconfig.json 里确保它被识别(bun init 生成的配置已经处理好了)。装好之后,Bun.serve 的选项、BunFile 的方法签名、Subprocess 的字段都会有完整提示。
Note
Bun全局对象的 API 表面仍在持续扩充,官方文档也明确标注它”会随新 API 加入而变化”。锁定@types/bun的版本与你使用的 Bun 版本一致,可以避免类型和实际行为对不上。
9.6 和 Node.js API 的关系
这一点常被初学者误解,需要说清楚:Bun.* 不是用来替换 node: 模块的,两者是共存关系。
Bun 的目标是 100% Node.js API 兼容(这是目标,尚未完全达成),因此绝大多数 node: 内置模块在 Bun 里可以直接用:
import { readdir, mkdir } from "node:fs/promises";
import path from "node:path";
import { createServer } from "node:http";
同时,Bun 也实现了大量 Node.js 全局变量:Buffer、process、__dirname、__filename、require、global 等等。
那什么时候该用 Bun.*?给一个实用判断:
- 需要跨运行时可移植 → 用 Web 标准 API 或
node:模块。 - 明确只跑在 Bun 上、且该操作是性能热点 → 优先用
Bun.*,它们针对 Bun 做过重度优化。 Bun.*没覆盖到的能力(如目录遍历、路径拼接) → 直接用node:模块,不必绕路。
不需要在两者之间”二选一”。一个真实项目里同时出现 Bun.file 和 node:path 是完全正常的写法。
9.7 小结
本章建立的是一张地图,不是一本词典。需要带走的几条:
- 设计取向:Web 标准优先,
Bun.*只补服务端空白,且建立在标准原语之上。 - 两类入口:
Bun全局对象(免导入)与bun:前缀内置模块(需导入)。 - 五个高频 API:
Bun.serve(HTTP)、Bun.file(读)、Bun.write(写)、Bun.spawn(子进程)、Bun.$(Shell)。 - 类型提示靠
@types/bun,版本与 Bun 对齐。 - 和 Node.js 共存,不是互斥替代。
下一章我们进入模块解析——搞清楚 import "./hello"、import "react"、import "bun:sqlite"、import "node:fs" 这四种写法,Bun 分别是怎么找到对应文件的。