تخطَّ إلى المحتوى

Hono

إطار عمل ويب فائق السرعة للحافة، يدعم Cloudflare Workers وDeno وBun وVercel وAWS Lambda وNode.js بدون أي تبعيات.

أمروصف
npm create hono@latest my-appإنشاء مشروع Hono جديد باستخدام npm
yarn create hono my-appالإنشاء باستخدام Yarn
pnpm create hono my-appالإنشاء باستخدام pnpm
bun create hono my-appالإنشاء باستخدام Bun
deno init --lib honoالإنشاء باستخدام Deno

الإضافة إلى مشروع موجود

Section titled “الإضافة إلى مشروع موجود”
أمروصف
npm install honoتثبيت Hono في مشروع موجود
yarn add honoالتثبيت باستخدام Yarn
pnpm add honoالتثبيت باستخدام pnpm
bun add honoالتثبيت باستخدام Bun
# Create project with specific runtime template
npm create hono@latest my-app -- --template cloudflare-workers
npm create hono@latest my-app -- --template bun
npm create hono@latest my-app -- --template deno
npm create hono@latest my-app -- --template nodejs
npm create hono@latest my-app -- --template vercel
npm create hono@latest my-app -- --template aws-lambda
npm create hono@latest my-app -- --template cloudflare-pages
npm create hono@latest my-app -- --template fastly
أمروصف
app.get('/path', handler)معالجة طلبات GET
app.post('/path', handler)معالجة طلبات POST
app.put('/path', handler)معالجة طلبات PUT
app.delete('/path', handler)معالجة طلبات DELETE
app.patch('/path', handler)معالجة طلبات PATCH
app.options('/path', handler)معالجة طلبات OPTIONS
app.all('/path', handler)معالجة جميع طرق HTTP
app.on('PURGE', '/path', handler)معالجة طرق مخصصة
import { Hono } from 'hono'

const app = new Hono()

app.get('/', (c) => {
  return c.text('Hello Hono!')
})

app.get('/json', (c) => {
  return c.json({ message: 'Hello', status: 'ok' })
})

app.get('/html', (c) => {
  return c.html('<h1>Hello Hono!</h1>')
})

export default app
// Path parameters
app.get('/users/:id', (c) => {
  const id = c.req.param('id')
  return c.json({ id })
})

// Multiple parameters
app.get('/posts/:postId/comments/:commentId', (c) => {
  const { postId, commentId } = c.req.param()
  return c.json({ postId, commentId })
})

// Optional parameter
app.get('/api/:version?/users', (c) => {
  const version = c.req.param('version') || 'v1'
  return c.json({ version })
})

// Wildcard
app.get('/files/*', (c) => {
  const path = c.req.path
  return c.text(`File path: ${path}`)
})

// Regex-like patterns
app.get('/user/:id{[0-9]+}', (c) => {
  const id = c.req.param('id')
  return c.json({ id: Number(id) })
})
أمروصف
c.req.query('key')الحصول على معامل استعلام واحد
c.req.query()الحصول على جميع معاملات الاستعلام
c.req.queries('tags')الحصول على مصفوفة قيم لمفتاح معين
أمروصف
c.req.json()تحليل جسم JSON
c.req.text()الحصول على الجسم كنص
c.req.formData()تحليل بيانات النموذج
c.req.blob()الحصول على الجسم كـ Blob
c.req.arrayBuffer()الحصول على الجسم كـ ArrayBuffer
c.req.parseBody()تحليل تلقائي للجسم حسب نوع المحتوى

الترويسات والبيانات الوصفية

Section titled “الترويسات والبيانات الوصفية”
أمروصف
c.req.header('Content-Type')الحصول على ترويسة الطلب
c.req.header()الحصول على جميع الترويسات
c.req.methodالحصول على طريقة HTTP
c.req.urlالحصول على عنوان URL الكامل للطلب
c.req.pathالحصول على مسار الطلب
c.req.rawالحصول على كائن Request الخام
c.req.valid('json')الحصول على البيانات المُتحقق منها
app.post('/users', async (c) => {
  const body = await c.req.json()
  const page = c.req.query('page') || '1'
  const auth = c.req.header('Authorization')

  return c.json({
    user: body,
    page: Number(page),
    authenticated: !!auth
  }, 201)
})
أمروصف
c.text('Hello')استجابة نص عادي
c.json({ key: 'value' })استجابة JSON
c.html('<h1>Hi</h1>')استجابة HTML
c.redirect('/new-url')إعادة توجيه 302
c.redirect('/new-url', 301)إعادة توجيه دائمة 301
c.notFound()استجابة 404
c.body(data)استجابة جسم خام
c.newResponse(body, status, headers)استجابة مخصصة كاملة

