Skip to main content

Overview

Hono is built for speed. With its lightweight architecture and Web Standard APIs, Hono delivers excellent performance out of the box. However, there are additional strategies you can use to optimize your applications even further.

Router Performance

Hono uses the RegExpRouter by default, which provides excellent performance:
import { Hono } from 'hono'

// The default router is already optimized
const app = new Hono()

app.get('/posts/:id', (c) => c.json({ id: c.req.param('id') }))
app.get('/users/:userId/posts/:postId', (c) => {
  return c.json({
    userId: c.req.param('userId'),
    postId: c.req.param('postId')
  })
})

Choosing the Right Router

Hono includes multiple routers optimized for different scenarios:
  • RegExpRouter - Default, fast for most applications
  • SmartRouter - Automatically chooses the best router
  • LinearRouter - Simple, good for small apps
  • PatternRouter - Memory efficient for simple patterns
import { RegExpRouter } from 'hono/router/reg-exp-router'
import { Hono } from 'hono'

const app = new Hono({ router: new RegExpRouter() })

Minimize Middleware

Only use middleware where needed:
// Good: Apply middleware to specific routes
app.use('/api/*', logger())
app.use('/admin/*', authMiddleware)

// Avoid: Global middleware when not needed
// app.use('*', someHeavyMiddleware)

Response Optimization

Stream Large Responses

Use streaming for large responses:
import { stream } from 'hono/streaming'

app.get('/large-data', (c) => {
  return stream(c, async (stream) => {
    // Stream data in chunks
    for (let i = 0; i < 100; i++) {
      await stream.write(`Chunk ${i}\n`)
      await stream.sleep(100)
    }
  })
})

Use Compression

Compress responses to reduce bandwidth:
import { Hono } from 'hono'
import { compress } from 'hono/compress'

const app = new Hono()

// Apply compression middleware
app.use('*', compress())

app.get('/api/data', (c) => {
  const largeData = generateLargeData()
  return c.json(largeData)
})

Cache Responses

Implement caching for frequently accessed data:
import { cache } from 'hono/cache'

// Cache for 1 hour
app.get(
  '/api/posts',
  cache({
    cacheName: 'posts-cache',
    cacheControl: 'max-age=3600',
  })
)

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

JSON Performance

Use c.json() Efficiently

The c.json() method is optimized for performance:
app.get('/users', async (c) => {
  const users = await db.users.findMany()
  return c.json(users) // Fast JSON serialization
})

Avoid Unnecessary Serialization

If you already have JSON, use c.text() with appropriate headers:
app.get('/data', async (c) => {
  const jsonString = await redis.get('cached-data')
  return c.text(jsonString, 200, {
    'Content-Type': 'application/json',
  })
})

Database Optimization

Connection Pooling

Reuse database connections:
import { Hono } from 'hono'
import { createPool } from 'some-db-library'

const pool = createPool({
  max: 10,
  connectionString: 'postgresql://...',
})

const app = new Hono()

app.get('/users', async (c) => {
  const client = await pool.connect()
  try {
    const result = await client.query('SELECT * FROM users')
    return c.json(result.rows)
  } finally {
    client.release()
  }
})

Query Optimization

Fetch only what you need:
// Good: Select specific fields
app.get('/users', async (c) => {
  const users = await db.users.findMany({
    select: { id: true, name: true, email: true }
  })
  return c.json(users)
})

// Avoid: Fetching all fields when unnecessary
// const users = await db.users.findMany()

Use Indexes

Ensure your database queries use indexes:
app.get('/users/:id', async (c) => {
  const id = c.req.param('id')
  // Make sure 'id' field is indexed
  const user = await db.users.findUnique({ where: { id } })
  return c.json(user)
})

Async Operations

Parallel Requests

Use Promise.all() for parallel operations:
app.get('/dashboard', async (c) => {
  // Run queries in parallel
  const [users, posts, comments] = await Promise.all([
    db.users.count(),
    db.posts.count(),
    db.comments.count(),
  ])

  return c.json({ users, posts, comments })
})

Background Tasks

Offload heavy tasks using waitUntil:
app.post('/events', async (c) => {
  const event = await c.req.json()

  // Respond quickly
  const response = c.json({ received: true })

  // Process in background (Cloudflare Workers)
  c.executionCtx.waitUntil(
    processEventAsync(event)
  )

  return response
})

Memory Management

Avoid Memory Leaks

Don’t store large objects in global scope:
// Avoid: Global cache that grows indefinitely
// const cache = {}

// Good: Use external cache with TTL
import { LRUCache } from 'lru-cache'

const cache = new LRUCache({
  max: 100,
  ttl: 1000 * 60 * 5, // 5 minutes
})

app.get('/data/:id', async (c) => {
  const id = c.req.param('id')
  
  if (cache.has(id)) {
    return c.json(cache.get(id))
  }
  
  const data = await fetchData(id)
  cache.set(id, data)
  return c.json(data)
})

Static Assets

Use CDN for Static Files

Serve static files from a CDN when possible:
import { serveStatic } from 'hono/cloudflare-workers'

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

Set Appropriate Cache Headers

app.get('/static/*', serveStatic({ 
  root: './',
  onNotFound: (path, c) => {
    return c.text(`${path} not found`, 404)
  },
}))

// Add cache headers
app.use('/static/*', async (c, next) => {
  await next()
  c.header('Cache-Control', 'public, max-age=31536000') // 1 year
})

Bundle Size

Use hono/tiny

For ultra-small bundle size, use the tiny preset:
import { Hono } from 'hono/tiny'

const app = new Hono()

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

export default app

Tree Shaking

Import only what you need:
// Good: Import specific utilities
import { getCookie } from 'hono/cookie'
import { cors } from 'hono/cors'

// Avoid: Importing everything
// import * as hono from 'hono'

Benchmarking

Measure your application’s performance:
import { Hono } from 'hono'
import { timing } from 'hono/timing'

const app = new Hono()

app.use('*', timing())

app.get('/api/data', async (c) => {
  const data = await fetchData()
  return c.json(data)
})

// Check Server-Timing header in response

Platform-Specific Optimizations

Cloudflare Workers

// Use KV for fast edge caching
app.get('/cached/:key', async (c) => {
  const key = c.req.param('key')
  const cached = await c.env.KV.get(key)
  
  if (cached) {
    return c.json(JSON.parse(cached))
  }
  
  const data = await fetchData(key)
  await c.env.KV.put(key, JSON.stringify(data), {
    expirationTtl: 300 // 5 minutes
  })
  
  return c.json(data)
})

Deno Deploy

// Leverage Deno's native performance
import { Hono } from 'hono'

const app = new Hono()

app.get('/', (c) => c.text('Fast on Deno!'))

Deno.serve(app.fetch)

Bun

// Use Bun's fast runtime
import { Hono } from 'hono'

const app = new Hono()

app.get('/', (c) => c.text('Blazing fast with Bun!'))

export default {
  port: 3000,
  fetch: app.fetch,
}

Best Practices

Measure performance to identify bottlenecks before making optimizations.
Cache responses and data that are frequently accessed but rarely change.
Use indexes, limit result sets, and fetch only needed fields.
Deploy to edge platforms like Cloudflare Workers for faster response times.
Only apply middleware to routes that need it.

Build docs developers (and LLMs) love