Why Hono Needs Multi-Runtime AI Rules
Hono is the ultrafast web framework that runs on every JavaScript runtime: Cloudflare Workers, Deno, Bun, Node.js, AWS Lambda, and Vercel Edge. Its superpower is portability — write once, deploy anywhere. AI assistants break this by generating Node.js-specific code: require() imports, process.env, Node streams, and fs module — none of which exist on Cloudflare Workers or Deno.
The most common AI failures: using process.env instead of Hono's env binding (c.env), importing Node.js built-ins (fs, path, crypto) that don't exist on edge runtimes, generating Express-style middleware instead of Hono's middleware pattern, and missing Hono's built-in helpers (c.json(), c.text(), c.html()) in favor of manual Response construction.
Hono is small (14KB), fast (fastest router benchmarks), and designed for the edge. Rules must enforce Web Standard APIs that work everywhere — not Node.js APIs that limit deployment options.
Rule 1: Web Standard APIs Only
The rule: 'Use only Web Standard APIs: Request, Response, Headers, URL, URLSearchParams, fetch, crypto.subtle, TextEncoder, TextDecoder, ReadableStream. Never import Node.js built-ins (fs, path, os, crypto, http). Never use process.env — use c.env for environment bindings. Never use require() — use ESM imports. If you need Node-specific APIs, isolate them behind a runtime check or adapter.'
For environment variables: 'Access env through Hono's context: app.get("/", (c) => { const apiKey = c.env.API_KEY; }). On Cloudflare Workers, c.env contains Worker bindings (KV, D1, R2). On Node.js with @hono/node-server, c.env contains process.env. The abstraction is the same — the runtime fills it in.'
For crypto: 'Use Web Crypto API (crypto.subtle) instead of Node's crypto module. Use crypto.subtle.digest for hashing, crypto.subtle.sign/verify for signatures, crypto.getRandomValues for random bytes. These are available on every runtime Hono supports.'
- Web Standard only: Request, Response, Headers, URL, fetch, crypto.subtle
- Never Node built-ins: no fs, no path, no process.env, no require()
- c.env for environment — works on Workers, Deno, Bun, Node
- crypto.subtle for crypto — not Node's crypto module
- ESM imports only — never CommonJS require()
fs, path, process.env, crypto (Node module) — none exist on Cloudflare Workers or Deno. One Node import makes your Hono app undeployable on edge. Web Standard APIs only: fetch, Response, crypto.subtle.
Rule 2: Zod OpenAPI for Validation and Docs
The rule: 'Use @hono/zod-openapi for route validation and automatic OpenAPI documentation. Define routes with createRoute: const route = createRoute({ method: "get", path: "/users/:id", request: { params: z.object({ id: z.string() }) }, responses: { 200: { content: { "application/json": { schema: UserSchema } } } } }). This gives you: typed request parsing, automatic validation, and OpenAPI spec generation from one definition.'
For schemas: 'Define Zod schemas for all request inputs and response outputs. Use z.openapi() extension for adding OpenAPI metadata (description, examples). Reuse schemas across routes. The OpenAPI spec is auto-generated at /doc — no manual Swagger maintenance.'
AI generates routes without validation — c.req.json() returns unknown with no type safety. Zod OpenAPI adds validation + types + docs in one pattern. It's Hono's equivalent of Fastify's JSON Schema but with Zod's developer experience.
Zod OpenAPI gives you validation (auto 400 on bad input), TypeScript types (inferred from schema), and OpenAPI docs (auto-generated at /doc). Define the schema once — get all three for free.
Rule 3: Middleware Patterns
The rule: 'Use Hono's middleware pattern: app.use("*", logger()), app.use("/api/*", auth()). Middleware receives context and calls next(): async (c, next) => { ... await next(); ... }. Code before next() runs on request; code after next() runs on response. Use built-in middleware: cors(), logger(), secureHeaders(), bearerAuth(), prettyJSON().'
For custom middleware: 'Create middleware with createMiddleware: const auth = createMiddleware(async (c, next) => { const token = c.req.header("Authorization"); if (!token) return c.json({ error: "Unauthorized" }, 401); c.set("user", await verifyToken(token)); await next(); }). Use c.set() / c.get() for passing data between middleware and handlers.'
For middleware ordering: 'Register middleware before routes — order matters. Security middleware (cors, secureHeaders) first, then auth, then route-specific middleware. Use path patterns to scope middleware: app.use("/admin/*", adminAuth()) only applies to admin routes.'
- app.use for middleware — path patterns for scoping (/api/*, /admin/*)
- Before next() = request phase — after next() = response phase
- Built-in: cors(), logger(), secureHeaders(), bearerAuth()
- c.set()/c.get() for passing data between middleware and handlers
- Register middleware before routes — order matters
Rule 4: Context Helpers and Response Patterns
The rule: 'Use Hono's context helpers for responses: c.json({ data }) for JSON, c.text("hello") for text, c.html("<h1>Hello</h1>") for HTML, c.redirect("/login") for redirects, c.notFound() for 404. Set status with the second argument: c.json({ error }, 400). Never construct Response objects manually unless Hono's helpers don't cover your case.'
For request parsing: 'Use c.req.json() for JSON body, c.req.text() for text body, c.req.formData() for form data, c.req.param("id") for path params, c.req.query("page") for query params, c.req.header("Authorization") for headers. All methods are typed when using Zod OpenAPI routes.'
For streaming: 'Use c.stream() or c.streamText() for streaming responses. Return a ReadableStream for large data. Use Server-Sent Events with Hono's streaming helpers for real-time updates. Streaming works on all runtimes — it's Web Standard.'
Rule 5: Runtime-Specific Adapters
The rule: 'Use runtime-specific entry points: @hono/node-server for Node.js, export default app for Cloudflare Workers, Deno.serve(app.fetch) for Deno. Keep the Hono app code runtime-agnostic — the entry point is the only file that differs per runtime. Use wrangler for Cloudflare development, deno run for Deno, bun run for Bun.'
For Cloudflare bindings: 'Type Cloudflare bindings in the app type: type Bindings = { DB: D1Database; KV: KVNamespace; BUCKET: R2Bucket }; const app = new Hono<{ Bindings: Bindings }>(); Access with c.env.DB, c.env.KV. For D1 database access, use c.env.DB.prepare(query).bind(params).all().'
For testing: 'Use app.request() for testing without a server: const res = await app.request("/users/1"); const json = await res.json(); This works in any test runner (Vitest, Jest, Deno test) without starting a server. It's Hono's equivalent of Fastify's inject() — fast, isolated, no port binding.'
app.request('/users/1') tests your Hono app without binding a port — fast, isolated, works in any test runner. It's the best testing DX in the Node.js ecosystem.
Complete Hono Rules Template
Consolidated rules for Hono projects targeting multi-runtime deployment.
- Web Standard APIs only: Request, Response, fetch, crypto.subtle — never Node built-ins
- c.env for environment — works on Workers, Deno, Bun, Node — never process.env
- @hono/zod-openapi for validation + types + docs from one schema definition
- Middleware: app.use with path patterns — before next() for request, after for response
- Context helpers: c.json(), c.text(), c.redirect() — never manual Response construction
- Runtime-agnostic app code — runtime-specific entry point only
- Type Cloudflare bindings: Hono<{ Bindings: { DB: D1Database } }>
- app.request() for testing — no server needed — works in any test runner