Skip to content

Web Development · Backend Frameworks

Hono in 2026: The Web Framework Built for the Edge

Hono started as a tiny router for Cloudflare Workers and grew into the default choice for edge APIs. Here is what it does well, where it fits, and why the 'runs everywhere' claim is actually true.

Anurag Verma

Anurag Verma

7 min read

Hono in 2026: The Web Framework Built for the Edge

Sponsored

Share

When Cloudflare Workers first became mainstream, the available framework options were thin. Express didn’t work because it depended on Node.js APIs. Fastify had the same problem. You were writing routing logic by hand, matching path strings with regex and tediously parsing request bodies.

Hono came out of that constraint. It was built from scratch to run in V8-based runtimes without any Node.js dependencies. In 2026, it runs on Cloudflare Workers, Deno Deploy, Bun, Node.js, Vercel Edge Functions, AWS Lambda, Netlify Edge Functions, and the browser. The same application code works across all of them with minimal configuration changes.

That’s a real claim. Here’s what it actually means in practice.

The Core API

Hono’s routing API will be immediately familiar if you’ve used Express or Fastify:

import { Hono } from 'hono'

const app = new Hono()

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

app.get('/users/:id', async (c) => {
  const id = c.req.param('id')
  const user = await db.users.findOne(id)

  if (!user) return c.json({ error: 'Not found' }, 404)
  return c.json(user)
})

app.post('/users', async (c) => {
  const body = await c.req.json()
  const user = await db.users.create(body)
  return c.json(user, 201)
})

export default app

The context object c is the main interface. It provides request parsing, response helpers, cookies, headers, and environment access. The API is small enough to learn in an hour.

The runtime-specific entry points look like this:

// Cloudflare Workers — export default is enough
export default app

// Node.js
import { serve } from '@hono/node-server'
serve(app)

// Bun
Bun.serve(app)

// Deno
Deno.serve(app.fetch)

The app.fetch method is a standard Request => Response function — the same interface used by all edge runtimes. That’s the portability mechanism.

Middleware

Hono ships built-in middleware for the things you need on every API:

import { Hono } from 'hono'
import { cors } from 'hono/cors'
import { logger } from 'hono/logger'
import { prettyJSON } from 'hono/pretty-json'
import { bearerAuth } from 'hono/bearer-auth'

const app = new Hono()

// Apply globally
app.use('*', logger())
app.use('*', cors({
  origin: ['https://app.example.com'],
  allowMethods: ['GET', 'POST', 'PUT', 'DELETE'],
}))

// Apply to specific routes
app.use('/api/*', bearerAuth({ token: process.env.API_TOKEN }))
app.use('/api/*', prettyJSON())

The built-in list covers: logger, CORS, bearer auth, basic auth, JWT, compress, ETag, cache, rate limiting, CSRF, body limit, and several more. Most small-to-medium APIs don’t need third-party middleware packages.

Custom middleware follows the same pattern as any middleware system:

app.use('/admin/*', async (c, next) => {
  const token = c.req.header('Authorization')?.replace('Bearer ', '')

  if (!token) return c.json({ error: 'Unauthorized' }, 401)

  try {
    const payload = await verifyJWT(token, c.env.JWT_SECRET)
    c.set('userId', payload.sub)
    await next()
  } catch {
    return c.json({ error: 'Invalid token' }, 401)
  }
})

// Downstream handlers can read from context
app.get('/admin/dashboard', (c) => {
  const userId = c.get('userId')
  return c.json({ userId, dashboard: '...' })
})

TypeScript Support

Hono’s TypeScript integration is well-designed. You can define environment bindings (for Cloudflare Workers), variables, and custom context values with full type safety:

type Bindings = {
  DATABASE_URL: string
  JWT_SECRET: string
  KV_STORE: KVNamespace // Cloudflare KV type
}

type Variables = {
  userId: string
}

const app = new Hono<{ Bindings: Bindings; Variables: Variables }>()

app.use('/protected/*', async (c, next) => {
  // c.env.DATABASE_URL is typed as string
  // c.env.KV_STORE is typed as KVNamespace
  c.set('userId', 'user_123') // type-checked
  await next()
})

app.get('/protected/profile', (c) => {
  const userId = c.get('userId') // typed as string
  return c.json({ userId })
})

