EmpireUI
Get Pro
← Blog8 min read#hono#edge#api

Hono.js Guide: Ultra-Fast Edge API for Bun, Deno and Cloudflare

Hono.js is the fastest web framework on edge runtimes. Here's how to build production-ready APIs with Bun, Deno, and Cloudflare Workers in 2026.

Developer working on edge API code with terminal and browser

What Is Hono and Why Should You Care

Hono is a small, fast web framework that runs everywhere — Cloudflare Workers, Bun, Deno, Node, AWS Lambda, you name it. It clocks in at under 14kB and achieves benchmark scores that make Express look like it's running on a hamster wheel. If you've ever shipped a Next.js API route and thought "this feels heavier than it needs to be," Hono is the answer.

The name means "flame" in Japanese, and the performance obsession shows throughout. It uses a trie-based router that the Hono team benchmarked in 2024 at roughly 3x faster than Fastify on raw routing throughput. That's not a made-up stat — their public benchmark repo is reproducible and has been independently verified by the community.

Honestly, what separates Hono from "yet another tiny framework" is the first-class TypeScript support. Route handlers get inferred types end-to-end, your path params are typed, and the c.req.valid() middleware integration with Zod gives you a full type-safe request pipeline without any extra ceremony.

Worth noting: Hono v4 shipped a complete rewrite of the middleware system. If you're reading old tutorials that use app.use() with two arguments and weird binding tricks — that's pre-v4 code. Stick to the v4 docs.

Getting Started: Hono on Bun in Under 5 Minutes

Bun is the runtime where Hono feels most at home for local dev. Zero-config TypeScript, native .env loading, and startup times measured in milliseconds. Here's the fastest path from nothing to a working API.

bun create hono my-api
cd my-api
bun install
bun run dev

That scaffolds a project with a src/index.ts entry point, a pre-configured tsconfig.json tuned for Bun, and a hot-reload dev server. Your first route is three lines:

import { Hono } from 'hono'

const app = new Hono()

app.get('/', (c) => c.text('Hello from the edge'))

export default app

The c context object is where everything lives — c.req for the incoming request, c.res for the response helpers, c.env for your environment variables. In practice, you'll spend 90% of your time inside that c object once you move past trivial examples. Get familiar with it early.

Routing, Middleware, and Request Validation

Hono's router handles all the patterns you'd expect — named params, wildcards, grouped routes — without any extra packages. Grouping routes with app.route() keeps large APIs organized without the boilerplate you'd write in Express.

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

const api = new Hono()

const createUserSchema = z.object({
  name: z.string().min(2),
  email: z.string().email(),
})

api.post(
  '/users',
  zValidator('json', createUserSchema),
  async (c) => {
    const { name, email } = c.req.valid('json')
    // name and email are fully typed here
    return c.json({ id: crypto.randomUUID(), name, email }, 201)
  }
)

export default api

That c.req.valid('json') call is the key detail. After the validator middleware runs, the parsed body is typed exactly as your Zod schema describes. No casting, no as assertions. That's genuinely ergonomic — not just marketing copy.

