--- id: backend-hono-middleware-deep title: Hono Middleware β€” modern Express alternative category: Coding status: draft source_trust_level: B verification_status: conceptual created_at: 2026-05-09 updated_at: 2026-05-09 tags: [backend, hono, vibe-coding] tech_stack: { language: "TS", applicable_to: ["Backend"] } applied_in: [] aliases: [Hono, middleware, Cloudflare Workers, Deno, Bun, edge-friendly] --- # Hono Middleware Deep > Modern Express alternative. **Edge-friendly (CF Workers / Deno / Bun / Node), TypeScript-first, μž‘μ€ bundle**. ## πŸ“– 핡심 κ°œλ… - Web standards (Fetch, Request, Response). - λ§€ runtime (Node / Bun / Deno / CF / Lambda). - Type-safe routing. - Built-in middleware. ## πŸ’» μ½”λ“œ νŒ¨ν„΄ ### Basic ```ts import { Hono } from 'hono'; const app = new Hono(); app.get('/', (c) => c.text('Hello')); app.get('/users/:id', (c) => c.json({ id: c.req.param('id') })); // Cloudflare Worker export default app; // Bun Bun.serve({ fetch: app.fetch }); // Node import { serve } from '@hono/node-server'; serve(app); ``` β†’ 같은 code, λ‹€λ₯Έ runtime. ### Built-in middleware ```ts import { logger } from 'hono/logger'; import { cors } from 'hono/cors'; import { secureHeaders } from 'hono/secure-headers'; import { compress } from 'hono/compress'; import { etag } from 'hono/etag'; import { jwt } from 'hono/jwt'; app.use(logger()); app.use(cors({ origin: 'https://app.example.com' })); app.use(secureHeaders()); app.use(compress()); app.use(etag()); app.use('/api/*', jwt({ secret: 'secret' })); ``` β†’ λͺ¨λ“  production middleware. ### Custom middleware ```ts const auth = async (c, next) => { const token = c.req.header('Authorization')?.replace('Bearer ', ''); if (!token) return c.text('Unauthorized', 401); const user = await verifyToken(token); if (!user) return c.text('Invalid', 401); c.set('user', user); await next(); }; app.use('/api/*', auth); app.get('/api/me', (c) => { const user = c.get('user'); return c.json(user); }); ``` ### Type-safe context ```ts type Variables = { user: User; }; const app = new Hono<{ Variables: Variables }>(); app.use('*', async (c, next) => { c.set('user', { id: '1' }); await next(); }); app.get('/', (c) => { const user = c.get('user'); // type: User return c.json(user); }); ``` ### Validator ```ts import { zValidator } from '@hono/zod-validator'; import { z } from 'zod'; app.post( '/users', zValidator('json', z.object({ name: z.string(), email: z.string().email() })), (c) => { const data = c.req.valid('json'); // type-safe return c.json({ id: '...', ...data }); } ); ``` ### RPC mode (type-safe client) ```ts // Server const route = app.post('/users', zValidator('json', userSchema), (c) => { return c.json({ id: '...' }); }); export type AppType = typeof route; // Client import { hc } from 'hono/client'; import type { AppType } from './server'; const client = hc('https://api.example.com'); const r = await client.users.$post({ json: { name: 'Alice', email: 'a@x' } }); const data = await r.json(); // type-safe ``` β†’ tRPC 식 type-safe RPC. ### Streaming ```ts import { stream, streamSSE } from 'hono/streaming'; app.get('/stream', (c) => { return stream(c, async (stream) => { for (let i = 0; i < 100; i++) { await stream.writeln(`chunk ${i}`); await stream.sleep(100); } }); }); app.get('/sse', (c) => { return streamSSE(c, async (stream) => { let id = 0; while (true) { await stream.writeSSE({ event: 'msg', data: 'hello', id: String(id++) }); await stream.sleep(1000); } }); }); ``` ### WebSocket (Bun / Deno) ```ts import { upgradeWebSocket } from 'hono/cloudflare-workers'; app.get( '/ws', upgradeWebSocket((c) => ({ onMessage: (event, ws) => ws.send(`echo: ${event.data}`), onClose: () => console.log('closed'), })) ); ``` ### Sub-app (modular) ```ts const userApp = new Hono(); userApp.get('/', (c) => c.json([])); userApp.get('/:id', (c) => c.json({ id: c.req.param('id') })); const orderApp = new Hono(); orderApp.get('/', (c) => c.json([])); const app = new Hono(); app.route('/users', userApp); app.route('/orders', orderApp); ``` β†’ Module 별 뢄리. ### Error handling ```ts app.onError((err, c) => { console.error(err); return c.json({ error: err.message }, 500); }); app.notFound((c) => c.text('Not found', 404)); ``` ### File serving ```ts import { serveStatic } from '@hono/node-server/serve-static'; app.use('/static/*', serveStatic({ root: './public' })); ``` ### OpenAPI integration ```ts import { OpenAPIHono, createRoute, z } from '@hono/zod-openapi'; const app = new OpenAPIHono(); const route = createRoute({ method: 'get', path: '/users/:id', request: { params: z.object({ id: z.string() }) }, responses: { 200: { content: { 'application/json': { schema: z.object({ id: z.string() }) } } }, }, }); app.openapi(route, (c) => c.json({ id: c.req.valid('param').id })); app.doc('/openapi.json', { openapi: '3.0.0', info: { title: 'API', version: '1.0' } }); ``` β†’ Zod schema β†’ OpenAPI auto. ### Performance ``` Hono: ~30k req/sec on Bun. Express: ~10k req/sec on Node. Fastify: ~25k. β†’ Hono κ°€ κ°€μž₯ 빠름 + μž‘μ€. ``` ### Bundle size ``` Hono: ~12 KB. Express: ~80 KB. Fastify: ~90 KB. β†’ Edge / serverless μΉœν™”. ``` ### Use case ``` - Cloudflare Worker. - Vercel Edge. - Bun server. - Deno server. - Node μ„œλ²„ (alternative to Express). - Lambda. ``` ### Production deploy ```bash # Cloudflare wrangler deploy # Vercel vercel --prod # Bun bun run server.ts # Node node server.js ``` ### vs Express ``` Express: - κ°€μž₯ mature. - 큰 ecosystem. - Node only. Hono: - Modern, edge-friendly. - Type-safe. - Multi-runtime. - μž‘μ€ ecosystem (μžλΌλŠ”). β†’ New project = Hono. Existing Express = stay. ``` ### vs Fastify ``` Fastify: - Node-first. - Plugin system. - 빠름. Hono: - Edge-first. - Web standards. - 더 μž‘μ€. β†’ Edge / serverless = Hono. Node-only enterprise = Fastify. ``` ### Middleware order ```ts app.use(logger()); // 1st app.use(cors()); app.use(jwt({ secret })); // auth app.use('/api/*', rateLimit); // rate app.get('/api/users', ...); // route ``` β†’ Order matters. ### Context (c) ```ts c.req.url // string c.req.method c.req.header('...') c.req.query('...') c.req.param('...') c.req.json() // Promise c.req.formData() c.req.text() c.json({}) // Response c.text('hi') c.html('

