Skip to main content
Bun is a fast all-in-one JavaScript runtime that includes a bundler, test runner, and package manager. Hono works seamlessly with Bun and takes advantage of its performance.

Quick Start

Create a new Hono project for Bun:
bun create hono my-app
Select “bun” as your template when prompted.

Installation

Add Hono to your existing Bun project:
bun add hono

Basic Usage

Create your application in index.ts:
index.ts
import { Hono } from 'hono'

const app = new Hono()

app.get('/', (c) => c.text('Hello Bun!'))

app.get('/api/hello', (c) => {
  return c.json({ message: 'Hello from Bun!' })
})

export default app
Bun automatically uses the default export as the fetch handler.

Running Your App

Run your Hono app with Bun:
bun run index.ts
Or use the development mode with hot reload:
bun --hot run index.ts

Adapter Features

The Bun adapter provides several utilities:

Static Files

Serve static files from a directory:
import { Hono } from 'hono'
import { serveStatic } from 'hono/bun'

const app = new Hono()

app.use('/static/*', serveStatic({ root: './' }))
app.use('/favicon.ico', serveStatic({ path: './favicon.ico' }))

app.get('/', (c) => c.text('Hello!'))

export default app

WebSocket Support

Bun has native WebSocket support. Use Hono’s Bun adapter for WebSockets:
import { Hono } from 'hono'
import { createBunWebSocket } from 'hono/bun'

const app = new Hono()

const { upgradeWebSocket, websocket } = createBunWebSocket()

app.get(
  '/ws',
  upgradeWebSocket((c) => {
    return {
      onMessage(event, ws) {
        console.log(`Message from client: ${event.data}`)
        ws.send('Hello from server!')
      },
      onClose: () => {
        console.log('Connection closed')
      },
    }
  })
)

// Export with websocket handler
export default {
  port: 3000,
  fetch: app.fetch,
  websocket,
}

Connection Info

Get connection information including IP address:
import { Hono } from 'hono'
import { getConnInfo } from 'hono/bun'

const app = new Hono()

app.get('/info', (c) => {
  const info = getConnInfo(c)
  return c.json({
    remote: info.remote,
  })
})

export default app

Accessing Bun Server

Access the underlying Bun server instance:
import { Hono } from 'hono'
import { getBunServer } from 'hono/bun'
import type { Server } from 'bun'

const app = new Hono()

app.get('/server', (c) => {
  const server = getBunServer<Server>(c)
  
  return c.json({
    port: server?.port,
    hostname: server?.hostname,
  })
})

export default app

Static Site Generation (SSG)

Generate static files from your Hono app:
import { Hono } from 'hono'
import { toSSG } from 'hono/bun'

const app = new Hono()

app.get('/', (c) => c.html('<h1>Home</h1>'))
app.get('/about', (c) => c.html('<h1>About</h1>'))

// Generate static files
toSSG(app, { dir: './dist' })

Custom Server Configuration

Configure the Bun server with custom options:
index.ts
import { Hono } from 'hono'

const app = new Hono()

app.get('/', (c) => c.text('Hello!'))

export default {
  port: 3000,
  hostname: '0.0.0.0',
  fetch: app.fetch,
  development: process.env.NODE_ENV !== 'production',
}

Environment Variables

Bun loads .env files automatically:
.env
API_KEY=your-secret-key
DATABASE_URL=postgres://localhost/mydb
Access environment variables:
import { Hono } from 'hono'

const app = new Hono()

app.get('/config', (c) => {
  return c.json({
    apiKey: process.env.API_KEY,
    dbUrl: process.env.DATABASE_URL,
  })
})

export default app

Using Bun APIs

File System

Use Bun’s fast file system APIs:
import { Hono } from 'hono'

const app = new Hono()

app.get('/file/:name', async (c) => {
  const name = c.req.param('name')
  const file = Bun.file(`./files/${name}`)
  
  if (!(await file.exists())) {
    return c.notFound()
  }
  
  return c.body(await file.arrayBuffer())
})

export default app

SQLite

Use Bun’s built-in SQLite:
import { Hono } from 'hono'
import { Database } from 'bun:sqlite'

const app = new Hono()
const db = new Database('mydb.sqlite')

app.get('/users', (c) => {
  const users = db.query('SELECT * FROM users').all()
  return c.json(users)
})

app.post('/users', async (c) => {
  const body = await c.req.json()
  db.run('INSERT INTO users (name, email) VALUES (?, ?)', [body.name, body.email])
  return c.json({ message: 'User created' })
})

export default app

Testing

Bun includes a built-in test runner:
index.test.ts
import { expect, test } from 'bun:test'
import app from './index'

test('GET /', async () => {
  const res = await app.request('http://localhost/')
  expect(res.status).toBe(200)
  expect(await res.text()).toBe('Hello Bun!')
})

test('GET /api/hello', async () => {
  const res = await app.request('http://localhost/api/hello')
  expect(res.status).toBe(200)
  const json = await res.json()
  expect(json.message).toBe('Hello from Bun!')
})
Run tests:
bun test

Package Scripts

Set up scripts in package.json:
package.json
{
  "name": "my-hono-app",
  "scripts": {
    "dev": "bun --hot run index.ts",
    "start": "bun run index.ts",
    "test": "bun test"
  },
  "dependencies": {
    "hono": "^4.0.0"
  },
  "devDependencies": {
    "bun-types": "latest"
  }
}
Run scripts:
bun run dev   # Development with hot reload
bun run start # Production
bun test      # Run tests

Deployment

Standalone Binary

Build a standalone executable:
bun build --compile --minify --sourcemap ./index.ts --outfile myapp
Run the binary:
./myapp

Docker

Create a Dockerfile:
Dockerfile
FROM oven/bun:1

WORKDIR /app

COPY package.json bun.lockb ./
RUN bun install --frozen-lockfile

COPY . .

EXPOSE 3000

CMD ["bun", "run", "index.ts"]
Build and run:
docker build -t my-hono-app .
docker run -p 3000:3000 my-hono-app

VPS/Cloud Deployment

1

Install Bun on your server

curl -fsSL https://bun.sh/install | bash
2

Clone and install dependencies

git clone https://github.com/your-username/my-app.git
cd my-app
bun install
3

Run with process manager

Use PM2 or systemd:
pm2 start "bun run index.ts" --name my-hono-app

Best Practices

The --hot flag provides instant feedback during development.
Use Bun.file(), bun:sqlite, and other built-in APIs for better performance.
Bun runs TypeScript natively without a build step.
Bun’s test runner is fast and includes built-in matchers.

Performance Tips

  • Bun is significantly faster than Node.js for I/O operations
  • Use Bun.file() for file operations instead of fs module
  • Leverage Bun’s native WebSocket implementation
  • Use bun:sqlite for embedded databases

Resources

Bun Documentation

Official Bun documentation

Bun Runtime APIs

Explore Bun’s built-in APIs

Build docs developers (and LLMs) love