"Fast JavaScript/TypeScript runtime with bundling and testing. Trigger: When using Bun for server apps, scripts, or tooling."
Install
npx skillscat add joabgonzalez/ai-agents-skills/bun Install via the SkillsCat registry.
Bun Skill
Fast JS/TS runtime with native bundling, testing, package management.
When to Use
- Running JS/TS apps that benefit from fast startup and native TS support
- Using Bun's built-in bundler, test runner, or package manager
- Writing HTTP servers, scripts, or CLI tools
- Don't use for: full Node.js API compat, native C++ addons, LTS stability
Critical Patterns
✅ REQUIRED: Bun.serve() for HTTP Servers
Built-in HTTP with streaming, no framework needed.
// CORRECT: zero-dependency HTTP with Bun.serve
Bun.serve({
port: 3000,
fetch(req) {
const url = new URL(req.url);
if (url.pathname === '/health') return Response.json({ ok: true });
return new Response('Not found', { status: 404 });
},
});
// WRONG: importing express just for a simple endpoint
import express from 'express';✅ REQUIRED: Bun.file() for File I/O
Native file API with lazy BunFile for fast I/O.
// CORRECT: Bun-native file operations
const file = Bun.file('./config.json');
const config = await file.json();
await Bun.write('./output.txt', 'Hello from Bun');
// WRONG: Node fs/promises in a Bun project
import { readFile } from 'fs/promises';✅ REQUIRED: bun:test for Testing
Jest-compatible test runner, no install/config.
import { test, expect, describe, mock } from 'bun:test';
describe('math utils', () => {
test('adds numbers', () => {
expect(2 + 3).toBe(5);
});
test('mocks fetch', async () => {
const fn = mock(() => Response.json({ ok: true }));
globalThis.fetch = fn;
const res = await fetch('/api');
expect(fn).toHaveBeenCalledTimes(1);
});
});✅ REQUIRED: bunx for Package Execution
Run npm binaries without install (like npx, faster).
bunx tsc --noEmit
bunx prettier --write src/
bunx drizzle-kit generate✅ REQUIRED: Workspace Configuration
Monorepo support via npm-style workspaces in package.json.
{
"workspaces": ["packages/*", "apps/*"],
"scripts": {
"dev": "bun run --filter './apps/*' dev",
"test": "bun test --recursive"
}
}✅ REQUIRED: Bun.spawn() for Shell Commands
Spawn child processes with native API -- faster than Node's child_process.
// CORRECT: Bun-native process spawning
const proc = Bun.spawn(['git', 'status'], {
stdout: 'pipe',
stderr: 'pipe',
});
const output = await new Response(proc.stdout).text();
console.log(output);
// Alternative: Template literal syntax (shell $)
import { $ } from 'bun';
const result = await $`git status`;
console.log(result.stdout.toString());
// WRONG: Node's child_process (slower, requires import)
import { exec } from 'child_process';
exec('git status', (err, stdout) => console.log(stdout));✅ REQUIRED: Plugin System for Custom Loaders
Extend Bun's bundler with plugins for custom file types.
import type { BunPlugin } from 'bun';
const myPlugin: BunPlugin = {
name: 'yaml-loader',
setup(build) {
build.onLoad({ filter: /\.yaml$/ }, async (args) => {
const text = await Bun.file(args.path).text();
const yaml = parseYAML(text); // Your YAML parser
return {
contents: `export default ${JSON.stringify(yaml)}`,
loader: 'js',
};
});
},
};
// Use in build
await Bun.build({
entrypoints: ['./index.ts'],
plugins: [myPlugin],
});✅ REQUIRED: WebSockets with Bun.serve()
Native WebSocket support in HTTP server with zero dependencies.
Bun.serve({
port: 3000,
fetch(req, server) {
const url = new URL(req.url);
if (url.pathname === '/ws') {
if (server.upgrade(req)) return; // Upgrade to WebSocket
return new Response('Upgrade failed', { status: 400 });
}
return new Response('Not found', { status: 404 });
},
websocket: {
message(ws, message) {
console.log('Received:', message);
ws.send(`Echo: ${message}`);
},
open(ws) {
console.log('Client connected');
},
close(ws) {
console.log('Client disconnected');
},
},
});Decision Tree
- Simple HTTP service? ->
Bun.serve()with no framework - Reading/writing files? ->
Bun.file()andBun.write() - Running tests? ->
bun test(Jest-compatible, zero config) - One-off package binary? ->
bunx <package> - Bundling for production? ->
bun build --target=browseror--target=bun - Monorepo? -> Configure
workspacesin rootpackage.json - Node API not supported? -> Check compatibility docs; fall back to Node
- Shell scripting? ->
Bun.spawn()orBun.$tagged template
Example
// server.ts -- HTTP server with route map
const routes: Record<string, (req: Request) => Response | Promise<Response>> = {
'/': () => new Response('Welcome to the Bun server'),
'/health': () => Response.json({ status: 'ok' }),
'/file': async () => {
const file = Bun.file('./data.txt');
if (await file.exists()) return new Response(file);
return new Response('Not found', { status: 404 });
},
};
Bun.serve({
port: Number(Bun.env.PORT) || 3000,
fetch(req) {
const handler = routes[new URL(req.url).pathname];
return handler ? handler(req) : new Response('Not found', { status: 404 });
},
});Edge Cases
Node.js API gaps: Some built-ins (
vm,worker_threads) partial. Check compat docs.Native modules: C++ addons may not load. Use
bun:ffior WASM.Watch modes:
--watchrestarts process,--hotenables HMR (experimental).Environment vars:
Bun.env.VAR_NAMEorprocess.env..envauto-loads.Large files:
Bun.file()is lazy. Stream withnew Response(file).TS transpilation: On-the-fly, no type-check. Use
bunx tsc --noEmitin CI.Package resolution: Aggressive caching. Try
bun install --forceif issues.Port conflicts: Bun crashes if port in use. Handle with try/catch.
Test isolation: Same process by default. Use
--preloador--bail.Build targets:
--target=bun(Bun only),--target=node(Node),--target=browser(client).
Checklist
- Use
Bun.serve()for simple HTTP services instead of frameworks - Use
Bun.file()/Bun.write()instead offs - Tests use
bun:testwith no external runner - Scripts use
bun runinstead ofnpm run - Production builds use
bun buildwith correct--target - Node API compatibility verified for imported modules