| name | use-bun |
| description | Bun-first development patterns for TypeScript projects. Use when auditing Node.js projects for migration opportunities, starting new projects, evaluating npm dependencies, or reducing package.json bloat. Triggers on mentions of npm, yarn, pnpm, Node.js migration, dependency audit, or package optimization. |
| metadata | {"version":"1.1.0","category":"development"} |
Use Bun
Bun-first philosophy: prefer native APIs over external packages.
<when_to_use>
- Auditing existing projects for Bun migration opportunities
- Starting new TypeScript projects
- Evaluating whether to add a dependency
- Reviewing code that uses Node.js APIs
- Cleaning up package.json bloat
Boundary: This skill covers when to use Bun and what it replaces. For detailed Bun API patterns and implementation examples, load the outfitter:bun-dev skill instead.
</when_to_use>
CLI Commands
Use Bun directly instead of Node.js tooling:
| Instead of | Use |
|---|
node file.ts or ts-node file.ts | bun file.ts |
jest or vitest | bun test |
webpack or esbuild (CLI) | bun build |
npm install / yarn / pnpm | bun install |
npm run script | bun run script |
npx package | bunx package |
nodemon | bun --watch |
node --env-file=.env | bun (auto-loads .env) |
Decision Framework
Before adding a dependency, follow this hierarchy:
Need functionality
│
├─► Does Bun have a built-in API?
│ └─► YES → Use it directly
│
├─► Can you wrap a Bun primitive?
│ └─► YES → Thin abstraction over Bun API
│
└─► External package (last resort)
└─► Document why Bun couldn't do it
Evaluation Checklist
- Check Bun docs first: https://bun.sh/docs
- Search for
Bun. or bun: in docs
- Test if Node.js API you're using has faster Bun equivalent
- If adding package, verify Bun doesn't cover it natively
When Packages Are Justified
- Framework-level abstractions (Hono, TanStack Router)
- Domain-specific logic (Zod schemas, date-fns)
- Protocol implementations Bun doesn't cover
- Battle-tested crypto beyond basic hashing
Document exceptions with a code comment:
import { format, addDays } from 'date-fns';
Quick Reference
Bun APIs organized by category. Check these before reaching for npm.
Testing
| Bun API | Replaces |
|---|
bun:test | jest, vitest, mocha |
expect() | chai, expect.js |
import { describe, test, expect } from 'bun:test';
Database & Storage
| Bun API | Replaces |
|---|
bun:sqlite | better-sqlite3, sql.js |
Bun.sql | pg, postgres.js, mysql2 |
Bun.redis | ioredis, redis |
Bun.s3 | @aws-sdk/client-s3 |
import { Database } from 'bun:sqlite';
const client = Bun.redis();
const bucket = Bun.s3('my-bucket');
Networking
| Bun API | Replaces |
|---|
Bun.listen() | net.createServer |
Bun.connect() | net.connect |
Bun.udpSocket() | dgram.createSocket |
Bun.dns | dns.lookup |
HTTP
| Bun API | Replaces |
|---|
Bun.serve() | express, fastify, http |
Bun.fetch() | node-fetch, axios, got |
Bun.serve({ port: 3000, fetch: (req) => new Response('ok') });
Shell & Process
| Bun API | Replaces |
|---|
Bun.$ | execa, shelljs, zx |
Bun.spawn() | child_process.spawn |
Bun.spawnSync() | child_process.spawnSync |
import { $ } from 'bun';
await $`ls -la`;
File System
| Bun API | Replaces |
|---|
Bun.file() | fs.readFile, fs-extra |
Bun.write() | fs.writeFile |
file.exists() | fs.existsSync |
file.stream() | fs.createReadStream |
const content = await Bun.file('./data.json').json();
await Bun.write('./out.txt', content);
Utilities
| Bun API | Replaces |
|---|
Bun.password.hash() | bcrypt, argon2 |
Bun.hash() | xxhash, murmur, crc32 |
Bun.CryptoHasher | crypto.createHash |
Bun.Glob | glob, fast-glob, minimatch |
Bun.semver | semver |
Bun.YAML.parse() | js-yaml, yaml |
Bun.TOML.parse() | toml |
Bun.gzipSync() | zlib, pako |
Bun.zstdCompressSync() | zstd-codec |
Bun.Archive | tar, archiver |
Bun.sleep() | delay, timers-promises |
Bun.deepEquals() | lodash.isEqual, deep-equal |
Bun.escapeHTML() | escape-html |
Bun.stringWidth() | string-width |
Bun.Cookie | cookie, tough-cookie |
Bun.randomUUIDv7() | uuid |
Bun.which() | which |
const hash = await Bun.password.hash(password, { algorithm: 'argon2id' });
const glob = new Bun.Glob('**/*.ts');
const valid = Bun.semver.satisfies('1.2.3', '>=1.0.0');
Bundling
| Bun API | Replaces |
|---|
Bun.build() | esbuild, rollup, webpack |
bun build --compile | pkg, nexe |
bun build ./index.ts --outfile dist/bundle.js --minify
Frontend Development
Bun.serve() supports HTML imports with automatic bundling. No Vite/Webpack needed.
import index from "./index.html";
Bun.serve({
routes: {
"/": index,
"/api/data": (req) => Response.json({ ok: true }),
},
development: { hmr: true, console: true },
});
HTML files can import .tsx/.jsx/.ts directly:
<html>
<body>
<div id="root"></div>
<script type="module" src="./app.tsx"></script>
</body>
</html>
Run with hot reloading:
bun --hot server.ts
Audit Command
Scan a codebase for Bun migration opportunities using the bundled audit script.
bun ./scripts/audit-bun-usage.ts [path]
bun ./scripts/audit-bun-usage.ts ./my-project
bun ./scripts/audit-bun-usage.ts ./my-project --format=md
The audit identifies:
- npm packages replaceable by Bun built-ins
- Node.js imports with Bun equivalents
- Config files for tools Bun replaces
- Files already using Bun APIs (positive signal)
Common Migrations
From Express/Fastify
import express from 'express';
const app = express();
app.get('/', (req, res) => res.send('ok'));
app.listen(3000);
import { Hono } from 'hono';
const app = new Hono().get('/', (c) => c.text('ok'));
Bun.serve({ port: 3000, fetch: app.fetch });
From Jest/Vitest
import { describe, it, expect } from '@jest/globals';
import { describe, it, expect } from 'bun:test';
From better-sqlite3
import Database from 'better-sqlite3';
import { Database } from 'bun:sqlite';
From bcrypt
import bcrypt from 'bcrypt';
const hash = await bcrypt.hash(password, 10);
const hash = await Bun.password.hash(password, { algorithm: 'argon2id' });
From execa/shelljs
import { execa } from 'execa';
const { stdout } = await execa('ls', ['-la']);
import { $ } from 'bun';
const result = await $`ls -la`;
console.log(result.text());
ALWAYS:
- Check Bun docs before adding any dependency
- Use Bun.file/Bun.write over fs module
- Use bun:test for testing (Jest-compatible)
- Use bun:sqlite for SQLite (better-sqlite3 compatible)
- Use Bun.password for password hashing
- Use
Bun.$ for shell commands
NEVER:
- Add packages that duplicate Bun built-ins without documenting why
- Use node-fetch when Bun.fetch exists
- Use bcrypt when Bun.password exists
- Use fs when Bun.file/Bun.write exists
- Use child_process when Bun.$/Bun.spawn exists
DOCUMENT:
- Why a package is needed when Bun alternative exists
- Bun limitations encountered (helps track what to migrate later)
- Exceptions in code comments for future audits
Related skills:
outfitter:bun-dev — Detailed Bun API patterns, implementation examples, testing strategies
outfitter:typescript-dev — TypeScript patterns for Bun projects, strict typing, Zod validation