spinny:~/writing $ vim hono-framework-guide.md
1~2Eveken at egy JavaScript web framework valasztasa kompromisszumot jelentett: az Express univerzalis volt, de lassu es Node-hoz kotott, a Fastify gyors, de csak Node, a Next.js teljes erteku, de nehez. Amikor megerkeztek az edge runtime-ok — Cloudflare Workers, Deno Deploy, Bun, Vercel Edge — ezek a frameworkok megmutattak a hatarukat.3~4A [Hono](https://hono.dev) (japanul "lang" 🔥) a modern valasz. Egy 14KB alatti framework, teljes mertekben Web Standardson (`Request`, `Response`, `fetch`) epul, es minden olyan helyen fut, ahol JavaScript runtime van. Ugyanaz a kod deployolhato Cloudflare Workersre, Bunra, Denora, Node.js-re, Vercelra, Netlifyra es AWS Lambdara — valtoztatas nelkul.5~6## Miert Hono7~81. **Teljesitmeny.** A `RegExpRouter` minden route mintat egyetlen regexbe forditja. A benchmarkok meghaladjak a 400 000 ops/s-ot.92. **Hordozhatosag.** A Web Standards nulla Node fuggoseget jelent.103. **TypeScript-first DX.** Path parametereket literal tipuskent kovetkezteti ki, end-to-end type-safe RPC kliens.11~12```mermaid13graph LR14 Client[Client] -->|Request| App[app.fetch]15 App --> MW1[Middleware 1]16 MW1 --> MW2[Middleware 2]17 MW2 --> Router[RegExpRouter]18 Router --> Handler[Route Handler]19 Handler --> Context[c.json / c.text]20 Context -->|Response| Client21 App -.->|deploy| CF[Cloudflare Workers]22 App -.->|deploy| Bun[Bun]23 App -.->|deploy| Deno[Deno]24 App -.->|deploy| Node[Node.js]25 App -.->|deploy| Vercel[Vercel]26```27~28## Kezdes29~30```bash31npm create hono@latest my-api32cd my-api33npm install34npm run dev35```36~37```typescript38// src/index.ts39import { Hono } from 'hono'40~41const app = new Hono()42~43app.get('/', (c) => c.text('Hello Hono!'))44~45export default app46```47~48Cloudflare Workersen ennyi eleg. Bunon: `Bun.serve({ fetch: app.fetch, port: 3000 })`. Node-on: `serve({ fetch: app.fetch })` a `@hono/node-server`-bol.49~50## Routing51~52```typescript53import { Hono } from 'hono'54~55const app = new Hono()56~57app.get('/', (c) => c.text('Home'))58app.get('/posts/:id', (c) => {59 const id = c.req.param('id')60 return c.json({ id })61})62app.get('/posts/:id/comments/:commentId', (c) => {63 const { id, commentId } = c.req.param()64 return c.json({ id, commentId })65})66app.get('/files/*', (c) => c.text('Wildcard'))67app.post('/posts', async (c) => {68 const body = await c.req.json()69 return c.json({ created: body }, 201)70})71```72~73### Routok csoportositasa74~75```typescript76// routes/posts.ts77import { Hono } from 'hono'78~79const posts = new Hono()80posts.get('/', (c) => c.json({ posts: [] }))81posts.get('/:id', (c) => c.json({ id: c.req.param('id') }))82export default posts83~84// src/index.ts85import { Hono } from 'hono'86import posts from './routes/posts'87~88const app = new Hono()89app.route('/posts', posts)90```91~92## A Context objektum93~94```typescript95app.post('/echo', async (c) => {96 const userAgent = c.req.header('User-Agent')97 const page = c.req.query('page')98 const body = await c.req.json()99 const env = c.env100~101 c.set('requestId', crypto.randomUUID())102 const id = c.get('requestId')103~104 c.header('X-Request-Id', id)105 c.status(200)106 return c.json({ userAgent, page, body, id })107})108```109~110## Middleware: hagyma modell111~112```typescript113import { Hono } from 'hono'114import { logger } from 'hono/logger'115import { cors } from 'hono/cors'116import { secureHeaders } from 'hono/secure-headers'117~118const app = new Hono()119~120app.use('*', logger())121app.use('*', secureHeaders())122app.use('/api/*', cors({ origin: 'https://spinny.dev' }))123~124app.use('*', async (c, next) => {125 const start = performance.now()126 await next()127 c.header('X-Response-Time', `${performance.now() - start}ms`)128})129```130~131### Beepitett middleware-ek132~133| Middleware | Cel |134|-----------|---------|135| `logger` | Strukturalt logok metodusrol, utvonalrol, statuszrol, idotartamrol |136| `cors` | Origin, metodusok, headerek szerint konfiguralhato CORS |137| `csrf` | Origin alapu CSRF vedelem |138| `secureHeaders` | Beallitja CSP, HSTS, X-Frame-Options |139| `bearerAuth` / `basicAuth` | Keszen hasznalhato Bearer/Basic auth |140| `jwt` | JWT verify/sign `jose`-val |141| `etag` | ETag generalas es 304 kezeles |142| `cache` | Cache a Web Cache API-n keresztul |143| `compress` | gzip/deflate a valaszon |144| `bodyLimit` | Visszautasitja a kuszob feletti body-kat |145| `timing` | Server-Timing header profilingehoz |146~147### Type-safe egyedi middleware148~149```typescript150import { createMiddleware } from 'hono/factory'151~152type AuthVars = { userId: string; role: 'user' | 'admin' }153~154export const requireAuth = createMiddleware<{ Variables: AuthVars }>(155 async (c, next) => {156 const token = c.req.header('Authorization')?.replace('Bearer ', '')157 if (!token) return c.json({ error: 'Unauthorized' }, 401)158~159 const payload = await verifyJwt(token)160 c.set('userId', payload.sub)161 c.set('role', payload.role)162 await next()163 }164)165~166app.get('/me', requireAuth, (c) => {167 const userId = c.var.userId168 return c.json({ userId })169})170```171~172## Validacio Zoddal173~174```typescript175import { Hono } from 'hono'176import { zValidator } from '@hono/zod-validator'177import { z } from 'zod'178~179const createPost = z.object({180 title: z.string().min(1).max(200),181 body: z.string().min(1),182 tags: z.array(z.string()).default([]),183})184~185app.post(186 '/posts',187 zValidator('json', createPost),188 (c) => {189 const data = c.req.valid('json')190 return c.json({ ok: true, post: data }, 201)191 }192)193```194~195## RPC: end-to-end type-safe kliens196~197```typescript198// server.ts199import { Hono } from 'hono'200import { zValidator } from '@hono/zod-validator'201import { z } from 'zod'202~203const app = new Hono()204 .get('/posts/:id', (c) =>205 c.json({ id: c.req.param('id'), title: 'Hello' })206 )207 .post(208 '/posts',209 zValidator('json', z.object({ title: z.string(), body: z.string() })),210 (c) => c.json({ ok: true }, 201)211 )212~213export type AppType = typeof app214export default app215```216~217```typescript218// client.ts219import { hc } from 'hono/client'220import type { AppType } from './server'221~222const client = hc<AppType>('https://api.spinny.dev')223~224const res = await client.posts[':id'].$get({ param: { id: '42' } })225if (res.ok) {226 const data = await res.json()227 console.log(data.title)228}229~230const created = await client.posts.$post({231 json: { title: 'Szia', body: 'A Hono egy lang' },232})233```234~235### Status kod megkulonboztetes236~237```typescript238.get('/posts/:id', (c) => {239 const post = findPost(c.req.param('id'))240 if (!post) return c.json({ error: 'not found' }, 404)241 return c.json({ post }, 200)242})243```244~245```typescript246const res = await client.posts[':id'].$get({ param: { id } })247if (res.status === 404) {248 const { error } = await res.json()249}250if (res.status === 200) {251 const { post } = await res.json()252}253```254~255## Routerek es teljesitmeny256~257| Router | Erossegek | Mikor hasznalja |258|--------|-----------|-------------|259| `RegExpRouter` | Maximalis sebesseg, fordított regex | Default a legtobb API-hoz |260| `TrieRouter` | Minden mintat tamogat | Komplex mintak, amiket a RegExp nem kezel |261| `SmartRouter` | Automatikusan a legjobbat valasztja | Ajanlott default |262| `LinearRouter` | Ultragyors regisztracio | One-shot workerek, kritikus cold start |263| `PatternRouter` | Minimalis bundle (<15KB) | Extrem meretkorlatozasok |264~265```typescript266import { Hono } from 'hono'267import { LinearRouter } from 'hono/router/linear-router'268~269const app = new Hono({ router: new LinearRouter() })270```271~272## Multi-runtime deploy273~274### Cloudflare Workers275~276```typescript277import { Hono } from 'hono'278~279type Bindings = { MY_KV: KVNamespace; DB: D1Database }280const app = new Hono<{ Bindings: Bindings }>()281~282app.get('/cache/:key', async (c) => {283 const value = await c.env.MY_KV.get(c.req.param('key'))284 return c.json({ value })285})286~287export default app288```289~290Deploy: `npx wrangler deploy`.291~292### Bun293~294```typescript295import { Hono } from 'hono'296const app = new Hono()297app.get('/', (c) => c.text('Bun + Hono'))298~299Bun.serve({ fetch: app.fetch, port: 3000 })300```301~302### Node.js303~304```typescript305import { serve } from '@hono/node-server'306import { Hono } from 'hono'307~308const app = new Hono()309app.get('/', (c) => c.text('Node + Hono'))310~311serve({ fetch: app.fetch, port: 3000 })312```313~314### Deno315~316```typescript317import { Hono } from 'jsr:@hono/hono'318const app = new Hono()319app.get('/', (c) => c.text('Deno + Hono'))320Deno.serve(app.fetch)321```322~323### Vercel324~325```typescript326// api/[[...route]].ts327import { Hono } from 'hono'328import { handle } from 'hono/vercel'329~330const app = new Hono().basePath('/api')331app.get('/hello', (c) => c.json({ msg: 'Hello from Vercel' }))332~333export const GET = handle(app)334export const POST = handle(app)335```336~337## Gyakorlati pelda: REST API auth-tel es DB-vel338~339```typescript340import { Hono } from 'hono'341import { jwt } from 'hono/jwt'342import { logger } from 'hono/logger'343import { cors } from 'hono/cors'344import { zValidator } from '@hono/zod-validator'345import { z } from 'zod'346~347type Bindings = { DB: D1Database; JWT_SECRET: string }348type Variables = { jwtPayload: { sub: string } }349~350const app = new Hono<{ Bindings: Bindings; Variables: Variables }>()351~352app.use('*', logger())353app.use('/api/*', cors({ origin: 'https://spinny.dev', credentials: true }))354~355const auth = (c: any, next: any) =>356 jwt({ secret: c.env.JWT_SECRET })(c, next)357~358const api = app.basePath('/api')359~360api.get('/posts', async (c) => {361 const { results } = await c.env.DB362 .prepare('SELECT id, title, created_at FROM posts ORDER BY created_at DESC LIMIT 50')363 .all()364 return c.json({ posts: results })365})366~367api.post(368 '/posts',369 auth,370 zValidator('json', z.object({371 title: z.string().min(1).max(200),372 body: z.string().min(1),373 })),374 async (c) => {375 const { title, body } = c.req.valid('json')376 const userId = c.var.jwtPayload.sub377 const result = await c.env.DB378 .prepare('INSERT INTO posts (title, body, author_id) VALUES (?, ?, ?) RETURNING id')379 .bind(title, body, userId)380 .first<{ id: number }>()381 return c.json({ id: result?.id }, 201)382 }383)384~385api.onError((err, c) => {386 console.error(err)387 return c.json({ error: 'Internal error' }, 500)388})389~390export type AppType = typeof api391export default app392```393~394## Tesztelés395~396```typescript397import { describe, it, expect } from 'vitest'398import app from '../src/index'399~400describe('GET /api/posts', () => {401 it('visszaadja a posztok listajat', async () => {402 const res = await app.request('/api/posts')403 expect(res.status).toBe(200)404 const body = await res.json()405 expect(body.posts).toBeInstanceOf(Array)406 })407})408```409~410## Legjobb gyakorlatok411~412### 1. Lancold a route definiciokat413~414```typescript415const app = new Hono()416 .get('/posts', handler1)417 .post('/posts', handler2)418 .get('/posts/:id', handler3)419```420~421### 2. Exportald a tipust, ne az implementaciot422~423A kliensnek `AppType`-ot kell importalnia.424~425### 3. Egy router domain-enkent426~427Sub-app `posts`, `users`, `webhooks` szamara.428~429### 4. Validacio a hataron, mindig430~431Minden kulso bemenetnek at kell mennie a `zValidator`-on.432~433### 5. Tamaszkodj a bindingsre, ne a globalis kliensekre434~435Cloudflare-en ferj hozza KV/D1/R2-hoz `c.env`-en keresztul.436~437### 6. Mérj, mielott optimalizalod a routert438~439A default `SmartRouter` az esetek 95%-ban megfelel.440~441## Konkluzio442~443A Hono 2026-ban de facto standardda valt edge-ready API-k epitesehez TypeScriptben.444~445> **Indulasi checklist:**446>447> - [x] `npm create hono@latest` es valaszd a runtime sablont448> - [x] Definialj routokat lancolassal (`.get(...).post(...)`)449> - [x] Add hozza `logger`, `cors`, `secureHeaders`-t globalis middleware-kent450> - [x] Validalj minden bemenetet a `@hono/zod-validator`-ral451> - [x] Exportald `AppType`-ot es hasznald az API-t a type-safe `hc` klienssel452> - [x] Irj teszteket `app.request()`-tel — HTTP szerver nelkul453> - [x] Deployolj `wrangler deploy`-jal (CF), `vercel deploy`-jal vagy a runtime bundlerével454~
NORMAL · hono-framework-guide.md [readonly]454 lines · :q to close