hi

') c.redirect('/path') c.status(404) c.set('key', val) c.get('key') c.env // Cloudflare Worker env ``` ### Auth patterns ```ts // JWT app.use('/api/*', jwt({ secret: c.env.JWT_SECRET })); // Custom const apiKey = c.req.header('X-API-Key'); if (apiKey !== c.env.API_KEY) return c.text('Unauthorized', 401); // Basic auth import { basicAuth } from 'hono/basic-auth'; app.use('/admin/*', basicAuth({ username: 'admin', password: 'secret' })); ``` ### Rate limit ```ts const rateLimiter = new Map(); app.use('/api/*', async (c, next) => { const ip = c.req.header('x-real-ip') ?? 'unknown'; const now = Date.now(); const limit = rateLimiter.get(ip) ?? { count: 0, reset: now + 60000 }; if (now > limit.reset) { limit.count = 0; limit.reset = now + 60000; } limit.count++; rateLimiter.set(ip, limit); if (limit.count > 100) return c.text('Rate limit', 429); await next(); }); ``` β†’ Cloudflare = built-in. Self-host = manual. ### Real-world - **Cloudflare**: Hono 의 큰 user. - **Vercel**: Edge functions. - **Several startups**: edge-first. ## πŸ€” μ˜μ‚¬κ²°μ • κΈ°μ€€ | 상황 | μΆ”μ²œ | |---|---| | Edge / Cloudflare | Hono | | Node μƒˆ project | Hono | | Existing Express | Stay or migrate gradual | | Type-safe RPC | Hono RPC mode | | OpenAPI | Hono + zod-openapi | | WebSocket | Hono (Bun/Deno) | ## ❌ μ•ˆν‹°νŒ¨ν„΄ - **λͺ¨λ“  κ±° 1 file**: sub-app μ‚¬μš©. - **No type-safe**: zValidator. - **No error handler**: ugly. - **Middleware order 잘λͺ»**: auth before logger. - **Big bundle**: edge limit. ## πŸ€– LLM ν™œμš© 힌트 - Hono = modern Express. - Multi-runtime (CF / Node / Bun / Deno). - Type-safe RPC + OpenAPI. - μž‘μ€ bundle (edge μΉœν™”). ## πŸ”— κ΄€λ ¨ λ¬Έμ„œ - [[Backend_Hono_Modern]] - [[Backend_Edge_Runtime_Deep]] - [[Backend_Edge_Functions]]