Middleware stacks with app.use() run in order, and you can scope them to route prefixes. Authentication middleware on /api/* but not on /health? That's one app.use('/api/*', authMiddleware) line. Quick aside: Hono ships built-in middleware for CORS, bearer auth, basic auth, rate limiting, and logger — check the official middleware package before you write your own.

One more thing — chaining multiple validators is completely legal. You can validate JSON body, query params, and URL params separately in the same handler, each with their own Zod schema. Your IDE will track all of it.

Deploying to Cloudflare Workers

Cloudflare Workers is where Hono has the deepest ecosystem integration. The Hono team maintains a dedicated adapter, the wrangler CLI scaffolding works out of the box, and the cold-start story is basically nonexistent — Workers spin up in under 5ms because they use isolates, not containers.

bun create hono my-worker --template cloudflare-workers
cd my-worker
bun install
bunx wrangler dev

The generated wrangler.toml sets compatibility_date to a recent date and maps your src/index.ts as the entry point. Bindings — KV namespaces, Durable Objects, R2 buckets — show up on c.env with full type safety if you configure Env as a Hono type parameter:

type Env = {
  Bindings: {
    MY_KV: KVNamespace
    MY_BUCKET: R2Bucket
    API_SECRET: string
  }
}

const app = new Hono<Env>()

app.get('/data/:key', async (c) => {
  const value = await c.env.MY_KV.get(c.req.param('key'))
  if (!value) return c.notFound()
  return c.json({ value })
})

Deploying to production is bunx wrangler deploy. That's it. Your API is live on Cloudflare's global network in seconds, running within 50ms of almost every user on the planet. Look, Node.js on a single-region VPS can't compete with that latency profile for most use cases.

Hono on Deno Deploy

Deno Deploy is Cloudflare Workers' closest competitor in the edge-functions space, and Hono supports it with zero adapter changes — the same Hono app code runs on both. That runtime portability is the framework's killer feature for teams that don't want to be locked in.

// main.ts — works on Deno Deploy unchanged
import { Hono } from 'npm:hono'

const app = new Hono()

app.get('/ping', (c) => c.json({ pong: true, ts: Date.now() }))

Deno.serve(app.fetch)

Deno's built-in Deno.serve API handles the HTTP lifecycle, and app.fetch is the standard fetch-compatible handler that Hono exposes. No wrapper, no adapter file — just that one line at the bottom. Worth noting: Deno 2.0 (released late 2024) made Node compatibility dramatically better, so if you have npm packages that previously broke on Deno, try again — most of them work fine now.

The deno.json config is minimal. You'll add a tasks section for dev and start commands, maybe an importMap if you're pinning Hono to a specific version. Deno's permission model means your API explicitly declares network access with --allow-net, which is actually a nice audit trail in production environments that care about supply-chain security.

RPC Mode: End-to-End Type Safety Across Client and Server

This is the feature that tips Hono from "nice API framework" to "genuinely transformative" for full-stack TypeScript apps. Hono's RPC mode lets you export the app type and import it on the client, so your frontend gets fully inferred types for every route — params, request body, response shape, all of it.

// server/routes/posts.ts
import { Hono } from 'hono'
import { zValidator } from '@hono/zod-validator'
import { z } from 'zod'

const posts = new Hono()
  .get('/', (c) => c.json({ posts: [] }))
  .post(
    '/',
    zValidator('json', z.object({ title: z.string(), body: z.string() })),
    async (c) => {
      const data = c.req.valid('json')
      return c.json({ id: '1', ...data }, 201)
    }
  )

export type PostsRoute = typeof posts
export default posts
// client/api.ts
import { hc } from 'hono/client'
import type { PostsRoute } from '../server/routes/posts'

const client = hc<PostsRoute>('http://localhost:3000')

// Fully typed — TypeScript knows the shape of the response
const res = await client.posts.$post({
  json: { title: 'Hello', body: 'World' },
})
const data = await res.json()
// data.id, data.title, data.body are all typed

That hc client is not code-gen — it's just TypeScript inference. No build step, no schema file to sync. Change a route on the server, TypeScript immediately tells you which client calls are broken. This is the tRPC pattern without the tRPC lock-in, and it works across the network boundary.

In practice, teams using Hono RPC with a Next.js or SvelteKit frontend report dramatically fewer "API contract drift" bugs — the kind where the backend changed a field name and nobody noticed for two weeks. If you're building a frontend that lives in the same monorepo as your API, this alone is worth adopting Hono for. Pair it with something like the gradient generator or other tools from Empire UI to keep your UI layer equally clean.

Performance Tips and Production Gotchas

Edge runtimes have constraints you won't hit locally. The biggest one: no filesystem. If your Hono handler tries to fs.readFileSync anything, it'll throw at runtime on Workers or Deno Deploy. All assets need to come from KV, R2, or be bundled into the worker bundle itself.

CPU time limits are real too. Cloudflare Workers free tier gives you 10ms of CPU time per request (paid is 30ms). That's wall-clock time minus any I/O wait, so awaiting a KV read doesn't count — but a tight loop computing something will. Profile your handlers if you're doing non-trivial processing. A 256x256 image resize that takes 40ms CPU will get killed on the free tier.

// Good pattern: stream large responses instead of buffering
app.get('/large-data', async (c) => {
  const stream = new ReadableStream({
    async start(controller) {
      for (let i = 0; i < 1000; i++) {
        controller.enqueue(
          new TextEncoder().encode(JSON.stringify({ i }) + '\n')
        )
      }
      controller.close()
    },
  })
  return new Response(stream, {
    headers: { 'Content-Type': 'application/x-ndjson' },
  })
})

Error handling deserves its own attention. Hono's app.onError() hook catches unhandled exceptions and lets you return structured error responses instead of leaking stack traces. Set it up before your first route registration, not as an afterthought. Also add app.notFound() for clean 404 responses — the default is a plain text "404 Not Found" which looks sloppy in an otherwise typed, structured API.

That said, don't over-engineer for scale you don't have yet. Start with a single src/index.ts, add route files as the surface area grows, and reach for Durable Objects or databases only when you actually need state. The simplicity is a feature — browse components at Empire UI follows the same philosophy: start minimal, layer complexity only when the use case demands it.

FAQ

Is Hono a replacement for Express?

For edge runtimes, yes — Express depends on Node.js APIs that don't exist on Workers or Deno Deploy. For traditional Node servers, Hono works fine but you'd also consider Fastify. The real win is writing one codebase that deploys anywhere.

Can Hono connect to a PostgreSQL database?

Not directly from a Cloudflare Worker — TCP sockets weren't supported until Cloudflare's experimental Socket API landed in 2024. Use Neon, PlanetScale, or Turso (libSQL over HTTP) for database access from edge functions. On Bun and Deno, standard pg or postgres.js drivers work fine.

How does Hono RPC compare to tRPC?

Both give you end-to-end type safety without code generation. tRPC has a larger ecosystem and better React Query integration. Hono RPC is runtime-agnostic and doesn't require a separate adapter layer — if you're already using Hono as your API framework, RPC mode adds zero extra dependencies.

What's the bundle size impact on a Cloudflare Worker?

Hono core is under 14kB minified. Workers have a 1MB (free) or 5MB (paid) bundle limit, so Hono itself is not your problem. Your third-party dependencies and bundled assets are. Use wrangler build --dry-run to inspect bundle size before deploying.

Free components in 40 styles
React & Tailwind, copy-paste ready.
Browse →

Read next

Bun Workspaces Guide: Monorepo With Bun Install and ScriptsVercel Edge Functions Guide: Runtime, Limits and Real-World UsesHono vs Express in 2026: Edge-Ready vs Battle-TestedtRPC + Next.js Guide: End-to-End Type Safety Without GraphQL