في عالم تطوير الويب سريع التطور، قد يكون العثور على الإطار المناسب أمرًا صعبًا. أنت تريد شيئًا سريعًا وخفيف الوزن ومرنًا بما يكفي للعمل عبر بيئات مختلفة. أدخل Hono - إطار عمل ويب يكتسب زخمًا سريعًا بين المطورين لسرعته المذهلة، وبصمته الدنيا، وتصميمه سهل الاستخدام للمطورين.
Hono (بمعنى "شعلة" 🔥 باليابانية) يحمل اسمه عن جدارة - فهو سريع للغاية ويضيء تجربة التطوير الخاصة بك ببساطته الأنيقة. سواء كنت تبني واجهات برمجة تطبيقات (APIs)، أو خدمات مصغرة (microservices)، أو تطبيقات كاملة (full-stack)، يقدم Hono بديلاً مقنعًا للأطر الأثقل.
سيرشدك هذا الدليل عبر كل ما تحتاج لمعرفته للبدء مع Hono.js، من التثبيت إلى بناء تطبيقك الأول وفهم مفاهيمه الأساسية.
هل تريد منصة متكاملة وشاملة لفريق المطورين الخاص بك للعمل معًا بأقصى إنتاجية؟
Apidog يلبي جميع متطلباتك، ويحل محل Postman بسعر أكثر ملاءمة بكثير!
ما هو Hono؟

