spinny:~/writing $ vim hono-framework-guide.md
1~2Yıllarca bir JavaScript web framework''ü seçmek bir taviz vermek anlamına geliyordu: Express evrenseldi ama yavaş ve Node''a bağlıydı, Fastify hızlıydı ama yalnızca Node, Next.js eksiksizdi ama ağırdı. Edge runtime''ları — Cloudflare Workers, Deno Deploy, Bun, Vercel Edge — geldiğinde, bu framework''ler sınırlarını gösterdi: uyumsuz bağımlılıklar, devasa bundle''lar, Node''un `req`/`res`''sine bağlı API''ler.3~4[Hono](https://hono.dev) (Japoncada "alev" 🔥) modern cevap. Tamamen Web Standartları (`Request`, `Response`, `fetch`) üzerine kurulu 14KB altı bir framework, JavaScript runtime''i olan her yerde çalışır. Aynı kod Cloudflare Workers, Bun, Deno, Node.js, Vercel, Netlify ve AWS Lambda''ya değişiklik olmadan deploy edilir.5~6## Neden Hono7~81. **Performans.** `RegExpRouter` tüm route pattern''lerini tek bir regex''e derler, geleneksel router''ların doğrusal döngülerinden kaçınır. Benchmark''lar saniyede 400.000 op''u aşıyor.92. **Taşınabilirlik.** Web Standartları sıfır Node bağımlılığı demektir. Aynı `app.fetch` Cloudflare Worker''da default export edilir, `Bun.serve`''e geçirilir, Deno sunucusuna mount edilir veya `@hono/node-server` ile uyarlanır.103. **TypeScript-first DX.** Literal tipler olarak çıkarsanan path parametreleri, uçtan uca tip güvenli RPC istemcisi.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## Başlamak29~30```bash31npm create hono@latest my-api32cd my-api33npm install34npm run dev35```36~37Starter hangi şablonu kullanacağını sorar: `cloudflare-workers`, `bun`, `deno`, `nodejs`, `vercel`, `aws-lambda`, `nextjs` ve daha fazlası. Hızlıca denemek için tek bir dosyadan da başlayabilirsiniz:38~39```typescript40// src/index.ts41import { Hono } from 'hono'42~43const app = new Hono()44~45app.get('/', (c) => c.text('Hello Hono!'))46~47export default app48```49~50Cloudflare Workers''ta bu yeterli. Bun''da: `Bun.serve({ fetch: app.fetch, port: 3000 })`. Node''da: `@hono/node-server` üzerinden `serve({ fetch: app.fetch })`.51~52## Routing53~54```typescript55import { Hono } from 'hono'56~57const app = new Hono()58~59app.get('/', (c) => c.text('Home'))60app.get('/posts/:id', (c) => {61 const id = c.req.param('id') // string, tipli62 return c.json({ id })63})64app.get('/posts/:id/comments/:commentId', (c) => {65 const { id, commentId } = c.req.param()66 return c.json({ id, commentId })67})68app.get('/files/*', (c) => c.text('Wildcard'))69app.post('/posts', async (c) => {70 const body = await c.req.json()71 return c.json({ created: body }, 201)72})73```74~75Parametreler literal tip olarak çıkarsanır. Yanlış yazım derleme zamanı hatasıdır.76~77### Route gruplama78~79```typescript80// routes/posts.ts81import { Hono } from 'hono'82~83const posts = new Hono()84posts.get('/', (c) => c.json({ posts: [] }))85posts.get('/:id', (c) => c.json({ id: c.req.param('id') }))86export default posts87~88// src/index.ts89import { Hono } from 'hono'90import posts from './routes/posts'91~92const app = new Hono()93app.route('/posts', posts)94```95~96## Context nesnesi97~98```typescript99app.post('/echo', async (c) => {100 const userAgent = c.req.header('User-Agent')101 const page = c.req.query('page')102 const body = await c.req.json()103 const env = c.env104~105 c.set('requestId', crypto.randomUUID())106 const id = c.get('requestId')107~108 c.header('X-Request-Id', id)109 c.status(200)110 return c.json({ userAgent, page, body, id })111})112```113~114## Middleware: soğan modeli115~116```typescript117import { Hono } from 'hono'118import { logger } from 'hono/logger'119import { cors } from 'hono/cors'120import { secureHeaders } from 'hono/secure-headers'121~122const app = new Hono()123~124app.use('*', logger())125app.use('*', secureHeaders())126app.use('/api/*', cors({ origin: 'https://spinny.dev' }))127~128app.use('*', async (c, next) => {129 const start = performance.now()130 await next()131 c.header('X-Response-Time', `${performance.now() - start}ms`)132})133```134~135### Yerleşik middleware136~137| Middleware | İşlevi |138|-----------|---------|139| `logger` | Metod, path, durum, süreyi yapılandırılmış loglar |140| `cors` | Origin, metod, header''a göre yapılandırılabilir CORS |141| `csrf` | Origin tabanlı CSRF koruması |142| `secureHeaders` | CSP, HSTS, X-Frame-Options ayarlar |143| `bearerAuth` / `basicAuth` | Hazır Bearer/Basic kimlik doğrulama |144| `jwt` | `jose` ile JWT doğrulama/imzalama |145| `etag` | ETag üretir, 304''ü işler |146| `cache` | Web Cache API üzerinden önbellek |147| `compress` | Yanıt için gzip/deflate |148| `bodyLimit` | Eşik üstü body''leri reddeder |149| `timing` | Profil için Server-Timing header''ı |150~151### Tip güvenli özel middleware152~153```typescript154import { createMiddleware } from 'hono/factory'155~156type AuthVars = { userId: string; role: 'user' | 'admin' }157~158export const requireAuth = createMiddleware<{ Variables: AuthVars }>(159 async (c, next) => {160 const token = c.req.header('Authorization')?.replace('Bearer ', '')161 if (!token) return c.json({ error: 'Unauthorized' }, 401)162~163 const payload = await verifyJwt(token)164 c.set('userId', payload.sub)165 c.set('role', payload.role)166 await next()167 }168)169~170app.get('/me', requireAuth, (c) => {171 const userId = c.var.userId172 return c.json({ userId })173})174```175~176## Zod ile doğrulama177~178```typescript179import { Hono } from 'hono'180import { zValidator } from '@hono/zod-validator'181import { z } from 'zod'182~183const createPost = z.object({184 title: z.string().min(1).max(200),185 body: z.string().min(1),186 tags: z.array(z.string()).default([]),187})188~189app.post(190 '/posts',191 zValidator('json', createPost),192 (c) => {193 const data = c.req.valid('json')194 return c.json({ ok: true, post: data }, 201)195 }196)197```198~199## RPC: uçtan uca tip güvenli istemci200~201```typescript202// server.ts203import { Hono } from 'hono'204import { zValidator } from '@hono/zod-validator'205import { z } from 'zod'206~207const app = new Hono()208 .get('/posts/:id', (c) =>209 c.json({ id: c.req.param('id'), title: 'Hello' })210 )211 .post(212 '/posts',213 zValidator('json', z.object({ title: z.string(), body: z.string() })),214 (c) => c.json({ ok: true }, 201)215 )216~217export type AppType = typeof app218export default app219```220~221```typescript222// client.ts223import { hc } from 'hono/client'224import type { AppType } from './server'225~226const client = hc<AppType>('https://api.spinny.dev')227~228const res = await client.posts[':id'].$get({ param: { id: '42' } })229if (res.ok) {230 const data = await res.json()231 console.log(data.title)232}233~234const created = await client.posts.$post({235 json: { title: 'Merhaba', body: 'Hono bir alev' },236})237```238~239### Durum kodu ayrıştırma240~241```typescript242.get('/posts/:id', (c) => {243 const post = findPost(c.req.param('id'))244 if (!post) return c.json({ error: 'not found' }, 404)245 return c.json({ post }, 200)246})247```248~249```typescript250const res = await client.posts[':id'].$get({ param: { id } })251if (res.status === 404) {252 const { error } = await res.json()253}254if (res.status === 200) {255 const { post } = await res.json()256}257```258~259## Router''lar ve performans260~261| Router | Güçlü yönler | Ne zaman |262|--------|-----------|-------------|263| `RegExpRouter` | Maksimum hız, derlenmiş regex | Çoğu API için varsayılan |264| `TrieRouter` | Tüm pattern''leri destekler | RegExp''in işleyemediği karmaşık pattern''ler |265| `SmartRouter` | En iyiyi otomatik seçer | Önerilen varsayılan |266| `LinearRouter` | Ultra hızlı kayıt | Tek atışlık worker, kritik soğuk başlangıç |267| `PatternRouter` | Minimal bundle (<15KB) | Aşırı boyut kısıtları |268~269```typescript270import { Hono } from 'hono'271import { LinearRouter } from 'hono/router/linear-router'272~273const app = new Hono({ router: new LinearRouter() })274```275~276## Çoklu runtime deploy277~278### Cloudflare Workers279~280```typescript281import { Hono } from 'hono'282~283type Bindings = { MY_KV: KVNamespace; DB: D1Database }284const app = new Hono<{ Bindings: Bindings }>()285~286app.get('/cache/:key', async (c) => {287 const value = await c.env.MY_KV.get(c.req.param('key'))288 return c.json({ value })289})290~291export default app292```293~294Deploy: `npx wrangler deploy`.295~296### Bun297~298```typescript299import { Hono } from 'hono'300const app = new Hono()301app.get('/', (c) => c.text('Bun + Hono'))302~303Bun.serve({ fetch: app.fetch, port: 3000 })304```305~306### Node.js307~308```typescript309import { serve } from '@hono/node-server'310import { Hono } from 'hono'311~312const app = new Hono()313app.get('/', (c) => c.text('Node + Hono'))314~315serve({ fetch: app.fetch, port: 3000 })316```317~318### Deno319~320```typescript321import { Hono } from 'jsr:@hono/hono'322const app = new Hono()323app.get('/', (c) => c.text('Deno + Hono'))324Deno.serve(app.fetch)325```326~327### Vercel328~329```typescript330// api/[[...route]].ts331import { Hono } from 'hono'332import { handle } from 'hono/vercel'333~334const app = new Hono().basePath('/api')335app.get('/hello', (c) => c.json({ msg: 'Hello from Vercel' }))336~337export const GET = handle(app)338export const POST = handle(app)339```340~341## Pratik örnek: kimlik doğrulamalı ve veritabanlı REST API342~343```typescript344import { Hono } from 'hono'345import { jwt } from 'hono/jwt'346import { logger } from 'hono/logger'347import { cors } from 'hono/cors'348import { zValidator } from '@hono/zod-validator'349import { z } from 'zod'350~351type Bindings = { DB: D1Database; JWT_SECRET: string }352type Variables = { jwtPayload: { sub: string } }353~354const app = new Hono<{ Bindings: Bindings; Variables: Variables }>()355~356app.use('*', logger())357app.use('/api/*', cors({ origin: 'https://spinny.dev', credentials: true }))358~359const auth = (c: any, next: any) =>360 jwt({ secret: c.env.JWT_SECRET })(c, next)361~362const api = app.basePath('/api')363~364api.get('/posts', async (c) => {365 const { results } = await c.env.DB366 .prepare('SELECT id, title, created_at FROM posts ORDER BY created_at DESC LIMIT 50')367 .all()368 return c.json({ posts: results })369})370~371api.post(372 '/posts',373 auth,374 zValidator('json', z.object({375 title: z.string().min(1).max(200),376 body: z.string().min(1),377 })),378 async (c) => {379 const { title, body } = c.req.valid('json')380 const userId = c.var.jwtPayload.sub381 const result = await c.env.DB382 .prepare('INSERT INTO posts (title, body, author_id) VALUES (?, ?, ?) RETURNING id')383 .bind(title, body, userId)384 .first<{ id: number }>()385 return c.json({ id: result?.id }, 201)386 }387)388~389api.onError((err, c) => {390 console.error(err)391 return c.json({ error: 'Internal error' }, 500)392})393~394export type AppType = typeof api395export default app396```397~398## Test399~400```typescript401import { describe, it, expect } from 'vitest'402import app from '../src/index'403~404describe('GET /api/posts', () => {405 it('post listesini döner', async () => {406 const res = await app.request('/api/posts')407 expect(res.status).toBe(200)408 const body = await res.json()409 expect(body.posts).toBeInstanceOf(Array)410 })411})412```413~414## En iyi uygulamalar415~416### 1. Route tanımlarını zincirleyin417~418```typescript419const app = new Hono()420 .get('/posts', handler1)421 .post('/posts', handler2)422 .get('/posts/:id', handler3)423```424~425### 2. Uygulamayı değil, tipi export edin426~427İstemci `AppType`''i import etmeli.428~429### 3. Her domain için bir router430~431`posts`, `users`, `webhooks` için alt uygulamalar.432~433### 4. Sınırda her zaman doğrulayın434~435Her dış girdi `zValidator`''den geçmeli.436~437### 5. Global istemcilere değil binding''lere yaslanın438~439Cloudflare''de KV/D1/R2''ye `c.env` üzerinden erişin.440~441### 6. Router''ı optimize etmeden önce ölçün442~443Varsayılan `SmartRouter` vakaların %95''i için uygundur.444~445## Sonuç446~447Hono 2026''da TypeScript ile edge''e hazır API''ler oluşturmanın fiili standardı haline geldi.448~449> **Başlangıç kontrol listesi:**450>451> - [x] `npm create hono@latest` ve runtime şablonunuzu seçin452> - [x] Route''ları zincirleyerek tanımlayın (`.get(...).post(...)`)453> - [x] `logger`, `cors`, `secureHeaders`''ı global middleware olarak ekleyin454> - [x] Her girdiyi `@hono/zod-validator` ile doğrulayın455> - [x] `AppType` export edin ve API''yi tip güvenli `hc` istemcisiyle tüketin456> - [x] HTTP sunucusu olmadan `app.request()` ile testler yazın457> - [x] `wrangler deploy` (CF), `vercel deploy` veya runtime''ınızın bundler''ı ile deploy edin458~
NORMAL · hono-framework-guide.md [readonly]458 lines · :q to close