DEV Community

Carlos Oliva Pascual
Carlos Oliva Pascual

Posted on • Originally published at stacknotice.com

Bun Complete Guide: Runtime, Package Manager, Bundler & More (2026)

Bun is a JavaScript runtime, package manager, bundler, and test runner — all in one binary. Built on JavaScriptCore, written in Zig, designed to be fast at everything Node.js is slow at: startup, npm install, test execution, bundling.

Installation

# macOS / Linux
curl -fsSL https://bun.sh/install | bash

# Windows
powershell -c "irm bun.sh/install.ps1 | iex"

bun --version  # 1.x.x
Enter fullscreen mode Exit fullscreen mode

Package Manager

Drop-in replacement for npm, 10-30x faster on cold installs:

bun install          # install all deps
bun add express
bun add -d @types/node
bun remove lodash
bun run dev          # runs "dev" script
Enter fullscreen mode Exit fullscreen mode

Use Bun as package manager even on Node.js projects. You don't have to switch runtimes to get faster installs.

TypeScript Natively

No ts-node, no tsx, no build step:

bun script.ts           # runs TypeScript directly
bun --watch src/index.ts
bun --hot src/server.ts  # hot reload
Enter fullscreen mode Exit fullscreen mode

Bun strips types at runtime — it does not type-check. Run tsc --noEmit separately.

HTTP Server

const server = Bun.serve({
  port: 3000,

  async fetch(req) {
    const url = new URL(req.url)

    if (url.pathname === '/api/posts' && req.method === 'GET') {
      const posts = await db.post.findMany({ where: { published: true } })
      return Response.json(posts)
    }

    if (url.pathname === '/api/posts' && req.method === 'POST') {
      const body = await req.json()
      const post = await db.post.create({ data: body })
      return Response.json(post, { status: 201 })
    }

    return new Response('Not Found', { status: 404 })
  }
})
Enter fullscreen mode Exit fullscreen mode

For real APIs use Hono — it's built for Bun and adds routing, middleware, and validation:

import { Hono } from 'hono'
import { zValidator } from '@hono/zod-validator'
import { z } from 'zod'

const app = new Hono()

app.get('/api/posts', async (c) => {
  const posts = await db.post.findMany({ where: { published: true } })
  return c.json(posts)
})

app.post('/api/posts',
  zValidator('json', z.object({ title: z.string(), content: z.string() })),
  async (c) => {
    const data = c.req.valid('json')
    const post = await db.post.create({ data })
    return c.json(post, 201)
  }
)

export default { port: 3000, fetch: app.fetch }
Enter fullscreen mode Exit fullscreen mode

File APIs

// Read
const text = await Bun.file('./data.json').text()
const json = await Bun.file('./data.json').json<MyType>()

// Write
await Bun.write('./output.txt', 'Hello, World!')
await Bun.write('./data.json', JSON.stringify(data, null, 2))

// Check existence
const exists = await Bun.file('./config.json').exists()
Enter fullscreen mode Exit fullscreen mode

Built-in SQLite

No npm install needed:

import { Database } from 'bun:sqlite'

const db = new Database('myapp.db')

db.run(`
  CREATE TABLE IF NOT EXISTS posts (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    title TEXT NOT NULL,
    slug TEXT UNIQUE NOT NULL,
    content TEXT NOT NULL,
    published INTEGER DEFAULT 0
  )
`)

// Always use prepared statements
const insertPost = db.prepare(
  'INSERT INTO posts (title, slug, content) VALUES (?, ?, ?)'
)
const getPost = db.prepare('SELECT * FROM posts WHERE slug = ?')

insertPost.run('Hello', 'hello', 'Content')
const post = getPost.get('hello')
Enter fullscreen mode Exit fullscreen mode

Or use Drizzle ORM with the Bun adapter:

import { drizzle } from 'drizzle-orm/bun-sqlite'
import { Database } from 'bun:sqlite'

const db = drizzle(new Database('myapp.db'))
const posts = await db.select().from(postsTable)
Enter fullscreen mode Exit fullscreen mode

Test Runner

Jest-compatible, near-instant startup:

bun test
bun test --watch
bun test --coverage
Enter fullscreen mode Exit fullscreen mode
import { describe, it, expect, beforeEach, mock } from 'bun:test'

describe('Post operations', () => {
  it('creates a post with correct slug', async () => {
    const post = await createPost({ title: 'Hello World', content: '...' })
    expect(post.slug).toBe('hello-world')
  })

  it('returns null for non-existent slug', async () => {
    const post = await getPostBySlug('does-not-exist')
    expect(post).toBeNull()
  })
})
Enter fullscreen mode Exit fullscreen mode

Most Jest tests run without modification.

Bun Shell

Cross-platform scripting in TypeScript:

import { $ } from 'bun'

// Run commands
await $`git add -A && git commit -m "commit"`

// Capture output
const result = await $`git log --oneline -5`.text()
const lines = await $`git diff --name-only main`.lines()

// Check exit code
const { exitCode } = await $`npm test`.nothrow()
if (exitCode !== 0) process.exit(1)
Enter fullscreen mode Exit fullscreen mode

Works on Windows, macOS, and Linux with the same syntax.

Environment Variables

// .env loaded automatically — no dotenv package needed
const apiKey = process.env.API_KEY
const dbUrl = Bun.env.DATABASE_URL

// Type-safe validation still recommended
import { z } from 'zod'
const env = z.object({
  DATABASE_URL: z.string().url(),
  PORT: z.coerce.number().default(3000)
}).parse(process.env)
Enter fullscreen mode Exit fullscreen mode

Bundler

bun build src/index.ts --outdir dist
bun build src/app.tsx --outdir dist --target browser --minify
Enter fullscreen mode Exit fullscreen mode

When to Use Bun in Production

Good fit:

  • API servers (especially with Hono/Elysia)
  • CLI tools and scripts
  • Background workers
  • Serverless functions (faster cold starts)

Be cautious if:

  • Your app uses native Node.js addons (.node files)
  • You rely on specific Node.js internal APIs

Check bun.sh/nodejs-compat for current compatibility status.

Bun vs Node.js vs Deno

Bun Node.js Deno
TypeScript Native Needs tsx Native
Test runner Built-in Jest/Vitest Built-in
Bundler Built-in webpack/esbuild Built-in
SQLite Built-in better-sqlite3 Built-in
Package manager Built-in (fast) npm/pnpm JSR/npm
Production maturity Growing Mature Growing

The entry point: use Bun as your package manager on existing Node.js projects. bun install instead of npm install — no other changes needed. From there, adopt more features as it makes sense.


Full article at stacknotice.com/blog/bun-complete-guide-2026

Top comments (0)