Hono هو إطار عمل ويب صغير وبسيط وسريع للغاية مبني على معايير الويب (Web Standards). يعمل على أي بيئة تشغيل JavaScript تقريبًا: Cloudflare Workers، Fastly Compute، Deno، Bun، Vercel، Netlify، AWS Lambda، Lambda@Edge، و Node.js.
في جوهره، يحتضن Hono واجهات برمجة تطبيقات معايير الويب (مثل Request و Response و Fetch)، مما يمنحه قابلية نقل ملحوظة عبر المنصات. هذا يعني أنه يمكنك كتابة الكود الخاص بك مرة واحدة ونشره في أي مكان تقريبًا دون تعديلات كبيرة.
import { Hono } from 'hono'
const app = new Hono()
app.get('/', (c) => c.text('Hono!'))
export default app
يوضح هذا المثال البسيط بنية Hono النظيفة، المستوحاة من Express ولكن محدثة لتناسب نظام JavaScript البيئي الحالي.
دعنا نتعمق!
1. تثبيت Hono.js باستخدام Bun وإعداد المشروع
Bun هي بيئة تشغيل JavaScript حديثة معروفة بسرعتها ومجموعتها الشاملة من الأدوات، بما في ذلك مدير الحزم، والمجمع (bundler)، ومشغل الاختبارات. إعداد Hono باستخدام Bun أمر بسيط ومباشر.
المتطلبات الأساسية
تأكد من تثبيت Bun. إذا لم يكن كذلك، يمكنك تثبيته باتباع التعليمات على الموقع الرسمي لـ Bun.
إنشاء مشروع Hono جديد باستخدام Bun
أسرع طريقة لبدء مشروع Hono جديد باستخدام Bun هي استخدام أداة إنشاء الهيكل create-hono
:
bun create hono@latest my-hono-app
سيطلب منك هذا الأمر اختيار قالب. لهذا الدليل، اختر قالب bun
.
? Which template do you want to use? › - Use arrow keys. Return to submit.
❯ bun
cloudflare-workers
cloudflare-pages
deno
fastly
netlify
nodejs
vercel
بعد اختيار القالب، انتقل إلى دليل مشروعك الجديد وقم بتثبيت التبعيات:
cd my-hono-app
bun install
إضافة Hono إلى مشروع Bun موجود
إذا كان لديك مشروع Bun موجود، يمكنك إضافة Hono كتبعيات:
bun add hono
2. بناء تطبيق أساسي باستخدام Hono.js: "Hello Hono!"
دعنا ننشئ تطبيق "Hello World" بسيطًا (أو بالأحرى، "Hello Hono!").
داخل مشروعك، يجب أن يكون لديك دليل src
. قم بإنشاء أو فتح src/index.ts
(أو index.js
إذا كنت تفضل JavaScript العادي، على الرغم من أن TypeScript موصى به للحصول على فوائد Hono الكاملة).
// src/index.ts
import { Hono } from 'hono'
const app = new Hono()
app.get('/', (c) => {
return c.text('Hello Hono!')
})
export default app
دعنا نحلل هذا الكود:
- نقوم باستيراد الفئة
Hono
من حزمةhono
. - نقوم بإنشاء مثيل جديد من
Hono
، ويتم تسميته عادةًapp
. - نقوم بتعريف مسار لطلبات
GET
إلى المسار الجذر (/
). - معالج المسار هو دالة تأخذ كائن
Context
(يسمى عادةًc
) كوسيطة. c.text('Hello Hono!')
ينشئ كائن Response بنص عادي "Hello Hono!".- أخيرًا، نقوم بتصدير مثيل
app
. بالنسبة لـ Bun، هذا التصدير الافتراضي كافٍ لخادم التطوير لالتقاطه.
تشغيل تطبيقك
لتشغيل تطبيقك في وضع التطوير، استخدم السكريبت dev
المحدد في ملف package.json
الخاص بك (والذي تقوم أداة create-hono
بإعداده لك):
bun run dev
سيؤدي هذا عادةً إلى بدء تشغيل خادم على http://localhost:3000
. افتح هذا العنوان في متصفح الويب الخاص بك، ويجب أن ترى "Hello Hono!".
تغيير المنفذ
إذا كنت بحاجة إلى تشغيل تطبيقك على منفذ مختلف، يمكنك تعديل التصدير في src/index.ts
:
// src/index.ts
import { Hono } from 'hono'
const app = new Hono()
app.get('/', (c) => {
return c.text('Hello Hono on port 8080!')
})
export default {
port: 8080, // Specify your desired port
fetch: app.fetch,
}
الآن، عند تشغيل bun run dev
، سيتم تقديم التطبيق على http://localhost:8080
.
3. بناء واجهة برمجة تطبيقات (API) أساسية باستخدام Hono.js
يتفوق Hono في بناء واجهات برمجة التطبيقات (APIs). دعنا نستكشف التوجيه، وكيفية التعامل مع الطلبات وإنشاء الاستجابات.
التوجيه (Routing)
التوجيه في Hono بديهي. يحتوي مثيل app
على طرق تتوافق مع أفعال HTTP (.get()
، .post()
، .put()
، .delete()
، وما إلى ذلك).
مسار GET أساسي
لقد رأينا بالفعل مسار GET أساسيًا:
app.get('/hello', (c) => {
return c.text('Hello, world!')
})
معلمات المسار (Path Parameters)
يمكنك تعريف مسارات بمعلمات المسار باستخدام الصيغة :paramName
. يمكن الوصول إلى هذه المعلمات عبر c.req.param('paramName')
.
// Example: /users/123
app.get('/users/:id', (c) => {
const userId = c.req.param('id')
return c.text(`User ID: ${userId}`)
})
معلمات الاستعلام (Query Parameters)
يمكن الوصول إلى معلمات الاستعلام من عنوان URL (مثل /search?q=hono
) باستخدام c.req.query('queryName')
.
// Example: /search?category=frameworks&limit=10
app.get('/search', (c) => {
const category = c.req.query('category')
const limit = c.req.query('limit')
return c.text(`Searching in category: ${category}, Limit: ${limit}`)
})
يمكنك أيضًا الحصول على جميع معلمات الاستعلام ككائن باستخدام c.req.query()
.
التعامل مع طرق HTTP المختلفة
يجعل Hono من السهل التعامل مع طرق HTTP المختلفة لنفس المسار:
// src/index.ts
import { Hono } from 'hono'
const app = new Hono()
// GET a list of posts
app.get('/posts', (c) => {
// In a real app, you'd fetch this from a database
const posts = [
{ id: '1', title: 'Getting Started with Hono' },
{ id: '2', title: 'Advanced Hono Techniques' },
];
return c.json(posts); // Return JSON response
})
// GET a single post by ID
app.get('/posts/:id', (c) => {
const id = c.req.param('id')
// Fetch post by ID from a database
const post = { id: id, title: `Post ${id}`, content: 'This is the content...' };
if (!post) {
return c.notFound() // Built-in helper for 404
}
return c.json(post)
})
// CREATE a new post
app.post('/posts', async (c) => {
const body = await c.req.json() // Parse JSON body
// In a real app, you'd save this to a database
console.log('Creating post:', body)
return c.json({ message: 'Post created successfully!', data: body }, 201) // Respond with 201 Created
})
// UPDATE an existing post
app.put('/posts/:id', async (c) => {
const id = c.req.param('id')
const body = await c.req.json()
// Update post in database
console.log(`Updating post ${id}:`, body)
return c.json({ message: `Post ${id} updated successfully!`, data: body })
})
// DELETE a post
app.delete('/posts/:id', (c) => {
const id = c.req.param('id')
// Delete post from database
console.log(`Deleting post ${id}`)
return c.json({ message: `Post ${id} deleted successfully!` })
})
export default {
port: 3000,
fetch: app.fetch
}
كائن الطلب (c.req
)
يوفر كائن Context
(c
) الوصول إلى تفاصيل الطلب عبر c.req
. تشمل الخصائص والطرق الرئيسية:
c.req.url
: سلسلة عنوان URL الكاملة.c.req.method
: طريقة HTTP (مثل 'GET', 'POST').c.req.headers
: كائن Headers. الوصول إلى الرؤوس مثلc.req.header('Content-Type')
.c.req.param('name')
: الحصول على معلمة مسار.c.req.query('name')
: الحصول على معلمة استعلام.c.req.queries('name')
: الحصول على جميع القيم لمعلمة استعلام متكررة كصفيفة.c.req.json()
: يقوم بتحليل جسم الطلب كـ JSON. (يعيد Promise)c.req.text()
: يقرأ جسم الطلب كنص عادي. (يعيد Promise)c.req.arrayBuffer()
: يقرأ جسم الطلب كـ ArrayBuffer. (يعيد Promise)c.req.formData()
: يقوم بتحليل جسم الطلب كـ FormData. (يعيد Promise)c.req.valid('type')
: الوصول إلى البيانات التي تم التحقق من صحتها (تستخدم مع المدققين، سيتم تغطيتها لاحقًا).
كائن الاستجابة (c
) والمساعدات
يوفر كائن Context
(c
) أيضًا مساعدات مريحة لإنشاء كائنات Response
:
c.text(text, status?, headers?)
: يعيد استجابة نص عادي.c.json(object, status?, headers?)
: يعيد استجابة JSON. يتم تعيينContent-Type
تلقائيًا إلىapplication/json
.c.html(html, status?, headers?)
: يعيد استجابة HTML.c.redirect(location, status?)
: يعيد استجابة إعادة توجيه (الحالة الافتراضية 302).c.notFound()
: يعيد استجابة 404 Not Found.c.newResponse(body, status?, headers?)
: ينشئ كائنResponse
جديدًا. يمكن أن يكونbody
إماnull
،ReadableStream
،ArrayBuffer
،FormData
،URLSearchParams
، أوstring
.c.header(name, value)
: يضبط رأس استجابة.
مثال: تعيين رؤوس مخصصة
app.get('/custom-header', (c) => {
c.header('X-Powered-By', 'HonoJS-Flame')
c.header('Cache-Control', 'no-cache')
return c.text('Check the response headers!')
})
4. العمل مع البرمجيات الوسيطة (Middleware) في Hono.js
دوال البرمجيات الوسيطة هي حجر الزاوية في Hono (والعديد من أطر عمل الويب). إنها دوال يمكنها معالجة طلب قبل أن يصل إلى معالج المسار الرئيسي، أو معالجة استجابة بعد تشغيل المعالج. البرمجيات الوسيطة رائعة لمهام مثل التسجيل (logging)، المصادقة (authentication)، التحقق من صحة البيانات (data validation)، الضغط (compression)، CORS، والمزيد.
التوقيع الأساسي لدالة البرمجيات الوسيطة هو async (c, next) => { ... }
.
c
: كائن Context.next
: دالة يتم استدعاؤها لتمرير التحكم إلى البرمجية الوسيطة التالية في السلسلة، أو إلى معالج المسار النهائي. يجب عليك استدعاءawait next()
إذا كنت تريد تنفيذ البرمجيات الوسيطة أو المعالج اللاحق.
استخدام البرمجيات الوسيطة المضمنة
يأتي Hono مع مجموعة غنية من البرمجيات الوسيطة المضمنة. يمكنك استيرادها من hono/middleware-name
(مثل hono/logger
، hono/cors
).
لتطبيق البرمجيات الوسيطة على جميع المسارات، استخدم app.use(middlewareFunction)
.
لتطبيق البرمجيات الوسيطة على مسارات محددة، قدم نمط مسار كأول وسيطة: app.use('/admin/*', authMiddleware)
.
مثال: Logger و ETag Middleware
// src/index.ts
import { Hono } from 'hono'
import { logger } from 'hono/logger'
import { etag } from 'hono/etag'
import { prettyJSON } from 'hono/pretty-json' // For nicely formatted JSON responses
const app = new Hono()
// Apply middleware to all routes
app.use(logger()) // Logs request and response info to the console
app.use(etag()) // Adds ETag headers for caching
app.use(prettyJSON()) // Formats JSON responses with indentation
app.get('/', (c) => {
return c.text('Hello with Logger and ETag!')
})
app.get('/data', (c) => {
return c.json({ message: 'This is some data.', timestamp: Date.now() })
})
export default app
عند تشغيل هذا والوصول إلى /
أو /data
، سترى سجلات في وحدة التحكم الخاصة بك، وستتضمن الاستجابات رأس ETag
. سيتم تنسيق استجابات JSON من /data
بشكل جميل.
برمجيات وسيطة أخرى مفيدة ومضمنة:
cors
: يتعامل مع مشاركة الموارد عبر الأصول (Cross-Origin Resource Sharing).basicAuth
: يطبق المصادقة الأساسية.jwt
: مصادقة JWT (JSON Web Token).compress
: يضغط أجسام الاستجابة.cache
: يطبق التخزين المؤقت باستخدام Cache API.secureHeaders
: يضيف رؤوس HTTP مختلفة متعلقة بالأمان.bodyLimit
: يحدد حجم جسم الطلب.
يمكنك العثور على قائمة كاملة وتوثيق في مستندات Hono تحت عنوان "Middleware".
إنشاء برمجيات وسيطة مخصصة
يمكنك بسهولة كتابة البرمجيات الوسيطة الخاصة بك.
مثال 1: مؤقت طلب بسيط
// src/index.ts
import { Hono } from 'hono'
import { logger } from 'hono/logger'
const app = new Hono()
app.use(logger())
// Custom middleware to measure request processing time
app.use(async (c, next) => {
const start = Date.now()
console.log(`Request received at: ${new Date(start).toISOString()}`)
await next() // Call the next middleware or route handler
const ms = Date.now() - start
c.header('X-Response-Time', `${ms}ms`) // Add custom header to the response
console.log(`Request processed in ${ms}ms`)
})
app.get('/', (c) => {
return c.text('Hello from Hono with custom timing middleware!')
})
app.get('/slow', async (c) => {
// Simulate a slow operation
await new Promise(resolve => setTimeout(resolve, 1500))
return c.text('This was a slow response.')
})
export default app
الوصول إلى /
و /slow
لرؤية سجلات التوقيت ورأس X-Response-Time
.
مثال 2: برمجية وسيطة بسيطة لمصادقة مفتاح API
هذا مثال أساسي جدًا للتوضيح. في تطبيق حقيقي، استخدم طرق مصادقة أكثر قوة مثل JWT أو OAuth، وقم بتخزين مفاتيح API بشكل آمن.
// src/index.ts
import { Hono } from 'hono'
import { logger } from 'hono/logger'
import { HTTPException } from 'hono/http-exception' // For throwing standard HTTP errors
const app = new Hono()
app.use(logger())
const API_KEY = "supersecretapikey"; // In a real app, store this securely (e.g., env variable)
// Custom API Key Authentication Middleware
const apiKeyAuth = async (c, next) => {
const apiKeyHeader = c.req.header('X-API-KEY')
if (apiKeyHeader && apiKeyHeader === API_KEY) {
await next()
} else {
// Using HTTPException to return a standardized error response
throw new HTTPException(401, { message: 'Unauthorized: Invalid API Key' })
}
}
// Apply this middleware to a specific group of routes
app.use('/api/v1/*', apiKeyAuth)
app.get('/api/v1/me', (c) => {
return c.json({ user: 'Authenticated User', email: 'user@example.com' })
})
app.get('/public/info', (c) => {
return c.text('This is public information, no API key needed.')
})
// Error handler for HTTPException
app.onError((err, c) => {
if (err instanceof HTTPException) {
return err.getResponse()
}
// For other errors
console.error('Unhandled error:', err)
return c.text('Internal Server Error', 500)
})
export default app
لاختبار هذا:
curl http://localhost:3000/api/v1/me
(يجب أن يعيد 401 Unauthorized)curl -H "X-API-KEY: supersecretapikey" http://localhost:3000/api/v1/me
(يجب أن يعيد بيانات المستخدم)curl http://localhost:3000/public/info
(يجب أن يعيد معلومات عامة)
إعادة استخدام البرمجيات الوسيطة المخصصة مع createMiddleware
للببرمجيات الوسيطة الأكثر تعقيدًا أو القابلة لإعادة الاستخدام، يمكنك تعريفها بشكل منفصل باستخدام createMiddleware
من hono/factory
. يساعد هذا أيضًا في استنتاج أنواع TypeScript.
// src/middlewares/timing.ts
import { createMiddleware } from 'hono/factory'
export const timingMiddleware = createMiddleware(async (c, next) => {
const start = Date.now()
await next()
const ms = Date.now() - start
c.res.headers.set('X-Response-Time', `${ms}ms`) // Note: c.res.headers.set
console.log(` -> Processed in ${ms}ms`)
})
// src/middlewares/auth.ts
import { createMiddleware } from 'hono/factory'
import { HTTPException } from 'hono/http-exception'
const VALID_API_KEY = "anothersecretkey";
// Type for environment variables if your middleware needs them
type Env = {
Variables: {
user?: { id: string } // Example: set user data after auth
}
// Bindings: { MY_KV_NAMESPACE: KVNamespace } // Example for Cloudflare Workers
}
export const secureApiKeyAuth = createMiddleware<Env>(async (c, next) => {
const apiKey = c.req.header('Authorization')?.replace('Bearer ', '')
if (apiKey === VALID_API_KEY) {
// Optionally, you can set variables in the context for downstream handlers
c.set('user', { id: 'user123' })
await next()
} else {
throw new HTTPException(401, { message: 'Access Denied: Secure API Key Required.'})
}
})
// src/index.ts
import { Hono } from 'hono'
import { logger } from 'hono/logger'
import { timingMiddleware } from './middlewares/timing' // Assuming they are in a middlewares folder
import { secureApiKeyAuth } from './middlewares/auth'
const app = new Hono()
app.use(logger())
app.use(timingMiddleware) // Apply to all routes
app.get('/', (c) => {
return c.text('Hello from Hono with factored middleware!')
})
app.use('/secure/data/*', secureApiKeyAuth) // Apply only to /secure/data/*
app.get('/secure/data/profile', (c) => {
// const user = c.get('user') // Access variable set by middleware
return c.json({ profileData: 'Sensitive profile information', /*user: user*/ })
})
app.get('/public/data', (c) => {
return c.text('This is public data, no key needed.')
})
// General error handler
app.onError((err, c) => {
if (err instanceof HTTPException) {
return err.getResponse();
}
console.error('Error:', err.message);
return c.json({ error: 'Internal Server Error', message: err.message }, 500);
});
export default app
هذا الهيكل يجعل البرمجيات الوسيطة الخاصة بك أكثر وحداتية وأسهل للاختبار بشكل مستقل.
5. اختبار تطبيقات Hono.js الخاصة بك
تم تصميم Hono ليكون سهل الاختبار. يأتي Bun مع مشغل الاختبار الخاص به، bun:test
، والذي يعمل بشكل جيد مع Hono. يمكنك أيضًا استخدام مشغلات اختبار أخرى مثل Vitest.
استخدام bun:test
(أساسي)
توفر وثائق bun.md
مثالًا أساسيًا للاختبار باستخدام bun:test
:
أنشئ ملف اختبار، على سبيل المثال، src/index.test.ts
:
// src/index.test.ts
import { describe, expect, it } from 'bun:test'
import app from '.' // Imports your app from src/index.ts
describe('Hono Application Tests', () => {
it('Should return 200 OK for GET /', async () => {
const req = new Request('http://localhost/') // Base URL doesn't matter much here
const res = await app.fetch(req)
expect(res.status).toBe(200)
expect(await res.text()).toBe('Hello Hono!') // Or whatever your root route returns
})
it('Should return JSON for GET /posts', async () => {
const req = new Request('http://localhost/posts')
const res = await app.fetch(req)
expect(res.status).toBe(200)
const json = await res.json()
expect(json).toBeArray() // Assuming /posts returns an array
// Add more specific assertions about the JSON content if needed
})
it('Should create a post for POST /posts', async () => {
const postData = { title: 'Test Post', content: 'This is a test.' };
const req = new Request('http://localhost/posts', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(postData),
});
const res = await app.fetch(req);
expect(res.status).toBe(201); // Expect 201 Created
const jsonResponse = await res.json();
expect(jsonResponse.message).toBe('Post created successfully!');
expect(jsonResponse.data.title).toBe(postData.title);
});
})
لتشغيل اختباراتك:
bun test
أو، إذا كنت تريد تشغيل ملف محدد:
bun test src/index.test.ts
استخدام مساعد app.request()
يوفر Hono طريقة app.request()
مريحة تبسط الاختبار من خلال السماح لك بتمرير مسار وخيارات مباشرة، بدلاً من إنشاء كائن Request
كامل في كل مرة. يظهر هذا في docs/guides/testing.md
.
// src/index.test.ts
import { describe, expect, it } from 'bun:test' // Or import from 'vitest'
import app from '.' // Your Hono app instance
describe('Hono App with app.request()', () => {
it('GET / should return "Hello Hono!"', async () => {
const res = await app.request('/')
expect(res.status).toBe(200)
expect(await res.text()).toBe('Hello Hono!') // Adjust to your actual root response
})
it('GET /posts/:id should return a specific post', async () => {
// Assuming your app has a route like app.get('/posts/:id', ...)
// and for this test, it might return { id: '123', title: 'Test Post 123' }
const res = await app.request('/posts/123')
expect(res.status).toBe(200)
const data = await res.json()
expect(data.id).toBe('123')
// expect(data.title).toBe('Post 123') // Or based on your app's logic
})
it('POST /posts should create a new post', async () => {
const newPost = { title: 'My New Post', content: 'Awesome content here.' }
const res = await app.request('/posts', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(newPost),
})
expect(res.status).toBe(201) // Assuming your POST /posts returns 201
const responseData = await res.json()
expect(responseData.message).toBe('Post created successfully!')
expect(responseData.data.title).toBe(newPost.title)
})
it('GET /api/v1/me without API key should be 401', async () => {
// Assuming the API key middleware from earlier example is active on this route
const res = await app.request('/api/v1/me')
expect(res.status).toBe(401)
})
it('GET /api/v1/me with correct API key should be 200', async () => {
const res = await app.request('/api/v1/me', {
headers: {
'X-API-KEY': 'supersecretapikey' // Use the key from your middleware
}
})
expect(res.status).toBe(200)
const data = await res.json();
expect(data.user).toBe('Authenticated User')
})
})
استخدام مساعد testClient()
للاختبار الآمن للنوع (Type-Safe Testing)
للحصول على أمان نوع أفضل، خاصة إذا كنت تستخدم قدرات Hono RPC أو لديك مخططات مسار محددة جيدًا، يوفر Hono مساعد testClient
(من hono/testing
). هذا العميل مُحدد الأنواع بناءً على مسارات تطبيق Hono الخاص بك، مما يمنحك إكمالًا تلقائيًا وفحصًا للنوع في اختباراتك.
ملاحظة هامة لاستنتاج نوع testClient
:
لكي يقوم testClient
باستنتاج الأنواع بشكل صحيح، يجب عليك تعريف مساراتك باستخدام طرق متسلسلة مباشرة على مثيل Hono
(على سبيل المثال، const app = new Hono().get(...).post(...)
) أو تصدير نوع المسار إذا كنت تستخدم RPC. إذا قمت بتعريف المسارات بشكل منفصل (على سبيل المثال، const app = new Hono(); app.get(...)
)، فقد يكون استنتاج النوع لـ testClient
محدودًا.
// src/app-for-test-client.ts
// To demonstrate testClient, let's define an app with chained routes
import { Hono } from 'hono'
import { zValidator } from '@hono/zod-validator' // Example for typed query params
import { z } from 'zod'
const appWithChainedRoutes = new Hono()
.get('/search',
zValidator('query', z.object({ q: z.string(), limit: z.coerce.number().optional() })),
(c) => {
const { q, limit } = c.req.valid('query')
return c.json({ query: q, limit: limit || 10, results: [`result for ${q} 1`, `result for ${q} 2`] })
}
)
.post('/submit',
zValidator('json', z.object({ name: z.string(), email: z.string().email() })),
async (c) => {
const data = c.req.valid('json')
return c.json({ message: `Received data for ${data.name}`, data }, 201)
}
);
export default appWithChainedRoutes;
// src/app-for-test-client.test.ts
import { describe, expect, it } from 'bun:test'
import { testClient } from 'hono/testing'
import appWithChainedRoutes from './app-for-test-client' // Import the app
describe('Hono App with testClient', () => {
const client = testClient(appWithChainedRoutes)
it('GET /search should return typed results', async () => {
const res = await client.search.$get({
query: { q: 'hono rocks' }
})
expect(res.status).toBe(200)
const data = await res.json()
expect(data.query).toBe('hono rocks')
expect(data.results.length).toBeGreaterThan(0)
})
it('GET /search with limit should respect it', async () => {
const res = await client.search.$get({
query: { q: 'hono with limit', limit: '5' } // Query params are strings initially
})
expect(res.status).toBe(200)
const data = await res.json()
expect(data.limit).toBe(5) // Zod validator coerced it to number
})
it('POST /submit should accept valid data', async () => {
const payload = { name: 'Test User', email: 'test@example.com' }
// For POST, PUT, etc. with JSON body, it's client.path.$post({ json: payload })
const res = await client.submit.$post({
json: payload
})
expect(res.status).toBe(201)
const data = await res.json()
expect(data.message).toBe(`Received data for ${payload.name}`)
expect(data.data.email).toBe(payload.email)
});
it('POST /submit with headers', async () => {
const payload = { name: 'Test User Headers', email: 'testheaders@example.com' }
const res = await client.submit.$post({
json: payload
}, {
headers: {
'X-Custom-Test-Header': 'Testing123'
}
})
expect(res.status).toBe(201)
// You'd typically have a route/middleware that reads this header to verify
});
})
لتشغيل هذا الاختبار، بافتراض أنك قمت بتثبيت @hono/zod-validator
و zod
(bun add @hono/zod-validator zod
):
bun test src/app-for-test-client.test.ts
يوفر testClient
هذا تجربة تطوير أفضل بكثير للاختبار، خاصة لواجهات برمجة التطبيقات (APIs) ذات المخططات المحددة.
الخلاصة
يقدم Hono منهجًا جديدًا لتطوير الويب بمزيجه من السرعة، التصميم خفيف الوزن، والميزات الصديقة للمطورين. لقد غطى هذا الدليل الأساسيات لتبدأ، ولكن هناك الكثير لاستكشافه:
- تقنيات التوجيه المتقدمة
- أنماط البرمجيات الوسيطة الأكثر تعقيدًا
- عرض من جانب الخادم (Server-side rendering) باستخدام JSX
- التكامل مع قواعد البيانات
- واجهات برمجة تطبيقات بنمط RPC مع أمان النوع باستخدام Hono Client
- النشر على منصات مختلفة
بينما تواصل رحلتك مع Hono، ارجع إلى التوثيق الرسمي ومستودع الأمثلة للحصول على معلومات وإلهام أكثر تفصيلاً.
مشهد تطوير الويب يتطور باستمرار، ويمثل Hono منهجًا حديثًا يحتضن معايير الويب، ويعطي الأولوية للأداء، ويعمل عبر النظام البيئي المتنامي لبيئات تشغيل JavaScript. سواء كنت تبني واجهات برمجة تطبيقات بسيطة أو تطبيقات معقدة، يوفر Hono الأدوات التي تحتاجها دون عبء غير ضروري.
ابدأ صغيرًا، جرب، وشاهد تطبيقاتك تنمو مع Hono - إطار عمل الويب خفيف الوزن والسريع للغاية الذي يرقى إلى اسمه الناري. 🔥
هل تريد منصة متكاملة وشاملة لفريق المطورين الخاص بك للعمل معًا بأقصى إنتاجية؟
Apidog يلبي جميع متطلباتك، ويحل محل Postman بسعر أكثر ملاءمة بكثير!