--- id: backend-hono-modern title: Hono / Elysia / Modern TS Frameworks category: Coding status: draft source_trust_level: B verification_status: conceptual created_at: 2026-05-09 updated_at: 2026-05-09 tags: [backend, hono, elysia, vibe-coding] tech_stack: { language: "TS / Bun / Node", applicable_to: ["Backend"] } applied_in: [] aliases: [Hono, Elysia, Bun.serve, Express alternative, Web Standard, modern TS framework] --- # Hono / Elysia > Express 의 modern 후속. **Web Standard (Request/Response) 기반 + edge runtime 호환 + type-safe**. Hono = universal, Elysia = Bun 친화. ## 📖 핵심 개념 - Web Standard: Request / Response (browser native). - Edge: Cloudflare Workers / Deno / Bun. - Type-safe: handler 의 query / body / response 자동 inferred. - 작은 + 빠름. ## 💻 코드 패턴 ### Hono 기본 ```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') })); app.post('/users', async (c) => { const body = await c.req.json(); return c.json({ id: '...', ...body }, 201); }); export default app; ``` ### Bun 으로 실행 ```bash bun run --hot src/index.ts ``` ### Cloudflare Workers ```ts // wrangler.toml [observability] enabled = true // src/index.ts import { Hono } from 'hono'; const app = new Hono<{ Bindings: { DB: D1Database; CACHE: KVNamespace } }>(); app.get('/users/:id', async (c) => { const id = c.req.param('id'); const cached = await c.env.CACHE.get(`user:${id}`); if (cached) return c.json(JSON.parse(cached)); const user = await c.env.DB.prepare('SELECT * FROM users WHERE id = ?').bind(id).first(); await c.env.CACHE.put(`user:${id}`, JSON.stringify(user), { expirationTtl: 60 }); return c.json(user); }); export default app; ``` ### Vercel Edge / Deno ```ts import { Hono } from 'hono'; const app = new Hono(); // ... routes export default app; // 자동 detect ``` ### Middleware ```ts import { logger } from 'hono/logger'; import { cors } from 'hono/cors'; import { compress } from 'hono/compress'; import { jwt } from 'hono/jwt'; app.use('*', logger()); app.use('*', cors({ origin: 'https://app.com' })); app.use('*', compress()); app.use('/api/*', jwt({ secret: process.env.JWT_SECRET! })); app.get('/api/me', (c) => { const payload = c.get('jwtPayload'); return c.json({ user: payload.sub }); }); ``` ### Zod validator ```ts import { zValidator } from '@hono/zod-validator'; import { z } from 'zod'; const CreateUser = z.object({ email: z.string().email(), name: z.string().min(1), }); app.post('/users', zValidator('json', CreateUser), async (c) => { const data = c.req.valid('json'); // typed // ... }); ``` ### Hono RPC (TS client) ```ts // server const route = app.get('/users/:id', (c) => c.json({ id: c.req.param('id'), name: 'A' })); export type AppType = typeof route; // client (frontend) import { hc } from 'hono/client'; import type { AppType } from '../server'; const client = hc('http://localhost:3000'); const r = await client.users[':id'].$get({ param: { id: '1' } }); const data = await r.json(); // typed! ``` → tRPC 비슷 — type 가 client / server 공유. ### Streaming (SSE) ```ts import { streamSSE } from 'hono/streaming'; app.get('/stream', (c) => { return streamSSE(c, async (stream) => { while (true) { await stream.writeSSE({ data: JSON.stringify({ time: Date.now() }), event: 'tick', id: crypto.randomUUID(), }); await stream.sleep(1000); } }); }); ``` ### Error handler ```ts import { HTTPException } from 'hono/http-exception'; app.onError((err, c) => { if (err instanceof HTTPException) { return c.json({ error: err.message }, err.status); } return c.json({ error: 'Internal' }, 500); }); app.get('/protected', (c) => { const auth = c.req.header('Authorization'); if (!auth) throw new HTTPException(401, { message: 'Unauthorized' }); return c.json({ ok: true }); }); ``` ### Elysia (Bun-only, 매우 빠름) ```ts import { Elysia, t } from 'elysia'; const app = new Elysia() .get('/', () => 'Hello') .get('/users/:id', ({ params: { id } }) => ({ id })) .post('/users', ({ body }) => ({ ...body, id: '...' }), { body: t.Object({ email: t.String({ format: 'email' }), name: t.String({ minLength: 1 }), }), }) .listen(3000); console.log(`http://localhost:${app.server?.port}`); ``` → TypeBox (JSON Schema) — Bun native. ### Elysia plugins ```ts import { swagger } from '@elysiajs/swagger'; import { jwt } from '@elysiajs/jwt'; import { cors } from '@elysiajs/cors'; app .use(swagger()) // /swagger 자동 docs .use(cors()) .use(jwt({ secret: process.env.JWT_SECRET })); ``` ### Bun.serve (raw, 가장 빠름) ```ts Bun.serve({ port: 3000, fetch(req) { const url = new URL(req.url); if (url.pathname === '/') return new Response('Hello'); if (url.pathname.startsWith('/api/')) return apiHandler(req); return new Response('Not found', { status: 404 }); }, }); ``` → Framework 없이. 가장 raw. ### Performance ``` Bun.serve: > 100K req/s (single core) Elysia: ~80K req/s Hono on Bun: ~80K req/s Hono on Node: ~30K req/s Express: ~10K req/s → 측정 + workload 따라. ``` ### File-based routing ``` Hono = code-first. Elysia = code-first. File-based 원하면: - Next.js App Router - Tanstack Start - Astro endpoints - Hono + 자체 file scanner ``` ### Database 통합 ```ts // Hono + Drizzle import { drizzle } from 'drizzle-orm/postgres-js'; import postgres from 'postgres'; const sql = postgres(process.env.DATABASE_URL!); const db = drizzle(sql); app.get('/users/:id', async (c) => { const user = await db.select().from(usersTable).where(eq(usersTable.id, c.req.param('id'))).limit(1); return c.json(user[0]); }); ``` ### Edge DB combo ``` Cloudflare Workers + D1: Hono + D1 binding Vercel Edge + Neon: Hono + neon HTTP Bun + Postgres: Hono / Elysia + Bun pg ``` ### vs Express ``` Express: + 커뮤니티 큼 + 미들웨어 많음 - 옛 callback API - Type 약함 - Edge 호환 X (Node only) Hono / Elysia: + Modern TS + Edge runtime + 빠름 + Type-safe - 작은 ecosystem (커지는 중) ``` ### Migration (Express → Hono) ```ts // Express app.get('/users/:id', async (req, res) => { res.json(await getUser(req.params.id)); }); // Hono app.get('/users/:id', async (c) => { return c.json(await getUser(c.req.param('id'))); }); ``` → 비슷. Migration 가능. ### Testing (Hono) ```ts import { Hono } from 'hono'; import { test, expect } from 'vitest'; const app = new Hono(); app.get('/', (c) => c.text('Hello')); test('GET /', async () => { const res = await app.request('/'); expect(res.status).toBe(200); expect(await res.text()).toBe('Hello'); }); ``` → App.request — 외부 server 필요 X. ### Deployment options ``` Hono: - Cloudflare Workers - Vercel Edge - AWS Lambda (with adapter) - Bun - Node - Deno Elysia: - Bun (only) ``` ### Build / size ``` Hono: ~12 KB (gzip) Elysia: ~30 KB Express: ~120 KB → Edge runtime 친화. ``` ## 🤔 의사결정 기준 | 환경 | 추천 | |---|---| | Edge runtime | Hono | | Bun max performance | Elysia | | Node + 큰 ecosystem | Hono 또는 Express | | Multi-cloud / portable | Hono | | File-based + full-stack | Next / Tanstack Start | | Raw / 가장 빠른 | Bun.serve | ## ❌ 안티패턴 - **Express + Edge runtime**: 호환 X. - **Node-specific module on Edge**: 깨짐. - **fetch / Response 의 standard 알기 X**: API confusion. - **Hono RPC + 큰 schema**: 빌드 시간. - **Elysia + Node**: Bun 만. - **Middleware 너무 많이**: latency. - **Type 안 활용**: 의미 없는 framework 선택. ## 🤖 LLM 활용 힌트 - Hono = universal modern. - Elysia = Bun 친화 + 빠름. - Web Standard API (Request / Response). - Hono RPC = type-safe TS fullstack. ## 🔗 관련 문서 - [[Backend_API_Gateway_BFF]] - [[Runtime_Bun_Deno_Comparison]] - [[API_OpenAPI_Spec]]