ترويسات الاستجابة والبث

Section titled “ترويسات الاستجابة والبث”
// Set headers
app.get('/api/data', (c) => {
  c.header('X-Custom-Header', 'value')
  c.header('Cache-Control', 'max-age=3600')
  return c.json({ data: 'example' })
})

// Set status code
app.post('/items', async (c) => {
  const item = await c.req.json()
  return c.json(item, 201)
})

// Stream response
app.get('/stream', (c) => {
  return c.streamText(async (stream) => {
    await stream.write('Hello ')
    await stream.sleep(1000)
    await stream.write('World!')
  })
})

البرمجيات الوسيطة المدمجة

Section titled “البرمجيات الوسيطة المدمجة”
أمروصف
import { cors } from 'hono/cors'برمجية وسيطة لـ CORS
import { logger } from 'hono/logger'تسجيل الطلبات
import { prettyJSON } from 'hono/pretty-json'تنسيق استجابات JSON
import { basicAuth } from 'hono/basic-auth'المصادقة الأساسية
import { bearerAuth } from 'hono/bearer-auth'مصادقة رمز Bearer
import { jwt } from 'hono/jwt'مصادقة JWT
import { compress } from 'hono/compress'ضغط Gzip/Brotli
import { etag } from 'hono/etag'تخزين ETag المؤقت
import { secureHeaders } from 'hono/secure-headers'ترويسات الأمان
import { csrf } from 'hono/csrf'حماية CSRF
import { timing } from 'hono/timing'ترويسة Server-Timing
import { cache } from 'hono/cache'التحكم بالتخزين المؤقت
import { bodyLimit } from 'hono/body-limit'تحديد حجم جسم الطلب

استخدام البرمجيات الوسيطة

Section titled “استخدام البرمجيات الوسيطة”
import { Hono } from 'hono'
import { cors } from 'hono/cors'
import { logger } from 'hono/logger'
import { secureHeaders } from 'hono/secure-headers'
import { bearerAuth } from 'hono/bearer-auth'

const app = new Hono()

// Global middleware
app.use('*', logger())
app.use('*', secureHeaders())
app.use('*', cors({
  origin: ['https://example.com'],
  allowMethods: ['GET', 'POST', 'PUT', 'DELETE'],
  allowHeaders: ['Content-Type', 'Authorization'],
  maxAge: 86400,
}))

// Route-specific middleware
app.use('/api/*', bearerAuth({ token: 'my-secret-token' }))

// Custom middleware
app.use('*', async (c, next) => {
  const start = Date.now()
  await next()
  const duration = Date.now() - start
  c.header('X-Response-Time', `${duration}ms`)
})
import { jwt, sign } from 'hono/jwt'

const app = new Hono()
const SECRET = 'my-secret-key'

app.use('/api/*', jwt({ secret: SECRET }))

app.post('/login', async (c) => {
  const { email, password } = await c.req.json()
  const token = await sign(
    { email, exp: Math.floor(Date.now() / 1000) + 3600 },
    SECRET
  )
  return c.json({ token })
})

app.get('/api/profile', (c) => {
  const payload = c.get('jwtPayload')
  return c.json({ email: payload.email })
})
import { zValidator } from '@hono/zod-validator'
import { z } from 'zod'

const userSchema = z.object({
  name: z.string().min(1),
  email: z.string().email(),
  age: z.number().int().positive().optional(),
})

app.post(
  '/users',
  zValidator('json', userSchema),
  (c) => {
    const user = c.req.valid('json')
    return c.json(user, 201)
  }
)

// Validate query parameters
const querySchema = z.object({
  page: z.string().optional().default('1'),
  limit: z.string().optional().default('10'),
})

app.get(
  '/users',
  zValidator('query', querySchema),
  (c) => {
    const { page, limit } = c.req.valid('query')
    return c.json({ page, limit })
  }
)
const app = new Hono()

// Group routes with basePath
const api = new Hono().basePath('/api')
api.get('/users', (c) => c.json({ users: [] }))
api.post('/users', (c) => c.json({ created: true }))
app.route('/', api)

// Versioned APIs
const v1 = new Hono()
v1.get('/health', (c) => c.json({ status: 'ok' }))

const v2 = new Hono()
v2.get('/health', (c) => c.json({ status: 'ok', version: 2 }))

app.route('/api/v1', v1)
app.route('/api/v2', v2)
import { HTTPException } from 'hono/http-exception'

// Custom 404
app.notFound((c) => {
  return c.json({ error: 'Not Found' }, 404)
})

// Global error handler
app.onError((err, c) => {
  if (err instanceof HTTPException) {
    return err.getResponse()
  }
  console.error(err)
  return c.json({ error: 'Internal Server Error' }, 500)
})

