सालों तक JavaScript वेब फ्रेमवर्क चुनने का मतलब था एक ट्रेड-ऑफ स्वीकार करना: Express यूनिवर्सल था पर धीमा और Node-बाउंड, Fastify तेज़ था पर सिर्फ Node के लिए, Next.js फुल-फ़ीचर्ड था पर भारी। जब एज रनटाइम — Cloudflare Workers, Deno Deploy, Bun, Vercel Edge — आए, इन फ्रेमवर्क्स की सीमाएं उजागर हुईं: इनकंपैटिबल डिपेंडेंसीज़, बड़े बंडल, Node के req/res से बंधे APIs।
Hono (जापानी में "ज्वाला" 🔥) इसका आधुनिक उत्तर है। 14KB से कम का फ्रेमवर्क, पूरी तरह वेब स्टैंडर्ड्स (Request, Response, fetch) पर बना, जो हर JavaScript रनटाइम पर चलता है। एक ही कोड Cloudflare Workers, Bun, Deno, Node.js, Vercel, Netlify और AWS Lambda पर बिना बदलाव के डिप्लॉय होता है।
Hono क्यों
Hono तीन चीज़ें सबसे बेहतर करता है:
- परफॉर्मेंस।
RegExpRouterसभी रूट पैटर्न्स को एक regex में कंपाइल करता है, ट्रेडिशनल राउटर्स के लीनियर लूप से बचता है। बेंचमार्क 4 लाख ops/s से ऊपर हैं। - पोर्टेबिलिटी। वेब स्टैंडर्ड्स यानी ज़ीरो Node डिपेंडेंसी। एक ही
app.fetchCloudflare Worker में default export,Bun.serveको पास, Deno सर्वर पर माउंट या@hono/node-serverसे अडॉप्ट होता है। - TypeScript-first DX। लिटरल टाइप के रूप में अनुमानित पाथ पैरामीटर्स, एंड-टू-एंड टाइप-सेफ RPC क्लाइंट, इनपुट और आउटपुट टाइप अनुमानित करने वाले वैलिडेटर्स।
शुरुआत
सबसे तेज़ रास्ता ऑफिशियल स्टार्टर है, जो आपके चुने रनटाइम के लिए प्रोजेक्ट स्कैफोल्ड करता है।
npm create hono@latest my-api cd my-api npm install npm run dev
स्टार्टर पूछता है कौन सा टेम्पलेट: cloudflare-workers, bun, deno, nodejs, vercel, aws-lambda, nextjs और भी। तुरंत आज़माने के लिए एक फाइल से भी शुरू कर सकते हैं:
// src/index.ts import { Hono } from 'hono' const app = new Hono() app.get('/', (c) => c.text('Hello Hono!')) export default app
Cloudflare Workers पर इतना काफी है। Bun पर: Bun.serve({ fetch: app.fetch, port: 3000 })। Node पर: @hono/node-server से serve({ fetch: app.fetch })।
रूटिंग
रूट्स HTTP वर्ब मेथड्स से डिक्लेयर होते हैं और पैरामीटर्स, वाइल्डकार्ड्स, regex को सपोर्ट करते हैं।
import { Hono } from 'hono' const app = new Hono() app.get('/', (c) => c.text('Home')) app.get('/posts/:id', (c) => { const id = c.req.param('id') // string, टाइप्ड return c.json({ id }) }) app.get('/posts/:id/comments/:commentId', (c) => { const { id, commentId } = c.req.param() return c.json({ id, commentId }) }) app.get('/files/*', (c) => c.text('Wildcard')) app.post('/posts', async (c) => { const body = await c.req.json() return c.json({ created: body }, 201) })
पैरामीटर्स लिटरल टाइप के रूप में अनुमानित होते हैं: TypeScript जानता है कि c.req.param('id') तभी string लौटाता है जब आपने पैटर्न में :id डिक्लेयर किया हो। नाम गलत होना कंपाइल-टाइम एरर है।
रूट्स को ग्रुप करना
app.route() सब-एप्लिकेशन्स को मॉड्यूल की तरह कंपोज़ करने देता है, हर एक का अपना प्रिफ़िक्स।
// routes/posts.ts import { Hono } from 'hono' const posts = new Hono() posts.get('/', (c) => c.json({ posts: [] })) posts.get('/:id', (c) => c.json({ id: c.req.param('id') })) export default posts // src/index.ts import { Hono } from 'hono' import posts from './routes/posts' const app = new Hono() app.route('/posts', posts)
नेस्टेड रूट्स रूट ऐप का बेस पाथ और टाइप विरासत में लेते हैं, इसलिए RPC क्लाइंट पूरी संरचना देखता है।
Context ऑब्जेक्ट
हर हैंडलर को एक c मिलता है — मौजूदा रिक्वेस्ट का Context। इनपुट पढ़ने और आउटपुट प्रोड्यूस करने के लिए सीखने वाला यही एक API है।
app.post('/echo', async (c) => { // पढ़ना const userAgent = c.req.header('User-Agent') const page = c.req.query('page') const body = await c.req.json() const env = c.env // बाइंडिंग्स (Cloudflare पर KV, D1, secrets) // मिडलवेयर और हैंडलर के बीच साझा वेरिएबल्स c.set('requestId', crypto.randomUUID()) const id = c.get('requestId') // रेस्पॉन्स c.header('X-Request-Id', id) c.status(200) return c.json({ userAgent, page, body, id }) })
मुख्य रेस्पॉन्स मेथड्स: c.text, c.json, c.html, c.body (raw), c.redirect। सभी दूसरे आर्ग्युमेंट के रूप में स्टेटस कोड लेते हैं।
मिडलवेयर: अनियन मॉडल
मिडलवेयर (c, next) => ... फंक्शन्स हैं जो हैंडलर के पहले और बाद में कोड चला सकते हैं। कंपोज़ होने पर ये क्लासिक अनियन मॉडल बनाते हैं: पहले रजिस्टर हुआ मिडलवेयर पहले शुरू और आखिर में खत्म होता है।
import { Hono } from 'hono' import { logger } from 'hono/logger' import { cors } from 'hono/cors' import { secureHeaders } from 'hono/secure-headers' const app = new Hono() app.use('*', logger()) app.use('*', secureHeaders()) app.use('/api/*', cors({ origin: 'https://spinny.dev' })) app.use('*', async (c, next) => { const start = performance.now() await next() c.header('X-Response-Time', `${performance.now() - start}ms`) })
await next() कंट्रोल अगले मिडलवेयर को देता है। उसके बाद का सब रेस्पॉन्स फेज़ में चलता है। next() से पहले Response लौटाने पर चेन शॉर्ट-सर्किट हो जाती है।
बिल्ट-इन मिडलवेयर
| मिडलवेयर | काम |
|---|---|
logger | मेथड, पाथ, स्टेटस, ड्यूरेशन का स्ट्रक्चर्ड लॉग |
cors | origin, मेथड्स, हेडर्स से कॉन्फ़िगर करने योग्य CORS |
csrf | origin-आधारित CSRF प्रोटेक्शन |
secureHeaders | CSP, HSTS, X-Frame-Options सेट करता है |
bearerAuth / basicAuth | आउट-ऑफ़-द-बॉक्स Bearer/Basic auth |
jwt | jose से JWT वेरिफ़ाई/साइन |
etag | ETag जेनरेट करता है, 304 हैंडल करता है |
cache | Web Cache API कैशिंग |
compress | रेस्पॉन्स पर gzip/deflate |
bodyLimit | थ्रेशोल्ड से ऊपर के बॉडी रिजेक्ट |
timing | प्रोफाइलिंग के लिए Server-Timing हेडर |
टाइप-सेफ कस्टम मिडलवेयर
import { createMiddleware } from 'hono/factory' type AuthVars = { userId: string; role: 'user' | 'admin' } export const requireAuth = createMiddleware<{ Variables: AuthVars }>( async (c, next) => { const token = c.req.header('Authorization')?.replace('Bearer ', '') if (!token) return c.json({ error: 'Unauthorized' }, 401) const payload = await verifyJwt(token) c.set('userId', payload.sub) c.set('role', payload.role) await next() } ) app.get('/me', requireAuth, (c) => { const userId = c.var.userId // string, टाइप्ड return c.json({ userId }) })
Zod से वैलिडेशन
import { Hono } from 'hono' import { zValidator } from '@hono/zod-validator' import { z } from 'zod' const createPost = 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', createPost), (c) => { const data = c.req.valid('json') return c.json({ ok: true, post: data }, 201) } )
अगर बॉडी वैलिडेट नहीं होती, Hono हैंडलर बुलाने से पहले Zod एरर के साथ 400 लौटाता है। query, param, header, cookie, form भी वैलिडेट कर सकते हैं।
RPC: एंड-टू-एंड टाइप-सेफ क्लाइंट
// server.ts import { Hono } from 'hono' import { zValidator } from '@hono/zod-validator' import { z } from 'zod' const app = new Hono() .get('/posts/:id', (c) => c.json({ id: c.req.param('id'), title: 'Hello' }) ) .post( '/posts', zValidator('json', z.object({ title: z.string(), body: z.string() })), (c) => c.json({ ok: true }, 201) ) export type AppType = typeof app export default app
// client.ts import { hc } from 'hono/client' import type { AppType } from './server' const client = hc<AppType>('https://api.spinny.dev') const res = await client.posts[':id'].$get({ param: { id: '42' } }) if (res.ok) { const data = await res.json() console.log(data.title) } const created = await client.posts.$post({ json: { title: 'नमस्ते', body: 'Hono एक ज्वाला है' }, })
सर्वर पर रूट का नाम बदलें और CI में क्लाइंट का TypeScript तुरंत टूट जाता है। tRPC जैसा फायदा, पर स्टैंडर्ड HTTP पर, बिना खास मिडलवेयर के, छोटे बंडल के साथ।
स्टेटस कोड डिस्क्रिमिनेशन
.get('/posts/:id', (c) => { const post = findPost(c.req.param('id')) if (!post) return c.json({ error: 'not found' }, 404) return c.json({ post }, 200) })
const res = await client.posts[':id'].$get({ param: { id } }) if (res.status === 404) { const { error } = await res.json() // { error: string } } if (res.status === 200) { const { post } = await res.json() // { post: Post } }
राउटर्स और परफॉर्मेंस
| राउटर | खूबियाँ | कब इस्तेमाल करें |
|---|---|---|
RegExpRouter | अधिकतम स्पीड, कंपाइल्ड regex | अधिकांश APIs के लिए डिफ़ॉल्ट |
TrieRouter | हर पैटर्न सपोर्ट | जटिल पैटर्न जो RegExp हैंडल नहीं करता |
SmartRouter | सबसे अच्छा ऑटो चुनता है | अनुशंसित डिफ़ॉल्ट |
LinearRouter | अल्ट्रा-फास्ट रजिस्ट्रेशन | वन-शॉट वर्कर्स, क्रिटिकल कोल्ड स्टार्ट |
PatternRouter | न्यूनतम बंडल (<15KB) | अत्यधिक साइज़ बाध्यताएं |
import { Hono } from 'hono' import { LinearRouter } from 'hono/router/linear-router' const app = new Hono({ router: new LinearRouter() })
मल्टी-रनटाइम डिप्लॉयमेंट
Cloudflare Workers
import { Hono } from 'hono' type Bindings = { MY_KV: KVNamespace; DB: D1Database } const app = new Hono<{ Bindings: Bindings }>() app.get('/cache/:key', async (c) => { const value = await c.env.MY_KV.get(c.req.param('key')) return c.json({ value }) }) export default app
डिप्लॉय: npx wrangler deploy।
Bun
import { Hono } from 'hono' const app = new Hono() app.get('/', (c) => c.text('Bun + Hono')) Bun.serve({ fetch: app.fetch, port: 3000 })
Node.js
import { serve } from '@hono/node-server' import { Hono } from 'hono' const app = new Hono() app.get('/', (c) => c.text('Node + Hono')) serve({ fetch: app.fetch, port: 3000 })
Deno
import { Hono } from 'jsr:@hono/hono' const app = new Hono() app.get('/', (c) => c.text('Deno + Hono')) Deno.serve(app.fetch)
Vercel
// api/[[...route]].ts import { Hono } from 'hono' import { handle } from 'hono/vercel' const app = new Hono().basePath('/api') app.get('/hello', (c) => c.json({ msg: 'Hello from Vercel' })) export const GET = handle(app) export const POST = handle(app)
वही बिज़नेस कोड, वही रूटिंग, वही मिडलवेयर। सिर्फ अडैप्टर बदलता है।
व्यावहारिक उदाहरण: ऑथ और DB के साथ REST API
import { Hono } from 'hono' import { jwt } from 'hono/jwt' import { logger } from 'hono/logger' import { cors } from 'hono/cors' import { zValidator } from '@hono/zod-validator' import { z } from 'zod' type Bindings = { DB: D1Database; JWT_SECRET: string } type Variables = { jwtPayload: { sub: string } } const app = new Hono<{ Bindings: Bindings; Variables: Variables }>() app.use('*', logger()) app.use('/api/*', cors({ origin: 'https://spinny.dev', credentials: true })) const auth = (c: any, next: any) => jwt({ secret: c.env.JWT_SECRET })(c, next) const api = app.basePath('/api') api.get('/posts', async (c) => { const { results } = await c.env.DB .prepare('SELECT id, title, created_at FROM posts ORDER BY created_at DESC LIMIT 50') .all() return c.json({ posts: results }) }) api.post( '/posts', auth, zValidator('json', z.object({ title: z.string().min(1).max(200), body: z.string().min(1), })), async (c) => { const { title, body } = c.req.valid('json') const userId = c.var.jwtPayload.sub const result = await c.env.DB .prepare('INSERT INTO posts (title, body, author_id) VALUES (?, ?, ?) RETURNING id') .bind(title, body, userId) .first<{ id: number }>() return c.json({ id: result?.id }, 201) } ) api.onError((err, c) => { console.error(err) return c.json({ error: 'Internal error' }, 500) }) export type AppType = typeof api export default app
React क्लाइंट इसे पूर्ण टाइप सेफ्टी के साथ कंज़्यूम करता है:
import { hc } from 'hono/client' import type { AppType } from '../api/src/index' const api = hc<AppType>(import.meta.env.VITE_API_URL) const res = await api.posts.$post({ json: { title: '2026 में Hono', body: '...' }, }, { headers: { Authorization: `Bearer ${token}` }, })
टेस्टिंग
import { describe, it, expect } from 'vitest' import app from '../src/index' describe('GET /api/posts', () => { it('पोस्ट्स की सूची लौटाता है', async () => { const res = await app.request('/api/posts') expect(res.status).toBe(200) const body = await res.json() expect(body.posts).toBeInstanceOf(Array) }) })
app.request() बिना HTTP सर्वर के रूट्स टेस्ट करने देता है।
बेस्ट प्रैक्टिसेज़
1. रूट डेफिनिशन्स को चेन करें
const app = new Hono() .get('/posts', handler1) .post('/posts', handler2) .get('/posts/:id', handler3)
चेनिंग RPC क्लाइंट के लिए जरूरी है। अलग लाइनों पर app.get(...) अनुमान तोड़ देता है।
2. इम्प्लीमेंटेशन नहीं, टाइप एक्सपोर्ट करें
क्लाइंट को AppType import करना चाहिए। import type फ्रंटएंड बिल्ड में बैकएंड कोड शामिल नहीं करता।
3. हर डोमेन पर एक राउटर
posts, users, webhooks के लिए अलग सब-ऐप। app.route() से कंपोज़ करें।
4. हमेशा बॉर्डर पर वैलिडेट करें
हर एक्सटर्नल इनपुट zValidator से होकर जाए।
5. ग्लोबल क्लाइंट्स पर नहीं, बाइंडिंग्स पर निर्भर करें
Cloudflare पर c.env से KV/D1/R2 तक पहुंचें।
6. राउटर ऑप्टिमाइज़ करने से पहले मापें
डिफ़ॉल्ट SmartRouter 95% मामलों के लिए ठीक है।
निष्कर्ष
Hono 2026 में TypeScript में एज-रेडी APIs बनाने का दे फ़ैक्टो स्टैंडर्ड बन गया है। वेब स्टैंडर्ड्स, परफॉर्मेंस, टाइप सेफ्टी और पोर्टेबिलिटी का संयोजन ट्रेडिशनल फ्रेमवर्क्स को रोकने वाली ठीक उन समस्याओं को हल करता है।
शुरुआती चेकलिस्ट:
npm create hono@latestऔर रनटाइम टेम्पलेट चुनें- चेनिंग से रूट्स डिफ़ाइन करें (
.get(...).post(...))logger,cors,secureHeadersग्लोबल मिडलवेयर के रूप में जोड़ें- हर इनपुट
@hono/zod-validatorसे वैलिडेट करेंAppTypeएक्सपोर्ट करें और टाइप-सेफhcक्लाइंट से API कंज़्यूम करेंapp.request()से टेस्ट लिखें — HTTP सर्वर की जरूरत नहींwrangler deploy(CF),vercel deployया आपके रनटाइम के बंडलर से डिप्लॉय करें