Claude Code for Hono: Ultrafast Web Framework for Edge and Node.js — Claude Skills 360 Blog
Blog / Backend / Claude Code for Hono: Ultrafast Web Framework for Edge and Node.js
Backend

Claude Code for Hono: Ultrafast Web Framework for Edge and Node.js

Published: February 16, 2027
Read time: 8 min read
By: Claude Skills 360

Hono is a lightweight web framework that runs on Cloudflare Workers, Deno Deploy, Vercel Edge, Bun, and Node.js — the same app.get(path, handler) API works everywhere. Middleware chains with app.use() for CORS, auth, logging, and rate limiting. @hono/zod-openapi generates OpenAPI specs from typed route definitions. Hono’s RPC mode exports a type-safe client — client.orders.$get({ query: { customerId } }) types the request and response from the server router. SSR with JSX renders HTML on the edge without a build step. hono/jsx/streaming streams responses incrementally. @hono/swagger-ui auto-generates API documentation. Claude Code generates Hono application routers, middleware, OpenAPI-typed routes, RPC client configurations, and the deployment configurations for production edge applications.

CLAUDE.md for Hono

## Hono Stack
- Version: hono >= 4.6, @hono/zod-openapi >= 0.16
- Routing: app.get/post/put/delete(path, ...handlers) — same API all runtimes
- Middleware: app.use("*", middleware) — runs before handlers
- Validation: @hono/zod-openapi for typed routes with OpenAPI output
- RPC: hc<AppType>(url) — typed client from router type
- JSX: import { jsx } from 'hono/jsx' or use jsx preset
- Context: c.req.json/param/query, c.json/text/html/notFound
- Deploy: wrangler for CF Workers, deno deploy for Deno, vercel for Edge

Core Application

// src/app.ts — Hono application with middleware
import { Hono } from "hono"
import { cors } from "hono/cors"
import { logger } from "hono/logger"
import { bearerAuth } from "hono/bearer-auth"
import { rateLimiter } from "hono-rate-limiter"
import { ordersRouter } from "./routes/orders"
import { productsRouter } from "./routes/products"

type Bindings = {
  DB: D1Database          // Cloudflare D1
  CACHE: KVNamespace      // Cloudflare KV
  API_SECRET: string
}

type Variables = {
  userId: string          // Set by auth middleware
}

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

// Global middleware
app.use("*", logger())

app.use("*", cors({
  origin: ["https://app.example.com"],
  allowMethods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"],
  allowHeaders: ["Authorization", "Content-Type"],
  credentials: true,
}))

app.use("*", async (c, next) => {
  const start = Date.now()
  await next()
  c.res.headers.set("X-Response-Time", `${Date.now() - start}ms`)
})

// Auth middleware for /api routes
app.use("/api/*", async (c, next) => {
  const auth = c.req.header("Authorization")
  if (!auth?.startsWith("Bearer ")) {
    return c.json({ error: "Unauthorized" }, 401)
  }

  const token = auth.slice(7)

  // Verify JWT using Cloudflare Workers crypto
  const userId = await verifyToken(token, c.env.API_SECRET)
  if (!userId) return c.json({ error: "Invalid token" }, 401)

  c.set("userId", userId)
  await next()
})

// Mount sub-routers
app.route("/api/orders", ordersRouter)
app.route("/api/products", productsRouter)

// Health check
app.get("/health", c => c.json({ status: "ok", ts: Date.now() }))

export default app

OpenAPI Route Definitions

// src/routes/orders-openapi.ts — typed routes with OpenAPI
import { OpenAPIHono, createRoute, z } from "@hono/zod-openapi"
import { swaggerUI } from "@hono/swagger-ui"

const OrderSchema = z.object({
  id: z.string().openapi({ example: "ord-abc123" }),
  status: z.enum(["pending", "processing", "shipped", "delivered", "cancelled"]),
  totalCents: z.number().int().openapi({ example: 2999 }),
  createdAt: z.string().datetime(),
}).openapi("Order")

const CreateOrderSchema = z.object({
  items: z.array(z.object({
    productId: z.string(),
    quantity: z.number().int().positive(),
    priceCents: z.number().int().positive(),
  })).min(1),
}).openapi("CreateOrder")

const listOrdersRoute = createRoute({
  method: "get",
  path: "/",
  request: {
    query: z.object({
      customerId: z.string().optional(),
      status: z.string().optional(),
      limit: z.coerce.number().int().max(100).default(20).optional(),
    }),
  },
  responses: {
    200: {
      content: {
        "application/json": {
          schema: z.object({
            orders: z.array(OrderSchema),
            total: z.number(),
          }),
        },
      },
      description: "List of orders",
    },
  },
})

const createOrderRoute = createRoute({
  method: "post",
  path: "/",
  request: {
    body: {
      content: {
        "application/json": { schema: CreateOrderSchema },
      },
    },
  },
  responses: {
    201: {
      content: {
        "application/json": { schema: OrderSchema },
      },
      description: "Created order",
    },
    422: {
      content: {
        "application/json": { schema: z.object({ error: z.string() }) },
      },
      description: "Validation error",
    },
  },
})

export const ordersOpenAPIRouter = new OpenAPIHono()

ordersOpenAPIRouter.openapi(listOrdersRoute, async (c) => {
  const { customerId, status, limit } = c.req.valid("query")
  const orders = await db.listOrders({ customerId, status, limit })
  return c.json({ orders, total: orders.length })
})