// Throw HTTP exceptions
app.get('/admin', (c) => {
  if (!c.req.header('Authorization')) {
    throw new HTTPException(401, { message: 'Unauthorized' })
  }
  return c.json({ admin: true })
})

الإعداد حسب بيئة التشغيل

Section titled “الإعداد حسب بيئة التشغيل”
import { Hono } from 'hono'

type Bindings = {
  MY_KV: KVNamespace
  MY_DB: D1Database
}

const app = new Hono<{ Bindings: Bindings }>()

app.get('/kv/:key', async (c) => {
  const value = await c.env.MY_KV.get(c.req.param('key'))
  return c.json({ value })
})

export default app
import { Hono } from 'hono'
import { serve } from '@hono/node-server'

const app = new Hono()
app.get('/', (c) => c.text('Hello Node.js!'))

serve({ fetch: app.fetch, port: 3000 })
import { Hono } from 'hono'

const app = new Hono()
app.get('/', (c) => c.text('Hello Bun!'))

export default { port: 3000, fetch: app.fetch }
import { Hono } from 'npm:hono'

const app = new Hono()
app.get('/', (c) => c.text('Hello Deno!'))

Deno.serve(app.fetch)
// server.ts
import { Hono } from 'hono'
import { zValidator } from '@hono/zod-validator'
import { z } from 'zod'

const app = new Hono()
  .get('/api/users', (c) => {
    return c.json({ users: [{ id: 1, name: 'Alice' }] })
  })
  .post(
    '/api/users',
    zValidator('json', z.object({ name: z.string() })),
    (c) => {
      const { name } = c.req.valid('json')
      return c.json({ id: 2, name }, 201)
    }
  )

export type AppType = typeof app
export default app
// client.ts — fully typed
import { hc } from 'hono/client'
import type { AppType } from './server'

const client = hc<AppType>('http://localhost:3000')

// Fully typed — autocomplete for routes + responses
const res = await client.api.users.$get()
const data = await res.json()
// data: { users: { id: number, name: string }[] }
import { testClient } from 'hono/testing'

const app = new Hono()
  .get('/hello', (c) => c.json({ message: 'Hello!' }))

// Using testClient (type-safe)
const client = testClient(app)
const res = await client.hello.$get()
const body = await res.json()
// body.message === 'Hello!'

// Using app.request() directly
const res2 = await app.request('/hello')
// res2.status === 200
  1. استخدم TypeScript generics لربط البيئة — عرّف Hono<{ Bindings: MyBindings }> للحصول على أمان النوع لمتغيرات البيئة وفضاءات أسماء KV وقواعد بيانات D1.

  2. تحقق من جميع المدخلات باستخدام Zod — استخدم @hono/zod-validator للتحقق من جسم الطلب والاستعلام والمعاملات. هذا يوفر أمان وقت التشغيل وأنواع TypeScript في خطوة واحدة.

  3. استخدم عميل RPC للواجهة الأمامية والخلفية — صدّر AppType من خادمك واستخدم hc<AppType>() في العميل لأمان نوع شامل بدون توليد كود.

  4. طبّق البرمجيات الوسيطة بأنماط المسارات — استخدم app.use('/api/*', middleware) لتحديد نطاق البرمجيات الوسيطة لمسارات محددة بدلاً من تطبيق كل شيء عالمياً.

  5. جمّع المسارات باستخدام new Hono().basePath() — نظّم نقاط النهاية المرتبطة في نسخ Hono منفصلة وركّبها بـ app.route() لفصل نظيف.

  6. عالج الأخطاء عالمياً — استخدم app.onError() و app.notFound() لضمان أن كل خطأ يُرجع استجابة JSON منظمة بدلاً من تتبع المكدس.

  7. استخدم c.executionCtx.waitUntil() — للعمل في الخلفية على Cloudflare Workers (التحليلات، التسجيل)، استخدم waitUntil() لتجنب حجب الاستجابة.

  8. اجعل المعالجات خفيفة — استخرج منطق الأعمال إلى دوال/وحدات منفصلة. معالجات المسارات يجب أن تحلل المدخلات وتستدعي المنطق وتُرجع الاستجابات.

  9. اختبر باستخدام testClient — عميل اختبار Hono المدمج آمن النوع ولا يحتاج خادماً يعمل، مما يجعل اختبارات الوحدة سريعة وموثوقة.

  10. انشر حيث يتواجد مستخدموك — يعمل Hono على كل بيئة تشغيل حافة رئيسية. اختر Cloudflare Workers للحافة العالمية، أو Bun للسرعة القصوى، أو Node.js لتوافق النظام البيئي.