Skip to content

Hono — Overview

OWNER: urszula, maxim ALSO_USED_BY: sandro, boris, yoanna LAST_VERIFIED: 2026-03-26 GE_STACK_VERSION: 4.12.x


Overview

Hono is GE's primary backend API framework for all dedicated API services. It replaces Express/Fastify across the stack. Admin-UI uses Next.js API routes internally, but every standalone backend service MUST use Hono.

Agents building API endpoints, middleware, or backend services need this page.


Why Hono Over Express/Fastify

CHECK: You are building a new backend service or API layer. THEN: Use Hono. No exceptions.

Decision Rationale

Criterion Express Fastify Hono
Bundle size ~2MB installed ~1.5MB ~14KB, zero deps
TypeScript Bolted on, @types/* Better, still gaps Native, first-class
Web Standards No (req/res) No Yes (Request/Response)
Zod integration Manual Manual @hono/zod-validator built-in
RPC type safety None None hono/client end-to-end types
Multi-runtime Node only Node only Node, Bun, Deno, Edge
Middleware DX callback(req,res,next) hooks + decorators onion model, await next()

GE chose Hono because: - End-to-end TypeScript type safety with zero codegen - Zod validation at route level produces both runtime checks AND OpenAPI docs - RPC client shares types between services without manual interface files - Ultralight footprint reduces container image size and cold-start time in k3s - Web Standards API means code is portable if we ever move runtimes


Version Pinning

GE_STACK_VERSION: 4.12.x

CHECK: You are adding Hono to a new service. THEN: Pin to the exact minor version in package.json.

{
  "dependencies": {
    "hono": "~4.12.9",
    "@hono/node-server": "~1.13.0",
    "@hono/zod-validator": "~0.4.1",
    "@hono/zod-openapi": "~0.18.0"
  }
}

IF: A new Hono major/minor version is available. THEN: Joshua (Innovation) proposes the upgrade via Discussion. THEN: Update GE_STACK_VERSION in ALL hono wiki pages after approval.


GE Runtime Environment

Hono runs on Node.js inside k3s containers. NOT on Bun, Deno, or Edge runtimes.

import { serve } from '@hono/node-server'
import { app } from './app'

const port = Number(process.env.PORT) || 3000

serve({ fetch: app.fetch, port }, (info) => {
  console.log(`Listening on port ${info.port}`)
})

CHECK: Your entrypoint uses @hono/node-server serve function. IF: You are using Bun.serve() or Deno.serve(). THEN: Replace with @hono/node-server. GE runs Node.js only.


Package Structure

Every GE Hono service follows this layout:

service-name/
  src/
    app.ts              # Hono app factory, middleware stack
    index.ts            # Entrypoint, calls serve()
    routes/
      {resource}.ts     # One file per resource/domain
    middleware/
      auth.ts           # Authentication middleware
      error-handler.ts  # Global error handler
    lib/
      db.ts             # Drizzle client
      redis.ts          # Redis client
    types/
      env.ts            # Hono Env type definition
  drizzle/              # Migrations
  Dockerfile
  package.json
  tsconfig.json
  vitest.config.ts

CHECK: Your service has a src/types/env.ts that defines the Hono Env type. IF: Missing. THEN: Create it. All context variables and bindings MUST be typed.

// src/types/env.ts
export type AppEnv = {
  Variables: {
    userId: string
    agentId: string | null
    requestId: string
  }
  Bindings: {}
}

App Factory Pattern

GE services use a factory function that returns the configured Hono app. This enables testing without starting the server.

// src/app.ts
import { Hono } from 'hono'
import { AppEnv } from './types/env'
import { errorHandler } from './middleware/error-handler'
import { authMiddleware } from './middleware/auth'
import { requestIdMiddleware } from './middleware/request-id'
import { resourceRoutes } from './routes/resource'

export function createApp() {
  const app = new Hono<AppEnv>()

  // Global middleware — order matters
  app.use('*', requestIdMiddleware)
  app.use('*', errorHandler)
  app.use('/api/*', authMiddleware)

  // Routes
  app.route('/api/resources', resourceRoutes)

  return app
}

CHECK: Your app factory passes AppEnv generic to new Hono<AppEnv>(). IF: You wrote new Hono() without the generic. THEN: Add it. Without it, c.get() and c.set() lose type safety.


Cross-References

READ_ALSO: wiki/docs/stack/hono/patterns.md READ_ALSO: wiki/docs/stack/hono/middleware.md READ_ALSO: wiki/docs/stack/hono/testing.md READ_ALSO: wiki/docs/stack/hono/pitfalls.md READ_ALSO: wiki/docs/stack/hono/checklist.md