ordersOpenAPIRouter.openapi(createOrderRoute, async (c) => {
  const body = c.req.valid("json")
  const order = await db.createOrder(body)
  return c.json(order, 201)
})

// Swagger UI
ordersOpenAPIRouter.get("/ui", swaggerUI({ url: "/api/orders/spec" }))

// OpenAPI spec endpoint
ordersOpenAPIRouter.doc("/spec", {
  openapi: "3.0.0",
  info: { title: "Orders API", version: "1.0.0" },
})

RPC Client

// src/client.ts — type-safe Hono RPC client
import { hc } from "hono/client"
import type { AppType } from "./app"

// AppType is the typeof app — includes all routes and their types
export const client = hc<AppType>("https://api.example.com", {
  headers: {
    Authorization: `Bearer ${getAuthToken()}`,
  },
})

// Usage — fully typed, no manual type assertions needed
async function fetchOrders(customerId: string) {
  const response = await client.api.orders.$get({
    query: { customerId, limit: "20" },
  })

  if (!response.ok) throw new Error(`HTTP ${response.status}`)
  // Return type inferred from server handler
  const { orders } = await response.json()
  return orders
}

async function createOrder(items: OrderItem[]) {
  const response = await client.api.orders.$post({
    json: { items },
  })
  return response.json()
}

JSX Server Rendering

// src/routes/pages.tsx — Hono JSX for server-rendered HTML
import { Hono } from "hono"
import { html } from "hono/html"

const pages = new Hono()

// Hono JSX component
function OrderCard({ order }: { order: Order }) {
  return (
    <div class="order-card">
      <h3>Order #{order.id.slice(-8)}</h3>
      <span class={`status status--${order.status}`}>{order.status}</span>
      <p>${(order.totalCents / 100).toFixed(2)}</p>
    </div>
  )
}

pages.get("/dashboard", async (c) => {
  const userId = c.var.userId
  const orders = await fetchOrders(userId)

  return c.html(
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <title>Dashboard</title>
        <link rel="stylesheet" href="/styles.css" />
      </head>
      <body>
        <h1>Your Orders</h1>
        <div class="orders-grid">
          {orders.map(order => <OrderCard key={order.id} order={order} />)}
        </div>
      </body>
    </html>
  )
})

// Streaming SSR
pages.get("/dashboard/stream", async (c) => {
  return c.streamText(async (stream) => {
    await stream.writeln(
      "<!DOCTYPE html><html><body><h1>Orders</h1>"
    )
    const orders = await fetchOrders(c.var.userId)
    for (const order of orders) {
      await stream.writeln(
        `<div>${order.id} — ${order.status}</div>`
      )
    }
    await stream.writeln("</body></html>")
  })
})

export { pages }

Cloudflare Workers Deployment

# wrangler.toml
name = "orders-api"
main = "src/worker.ts"
compatibility_date = "2024-09-23"

[[d1_databases]]
binding = "DB"
database_name = "orders"
database_id = "xxxx-xxxx-xxxx"

[[kv_namespaces]]
binding = "CACHE"
id = "xxxx"

[vars]
ENVIRONMENT = "production"
// src/worker.ts — Cloudflare Worker entry point
import { app } from "./app"

export default app

For the Express.js alternative that’s better for traditional Node.js deployments where Cloudflare Workers compatibility isn’t needed and the large Express middleware ecosystem (passport.js, multer) is required, the Node.js API guide covers Express patterns. For the Fastify framework that provides better TypeScript support and performance than Express while remaining Node.js-native, see the Fastify guide for schema validation and plugin architecture. The Claude Skills 360 bundle includes Hono skill sets covering routing, middleware, OpenAPI, and edge deployment. Start with the free tier to try Hono application generation.

Keep Reading

Backend

Claude Code for Bun: Fast JavaScript Runtime and Toolkit

Build with Bun and Claude Code — Bun.serve for HTTP servers, Bun.file for fast file I/O, Bun.$ for shell commands, Bun.sql for SQLite and PostgreSQL, Bun.build for bundling, bun:test for testing, Bun.hash for hashing, bun.lock for deterministic installs, bun run for package.json scripts, hot reloading with --hot, bun init for project scaffolding, and compatibility with Node.js modules.

6 min read Jun 13, 2027
Backend

Claude Code for Express.js Advanced: Patterns for Production APIs

Advanced Express.js patterns with Claude Code — typed request handlers with RequestHandler generics, async error handling middleware, Zod validation middleware factory, rate limiting with express-rate-limit and Redis store, helmet security middleware, compression, dependency injection with tsyringe, file upload with multer and S3, pagination utilities, JWT middleware, and structured logging with pino.

6 min read Jun 8, 2027
Backend

Claude Code for KeystoneJS: Node.js CMS and App Framework

Build full-stack apps with KeystoneJS and Claude Code — config with lists, fields.text and fields.relationship for schema definition, access control with isAuthenticated and isAdmin functions, hooks with beforeOperation and afterOperation, GraphQL API auto-generation from schema, AdminUI for content management, session with statelessSessions, Prisma adapter for database, file storage with images and files fields, and custom REST endpoints.

6 min read Jun 7, 2027

Put these ideas into practice

Claude Skills 360 gives you production-ready skills for everything in this article — and 2,350+ more. Start free or go all-in.

Back to Blog

Get 360 skills free