Loading...
Loading...
Bun runtime API reference for TypeScript scripts. Covers Bun.file(), Bun.write(), Bun.$() shell, Bun.spawn(), Bun.Glob, Bun.env, bun:sqlite, Bun.sql() for PostgreSQL/MySQL via DATABASE_URL, Bun.s3 for S3-compatible storage, Bun.redis for Redis/Valkey, Bun.Archive for tarballs, Bun.Image image processing, Bun.WebView headless browser automation, Bun.cron in-process scheduler, JSONC/JSON5/JSONL/markdown (named imports), Bun.hash, Bun.password, compression, and scripting utilities. Use when writing scripts, automating tasks, querying databases, working with S3 storage, Redis caching, processing images, automating a headless browser, parsing markdown/JSON variants, or doing file processing in a Bun project. Signals: bun.lock, bunfig.toml, DATABASE_URL, REDIS_URL, AWS_ACCESS_KEY_ID, Bun.$ usage Not for bun CLI commands (bun-cli skill), non-Bun runtimes, or ORM CLI tooling
npx skill4agent add dmythro/agent-skills bun-apitscts-node.tsbun file.tsbun.lockbun.lockbbunfig.toml@types/bunbun file.tsnode file.tsbun:sqliteDATABASE_URL.envBun.sql()AWS_ACCESS_KEY_IDBun.s3REDIS_URLVALKEY_URLBun.redishttp.createServerconst server = Bun.serve({
port: 3000,
fetch(req: Request): Response | Promise<Response> {
const url = new URL(req.url)
if (url.pathname === '/api/health') {
return Response.json({ status: 'ok' })
}
if (url.pathname === '/api/data' && req.method === 'POST') {
const body = await req.json()
return Response.json({ received: body })
}
return new Response('Not Found', { status: 404 })
},
error(error: Error): Response {
return new Response(`Error: ${error.message}`, { status: 500 })
},
})
console.log(`Listening on ${server.url}`)server.stop()server.reload()server.requestIP(req)server.upgrade(req)Reference: Seefor TLS, WebSocket upgrade, streaming responses, static file serving, and full server API.references/http-server.md
Bun.listen()Bun.connect()Bun.udpSocket()WebSocketfetch()const server = Bun.listen({
hostname: '127.0.0.1',
port: 8080,
socket: {
open(socket) { socket.write('welcome\n') },
data(socket, data) { /* Buffer */ },
},
})Reference: Seefor TCP/UDP handlers, Unix sockets, the WebSocket client (references/networking.md), andws+unix://transport options (HTTP/2, HTTP/3, proxies, system CA).fetch()
// Create a BunFile reference (lazy, no read yet)
const file = Bun.file('path/to/file.txt')
// Read contents
const text = await file.text() // string
const json = await file.json() // parsed JSON
const bytes = await file.arrayBuffer() // ArrayBuffer
const stream = file.stream() // ReadableStream
const blob = await file.blob() // Blob
// File metadata
file.size // Size in bytes
file.type // MIME type (auto-detected)
file.name // File path
await file.exists() // Boolean
// Read from URL
const remote = Bun.file('https://example.com/data.json')// Write string
await Bun.write('output.txt', 'content')
// Write from BunFile (efficient copy)
await Bun.write('copy.txt', Bun.file('original.txt'))
// Write JSON
await Bun.write('data.json', JSON.stringify(data, null, 2))
// Write Uint8Array / ArrayBuffer
await Bun.write('binary.dat', new Uint8Array([1, 2, 3]))
// Write Response body
await Bun.write('page.html', await fetch('https://example.com'))
// Write to stdout
await Bun.write(Bun.stdout, 'Hello\n')Bun.stdin // BunFile for stdin
Bun.stdout // BunFile for stdout
Bun.stderr // BunFile for stderr
// Read all of stdin
const input = await Bun.stdin.text()
// Stream stdin line by line
for await (const chunk of Bun.stdin.stream()) {
// process chunk (Uint8Array)
}// JSON transform
const data = await Bun.file('input.json').json()
data.version = '2.0.0'
await Bun.write('output.json', JSON.stringify(data, null, 2))
// File generation from template
const template = await Bun.file('template.html').text()
const output = template.replace('{{title}}', 'My Page')
await Bun.write('index.html', output)
// Check if file exists before reading
const file = Bun.file('config.json')
if (await file.exists()) {
const config = await file.json()
}Reference: Seefor BunFile interface, write overloads, streaming, MIME detection, and file watching.references/file-io.md
import { $ } from 'bun'
// Basic execution
const result = await $`ls -la`
console.log(result.text()) // stdout as string
// With interpolation (auto-escaped)
const dir = 'my folder'
await $`ls ${dir}` // Safe: "my folder" is properly quoted
// Output methods
const output = await $`echo hello`
output.text() // "hello\n"
output.json() // Parse stdout as JSON
output.lines() // string[] (splits on newlines)
output.bytes() // Uint8Array
output.blob() // Blob
output.exitCode // number
output.stderr // Buffer
// Piping
await $`cat file.txt | grep pattern | wc -l`
// Quiet mode (suppress stdout)
await $`npm install`.quiet()
// No-throw mode (don't throw on non-zero exit)
const result = await $`command-that-might-fail`.nothrow()
if (result.exitCode !== 0) {
console.error('Failed:', result.stderr.toString())
}
// Combined
await $`risky-command`.quiet().nothrow()
// Environment variables
await $`echo $HOME`.env({ HOME: '/custom' })
// Working directory
await $`ls`.cwd('/tmp')
// Redirect to file
await $`echo hello > output.txt`
await $`cat < input.txt`
// Pipe between commands
const input = Buffer.from('hello')
await $`cat`.stdin(input)const proc = Bun.spawn(['command', 'arg1', 'arg2'], {
cwd: '/path',
env: { ...process.env, CUSTOM: 'value' },
stdin: 'pipe', // 'pipe' | 'inherit' | 'ignore' | BunFile | Blob | Response
stdout: 'pipe', // 'pipe' | 'inherit' | 'ignore' | BunFile
stderr: 'pipe', // 'pipe' | 'inherit' | 'ignore' | BunFile
onExit(proc, exitCode, signalCode, error) {
// Called when process exits
},
})
// Write to stdin
proc.stdin.write('input data')
proc.stdin.end()
// Read stdout
const output = await new Response(proc.stdout).text()
// Wait for completion
await proc.exited // Promise<number> (exit code)
// Kill
proc.kill() // SIGTERM
proc.kill('SIGKILL') // Specific signalconst result = Bun.spawnSync(['command', 'arg1'], {
cwd: '/path',
env: { ...process.env },
})
result.exitCode // number
result.stdout // Buffer
result.stderr // Buffer
result.success // booleanReference: Seefor complete $ API, spawn options, IPC, and signal handling.references/shell-and-process.md
const glob = new Bun.Glob('**/*.ts')
// Async iteration
for await (const path of glob.scan({ cwd: './src', onlyFiles: true })) {
console.log(path)
}
// Sync iteration
for (const path of glob.scanSync('./src')) {
console.log(path)
}
// Test if a path matches
glob.match('src/index.ts') // true
glob.match('README.md') // false
// Scan options
glob.scan({
cwd: './src', // Directory to scan (default: '.')
dot: false, // Include dotfiles (default: false)
onlyFiles: true, // Skip directories (default: true)
absolute: false, // Return absolute paths (default: false)
followSymlinks: false, // Follow symlinks (default: false)
})Bun.env.NODE_ENV // Environment variable (same as process.env)
Bun.env.DATABASE_URL // Typed access
Bun.argv // string[] — [bunPath, scriptPath, ...args]
// Equivalent: process.argv
Bun.main // Absolute path to the entry point script
import.meta.dir // Directory of current file
import.meta.file // Filename of current file
import.meta.path // Full path of current file
import.meta.dirname // Same as import.meta.dir (Node.js compat)
import.meta.filename // Same as import.meta.path (Node.js compat)DATABASE_URL.envimport { sql, SQL } from "bun"
// Default instance -- auto-connects using DATABASE_URL from environment
const users = await sql`SELECT * FROM users WHERE active = ${true} LIMIT ${10}`
// Explicit connection
const db = new SQL("postgres://user:pass@localhost:5432/mydb")
const results = await db`SELECT * FROM users`
// MySQL
const mysql = new SQL("mysql://user:pass@localhost:3306/mydb")const user = { name: "Alice", email: "alice@example.com" }
// Insert -- expands object to (column1, column2) VALUES (val1, val2)
const [newUser] = await sql`INSERT INTO users ${sql(user)} RETURNING *`
// Bulk insert
await sql`INSERT INTO users ${sql([user1, user2, user3])}`
// Update -- expands to SET column1 = val1, column2 = val2
await sql`UPDATE users SET ${sql(updates)} WHERE id = ${userId}`await sql.begin(async (tx) => {
const [user] = await tx`INSERT INTO users (name) VALUES (${"Alice"}) RETURNING *`
await tx`INSERT INTO audit_log (action, user_id) VALUES ('created', ${user.id})`
})
// Auto-committed on success, rolled back on errorReference: Seefor connection options, pool management, savepoints, MySQL specifics, and prepared statement configuration.references/sql-client.md
AWS_ACCESS_KEY_IDimport { s3, write } from "bun"
// Reads credentials from AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, etc.
const file = s3.file("data.json") // Lazy reference, no network yet
// Read from S3
const data = await file.json() // Download and parse JSON
const text = await file.text() // Download as string
const stream = file.stream() // ReadableStream
// Upload to S3
await write(s3.file("output.json"), JSON.stringify(data))
// Presigned URLs (synchronous, no network request)
const url = s3.presign("report.pdf", {
expiresIn: 3600, // 1 hour
method: "PUT", // For uploads
acl: "public-read",
})
// Delete
await file.delete()Reference: Seefor custom S3Client, presign options, multipart upload, and serving from Bun.serve.references/s3-client.md
REDIS_URLVALKEY_URLimport { redis, RedisClient } from "bun"
// Default client -- reads REDIS_URL from environment
await redis.set("key", "value")
const value = await redis.get("key") // "value" | null
// With expiration
await redis.set("session", "data", "EX", 3600)
// Counter operations
await redis.incr("counter")
await redis.incrby("counter", 5)
// Hash operations
await redis.hset("user:1", "name", "Alice", "email", "alice@example.com")
await redis.hget("user:1", "name") // "Alice"
// Custom client
const client = new RedisClient("redis://user:pass@host:6379")Reference: Seefor all commands (strings, hashes, lists, sets, sorted sets), pub/sub, pipelines, and common patterns.references/redis-client.md
// Create archive
const archive = new Bun.Archive({
"hello.txt": "Hello, World!",
"config.json": JSON.stringify({ key: "value" }),
})
await Bun.write("archive.tar", archive)
// With gzip compression
const compressed = new Bun.Archive(
{ "hello.txt": "Hello, World!" },
{ compress: "gzip", level: 9 }
)
await Bun.write("archive.tar.gz", compressed)
// Extract (auto-detects gzip)
const tarball = await Bun.file("archive.tar.gz").bytes()
const extracted = new Bun.Archive(tarball)jsonc-parserjson5import { JSONC } from "bun"
const config = JSONC.parse(`{
// Database config
"host": "localhost",
"port": 5432, // default port
}`)tsconfig.jsonjsconfig.jsonpackage.jsonbun.lock.jsoncimport config from "./config.jsonc"import { JSON5, JSONL, markdown, cron } from "bun"
// JSON5 -- superset of JSON (comments, unquoted keys, trailing commas)
const config = JSON5.parse(`{ unquoted: 'value', /* comment */ }`)
// JSONL -- newline-delimited JSON
const records = JSONL.parse('{"a":1}\n{"a":2}\n')
// Markdown -- built-in CommonMark parser (replaces marked, remark, etc.)
const html = markdown.html("# Title\n\n**Bold** text.")
const ansi = markdown.ansi("# Title") // ANSI terminal output (v1.3.12+)
// Cron -- in-process scheduler + expression parser
const job = cron("0 9 * * 1-5", runReport) // scheduler (v1.3.12+)
const next = cron.parse("0 9 * * 1-5") // next run as ISO string
// ANSI-aware string utilities (replace wrap-ansi, slice-ansi npm packages)
const coloredText = "\x1b[31mHello, World!\x1b[0m"
Bun.wrapAnsi(coloredText, 80) // Wrap to column width
Bun.sliceAnsi(coloredText, 0, 5) // Grapheme-aware sliceReference: Seefor full details on all parsing and utility APIs.references/utilities.md
import { Database } from 'bun:sqlite'
// Open database
const db = new Database('mydb.sqlite')
const db = new Database(':memory:') // In-memory
// Enable WAL mode (recommended)
db.exec('PRAGMA journal_mode = WAL')
// Execute statements
db.exec('CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT)')
// Prepared statements
const insert = db.prepare('INSERT INTO users (name, email) VALUES (?, ?)')
insert.run('Alice', 'alice@example.com')
// Query
const select = db.prepare('SELECT * FROM users WHERE name = ?')
const user = select.get('Alice') // Single row or null
const users = select.all('Alice') // All matching rows
// Named parameters
const stmt = db.prepare('SELECT * FROM users WHERE name = $name')
stmt.get({ $name: 'Alice' })
// Transactions
const insertMany = db.transaction((users) => {
for (const user of users) {
insert.run(user.name, user.email)
}
})
insertMany([
{ name: 'Bob', email: 'bob@example.com' },
{ name: 'Carol', email: 'carol@example.com' },
])
// Close
db.close()Reference: Seefor Database constructor, Statement API, transactions, and column types.references/sqlite-and-data.md
// Non-cryptographic (fast, for hash tables/checksums)
Bun.hash('input') // number (wyhash, fastest)
Bun.hash.crc32('input') // CRC32
// Cryptographic
new Bun.CryptoHasher('sha256').update('data').digest('hex')
// Password hashing (async, bcrypt by default)
const hash = await Bun.password.hash('password')
const hash = await Bun.password.hash('password', { algorithm: 'argon2id' })
const valid = await Bun.password.verify('password', hash)Reference: Seefor all hash algorithms, CryptoHasher streaming API, and password hashing options (bcrypt vs argon2id, cost parameters).references/hashing.md
// Gzip
const compressed = Bun.gzipSync(data) // Uint8Array → Uint8Array
const decompressed = Bun.gunzipSync(compressed)
// Deflate
const compressed = Bun.deflateSync(data)
const decompressed = Bun.inflateSync(compressed)
// Zstandard (zstd)
const compressed = Bun.zstdCompressSync(data)
const decompressed = Bun.zstdDecompressSync(compressed)
// With options
Bun.gzipSync(data, { level: 9, memLevel: 9 })
Bun.deflateSync(data, { level: 6 })
Bun.zstdCompressSync(data, { level: 3 })Uint8Array | string | ArrayBufferUint8Array// Which (find binary in PATH)
Bun.which('node') // '/usr/local/bin/node' or null
Bun.which('bun', { PATH: '/custom/bin' })
// Inspect (like console.log formatting)
Bun.inspect(obj) // string
Bun.inspect(obj, { depth: 4, colors: true })
// Module resolution
Bun.resolveSync('./module', '/from/dir') // Resolved absolute path
// Deep equality
Bun.deepEquals(a, b) // boolean (structural equality)
Bun.deepEquals(a, b, true) // Strict (differentiates 0 and -0)
// Sleep
await Bun.sleep(1000) // ms
await Bun.sleep(Bun.nanoseconds() + 1e9) // Until timestamp
// Timing
Bun.nanoseconds() // High-resolution timer (bigint)
// UUID
Bun.randomUUIDv7() // Time-ordered UUID v7
// String width (for terminal column alignment)
Bun.stringWidth('hello') // 5
Bun.stringWidth('你好') // 4 (CJK double-width)
// Peek at a promise without awaiting
const value = Bun.peek(promise) // Returns value if resolved, promise if pending
// Color detection
Bun.color('red', 'css') // 'rgb(255, 0, 0)'
Bun.color('#ff0000', 'ansi') // ANSI escape code
Bun.color('hsl(0, 100%, 50%)', 'number') // 0xff0000Reference: Seefor complete utility function signatures and examples.references/utilities.md
semver// Check if a version satisfies a range
Bun.semver.satisfies('1.2.3', '^1.0.0') // true
Bun.semver.satisfies('2.0.0', '>=1.0 <2.0') // false
Bun.semver.satisfies('1.0.0-beta', '*') // false (pre-release excluded by default)
// Sort versions (returns -1, 0, or 1)
Bun.semver.order('1.0.0', '2.0.0') // -1 (a < b)
Bun.semver.order('2.0.0', '1.0.0') // 1 (a > b)
Bun.semver.order('1.0.0', '1.0.0') // 0 (equal)
// Sort an array of versions
const versions = ['3.0.0', '1.2.0', '2.1.0']
versions.sort(Bun.semver.order) // ['1.2.0', '2.1.0', '3.0.0']import { serialize, deserialize } from 'bun:jsc'
const data = { key: 'value', nested: [1, 2, 3] }
const bytes = serialize(data) // Uint8Array
const restored = deserialize(bytes) // Original structureJSON.stringifyJSON.parseDateRegExpMapSetArrayBuffersharpjimpconst thumb = await Bun.file('upload.jpg')
.image()
.resize(400, 400, { fit: 'cover' })
.webp({ quality: 82 })
.bytes()
const { width, height, format } = await new Bun.Image(buffer).metadata()
const blur = await Bun.file('hero.jpg').image().placeholder() // thumbhash data URLReference: Seefor transforms, output formats, and metadata.references/image.md
await using view = new Bun.WebView({ width: 1280, height: 720 })
await view.navigate('https://bun.sh')
const title = await view.evaluate('document.title')
await Bun.write('page.png', await view.screenshot())Reference: Seefor the full method list and CDP access.references/webview.md
#!/usr/bin/env bun
const args = Bun.argv.slice(2)
const command = args[0]
switch (command) {
case 'generate':
await generate(args.slice(1))
break
case 'process':
await process(args.slice(1))
break
default:
console.log('Usage: script <generate|process> [args]')
process.exit(1)
}const glob = new Bun.Glob('**/*.schema.json')
for await (const path of glob.scan('./schemas')) {
const schema = await Bun.file(`./schemas/${path}`).json()
const code = generateTypeScript(schema)
const outPath = path.replace('.schema.json', '.ts')
await Bun.write(`./generated/${outPath}`, code)
}import { $ } from 'bun'
import { Database } from 'bun:sqlite'
// Fetch data
const data = await $`curl -s https://api.example.com/data`.json()
// Process and store
const db = new Database('output.sqlite')
db.exec('CREATE TABLE IF NOT EXISTS items (id TEXT PRIMARY KEY, value TEXT)')
const insert = db.prepare('INSERT OR REPLACE INTO items (id, value) VALUES (?, ?)')
const batch = db.transaction((items) => {
for (const item of items) {
insert.run(item.id, JSON.stringify(item))
}
})
batch(data.items)
db.close()Bun.file()Bun.write()fs.readFilefs.writeFileBun.$child_processBun.sql()DATABASE_URLbun:sqliteBun.GlobglobBun.CryptoHashercrypto.createHashBun.passwordbcryptargon2Bun.gzipSyncBun.zstdCompressSynczlibBun.envprocess.envimport.meta.dir__dirnameimport.meta.dirnameBun.which()whichBun.s3@aws-sdk/client-s3Bun.redisioredisredisBun.ArchivetararchiverJSONC.parse()jsonc-parserJSON5.parse()json5JSONL.parse()markdown.html()markdown.ansi()markedremarkmarkdown-itBun.wrapAnsi()wrap-ansiBun.sliceAnsi()slice-ansiBun.Imagesharpjimp