Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Build ultra-fast web APIs and full-stack apps with Hono — runs on Cloudflare Workers, Deno, Bun, Node.js, and any WinterCG-compatible runtime.
| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-08 | ✓→✗ | ▼ Worse | 108% | 0% |
| case-01 | ✓→✓ | = Same ✓ | 259% | 0% |
| case-02 | ✓→✓ | = Same ✓ | 243% | 0% |
| case-03 | ✓→✓ | = Same ✓ | 217% | 0% |
| case-04 | ✓→✓ | = Same ✓ | 136% | 0% |
Hono (炎, "flame" in Japanese) is a small, ultrafast web framework built on Web Standards (Request/Response/fetch). It runs anywhere: Cloudflare Workers, Deno Deploy, Bun, Node.js, AWS Lambda, and any WinterCG-compatible runtime — with the same code. Hono's router is one of the fastest available, and its middleware system, built-in JSX support, and RPC client make it a strong choice for edge APIs, BFFs, and lightweight full-stack apps.
c.req, c.json, or hc() RPC clientCloudflare Workers (recommended for edge):
bashnpm create hono@latest my-api # Select: cloudflare-workers cd my-api npm install npm run dev # Wrangler local dev npm run deploy # Deploy to Cloudflare
Bun / Node.js:
bashmkdir my-api && cd my-api bun init bun add hono
typescript// src/index.ts (Bun) import { Hono } from 'hono'; const app = new Hono(); app.get('/', c => c.text('Hello Hono!')); export default { port: 3000, fetch: app.fetch, };
typescriptimport { Hono } from 'hono'; const app = new Hono(); // Basic methods app.get('/posts', c => c.json({ posts: [] })); app.post('/posts', c => c.json({ created: true }, 201)); app.put('/posts/:id', c => c.json({ updated: true })); app.delete('/posts/:id', c => c.json({ deleted: true })); // Route params and query strings app.get('/posts/:id', async c => { const id = c.req.param('id'); const format = c.req.query('format') ?? 'json'; return c.json({ id, format }); }); // Wildcard app.get('/static/*', c => c.text('static file')); export default app;
Chained routing:
typescriptapp .get('/users', listUsers) .post('/users', createUser) .get('/users/:id', getUser) .patch('/users/:id', updateUser) .delete('/users/:id', deleteUser);
Hono middleware works exactly like fetch interceptors — before and after handlers:
typescriptimport { Hono } from 'hono'; import { logger } from 'hono/logger'; import { cors } from 'hono/cors'; import { bearerAuth } from 'hono/bearer-auth'; const app = new Hono(); // Built-in middleware app.use('*', logger()); app.use('/api/*', cors({ origin: 'https://myapp.com' })); app.use('/api/admin/*', bearerAuth({ token: process.env.API_TOKEN! })); // Custom middleware app.use('*', async (c, next) => { c.set('requestId', crypto.randomUUID()); await next(); c.header('X-Request-Id', c.get('requestId')); });
Available built-in middleware: logger, cors, csrf, etag, cache, basicAuth, bearerAuth, jwt, compress, bodyLimit, timeout, prettyJSON, secureHeaders.
typescriptapp.post('/submit', async c => { // Parse body const body = await c.req.json<{ name: string; email: string }>(); const form = await c.req.formData(); const text = await c.req.text(); // Headers and cookies const auth = c.req.header('authorization'); const token = getCookie(c, 'session'); // Responses return c.json({ ok: true }); // JSON return c.text('hello'); // plain text return c.html('<h1>Hello</h1>'); // HTML return c.redirect('/dashboard', 302); // redirect return new Response(stream, { status: 200 }); // raw Response });
typescriptimport { zValidator } from '@hono/zod-validator'; import { z } from 'zod'; const createPostSchema = z.object({ title: z.string().min(1).max(200), body: z.string().min(1), tags: z.array(z.string()).default([]), }); app.post( '/posts', zValidator('json', createPostSchema), async c => { const data = c.req.valid('json'); // fully typed const post = await db.post.create({ data }); return c.json(post, 201); } );
typescript// src/routes/posts.ts import { Hono } from 'hono'; const posts = new Hono(); posts.get('/', async c => { /* list posts */ }); posts.post('/', async c => { /* create post */ }); posts.get('/:id', async c => { /* get post */ }); export default posts;
typescript// src/index.ts import { Hono } from 'hono'; import posts from './routes/posts'; import users from './routes/users'; const app = new Hono().basePath('/api'); app.route('/posts', posts); app.route('/users', users); export default app;
Hono's RPC mode exports route types that the hc client consumes — similar to tRPC but using fetch conventions:
typescript// server: src/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: [{ id: '1', title: 'Hello' }] })) .post( '/', zValidator('json', z.object({ title: z.string() })), async c => { const { title } = c.req.valid('json'); return c.json({ id: '2', title }, 201); } ); export default posts; export type PostsType = typeof posts;
typescript// client: src/client.ts import { hc } from 'hono/client'; import type { PostsType } from '../server/routes/posts'; const client = hc<PostsType>('/api/posts'); // Fully typed — autocomplete on routes, params, and responses const { posts } = await client.$get().json(); const newPost = await client.$post({ json: { title: 'New Post' } }).json();
typescriptimport { Hono } from 'hono'; import { jwt, sign } from 'hono/jwt'; const app = new Hono(); const SECRET = process.env.JWT_SECRET!; app.post('/login', async c => { const { email, password } = await c.req.json(); const user = await validateUser(email, password); if (!user) return c.json({ error: 'Invalid credentials' }, 401); const token = await sign({ sub: user.id, exp: Math.floor(Date.now() / 1000) + 3600 }, SECRET); return c.json({ token }); }); app.use('/api/*', jwt({ secret: SECRET })); app.get('/api/me', async c => { const payload = c.get('jwtPayload'); const user = await getUserById(payload.sub); return c.json(user); }); export default app;
typescript// src/index.ts import { Hono } from 'hono'; type Bindings = { DB: D1Database; API_TOKEN: string; }; const app = new Hono<{ Bindings: Bindings }>(); app.get('/users', async c => { const { results } = await c.env.DB.prepare('SELECT * FROM users LIMIT 50').all(); return c.json(results); }); app.post('/users', async c => { const { name, email } = await c.req.json(); await c.env.DB.prepare('INSERT INTO users (name, email) VALUES (?, ?)') .bind(name, email) .run(); return c.json({ created: true }, 201); }); export default app;
typescriptimport { stream, streamText } from 'hono/streaming'; app.get('/stream', c => streamText(c, async stream => { for (const chunk of ['Hello', ' ', 'World']) { await stream.write(chunk); await stream.sleep(100); } }) );
app.route('/users', usersRouter)zValidator for all request body, query, and param validationBindings generic: new Hono<{ Bindings: Env }>()hc) when your frontend and backend share the same repoc.json()/c.text() over new Response() for cleaner codefs, path, process) if you want edge portabilityVariables, Bindings) to keep c.get() type-safezValidator before using data from requests.csrf middleware on mutation endpoints when serving HTML/forms.wrangler.toml [vars] (non-secret) or wrangler secret put (secret) — never hardcode them in source.bearerAuth or jwt, ensure tokens are validated server-side — do not trust client-provided user IDs.undefined — response is emptySolution: Always return a response from handlers: return c.json(...) not just c.json(...).
Solution: Call await next() before post-response logic; Hono runs code after next() as the response travels back up the chain.
c.env is undefined on Node.jsSolution: Cloudflare env bindings only exist in Workers. Use process.env on Node.js.
Solution: Check that app.route('/prefix', subRouter) uses the same prefix your client calls. Sub-routers should not repeat the prefix in their own routes.
@cloudflare-workers-expert — Deep dive into Cloudflare Workers platform specifics@trpc-fullstack — Alternative RPC approach for TypeScript full-stack apps@zod-validation-expert — Detailed Zod schema patterns used with @hono/zod-validator@nodejs-backend-patterns — When you need a Node.js-specific backend (not edge)Other measured skills in the registry, with their headline benchmark lift.