RPC is where the TypeScript goes furthest. Hono can generate a fully-typed client from your route definitions:

// server.ts
const routes = app
  .get('/users/:id', (c) => {
    return c.json({ id: c.req.param('id'), name: 'Alice' })
  })
  .post('/users', async (c) => {
    const body = await c.req.json<{ name: string }>()
    return c.json({ id: 'new', ...body }, 201)
  })

export type AppType = typeof routes

// client.ts
import { hc } from 'hono/client'
import type { AppType } from './server'

const client = hc<AppType>('https://api.example.com')

// Fully typed — parameter names, response shapes, everything
const user = await client.users[':id'].$get({ param: { id: '123' } })
const json = await user.json()
// json is typed as { id: string, name: string }

The RPC client is the closest thing in the JavaScript ecosystem to tRPC without requiring a specific framework. If your frontend and backend are in the same repo, this removes almost all manual type duplication.

Routing

Hono supports grouped routes, which makes larger APIs manageable:

import { Hono } from 'hono'

const api = new Hono().basePath('/api')

const users = new Hono()
users.get('/', (c) => c.json({ users: [] }))
users.get('/:id', (c) => c.json({ id: c.req.param('id') }))
users.post('/', async (c) => c.json(await c.req.json(), 201))

const products = new Hono()
products.get('/', (c) => c.json({ products: [] }))
products.post('/', async (c) => c.json(await c.req.json(), 201))

api.route('/users', users)
api.route('/products', products)

export default api

This keeps each resource in its own file and composes cleanly. The router is a trie-based implementation, which keeps performance predictable as route counts grow.

Performance

Hono is fast. On Cloudflare Workers, where cold start time matters, it adds essentially no overhead. The bundle size is around 14KB (uncompressed) for the core, which matters on edge runtimes where bundle size affects startup time.

The benchmark comparisons are Hono’s homepage selling point, and the numbers are real for edge runtimes. On Node.js, the differences between Hono and Fastify or Elysia are smaller and mostly irrelevant for typical API workloads. The performance case for Hono on Node.js is about consistency with your edge deployments, not about raw throughput.

When Hono Is the Right Choice

Building a Cloudflare Workers API. This is the original use case and still the strongest one. Hono was designed for Workers and the integration is frictionless. Environment bindings, Durable Objects, KV, R2, D1 — all work naturally through the context.

An edge-deployed backend that needs to run locally too. If you’re deploying to Vercel Edge Functions or Netlify Edge, Hono’s portability means your local dev server runs the same code without emulation layers.

An API that needs to be small and dependency-light. Serverless cold starts are sensitive to bundle size. A Hono API with a few middleware packages is far smaller than an equivalent Express/Fastify setup with the same ecosystem dependencies.

A team already using TypeScript that wants end-to-end type safety. The RPC client pairs well with React apps and makes type-safe API calls without code generation steps.

Where It Doesn’t Fit

For a conventional Node.js server-rendered app (not edge), Hono’s advantages are less clear. Fastify is faster on raw Node.js benchmarks, has a larger plugin ecosystem, and has more production track record on that runtime. Neither is a strong argument against Hono, but if your stack is firmly Node.js, the “runs everywhere” advantage doesn’t apply.

For apps that need heavy request processing — file parsing, streaming, complex multipart handling — Hono’s streaming support exists but is less developed than mature Node.js frameworks. These are fixable gaps, not architectural limitations.

Getting Started

# Cloudflare Workers
npm create cloudflare@latest my-api -- --template hono

# Node.js
npm create hono@latest my-api
# Select Node.js runtime

# Bun
bun create hono my-api

The scaffold includes the runtime-specific entry point, basic routing, and TypeScript configuration. From there, it’s just adding routes.

Hono filled a specific gap — a fast, minimal, TypeScript-first router that didn’t need Node.js — and it filled it well. In 2026 the edge runtime ecosystem has matured significantly, and Hono is the default framework choice for anything running outside a traditional Node.js server.

Sponsored

Enjoyed it? Pass it on.

Share this article.

Sponsored

The dispatch

Working notes from
the studio.

A short letter twice a month — what we shipped, what broke, and the AI tools earning their keep.

No spam, ever. Unsubscribe anytime.

Discussion

Join the conversation.

Comments are powered by GitHub Discussions. Sign in with your GitHub account to leave a comment.

Sponsored