اخبار وب

Hono: فریمورک وب سبک برای Edge

راهنمای Hono مبتنی بر Web Standards؛ محدودیت runtimeها، ترتیب middleware، اعتبارسنجی و خطا همراه نمونه کامل TypeScript برای اجرای روی Bun.

1404/12/27 2 دقیقه مطالعه 981 بازدید
Hono: فریمورک وب سبک برای Edge

Hono یک framework وب مبتنی بر Web Standards است که مسیریابی و middleware را برای چند Runtime ارائه می‌کند. این توصیف به معنی اجرای یکسان هر کد و هر وابستگی در همه محیط‌ها نیست. متن قبلی Hono را «سریع‌ترین» و «هشت برابر سریع‌تر از Express» معرفی کرده بود، بدون روش Benchmark و نسخه ابزارها. آن اعداد و رتبه‌بندی حذف شده‌اند. برای انتخاب Hono باید APIهای مقصد، نیاز middleware و هزینه نگهداری را بررسی کنیم و یک سرویس کوچک با قرارداد روشن بسازیم.

Web Standards چه کمکی می‌کند؟

Request و Response مدل‌های آشنای API وب هستند. Hono از این قراردادها برای دریافت درخواست و تولید پاسخ استفاده می‌کند. مستند رسمی Hono محیط‌های پشتیبانی‌شده و ویژگی‌های اصلی را معرفی می‌کند. وقتی framework بر این قرارداد تکیه دارد، بخشی از منطق پاسخ میان محیط‌ها قابل اشتراک‌تر می‌شود. با این حال، فایل‌سیستم، اتصال database و متغیر محیط در همه runtimeها رفتار برابر ندارند. بخش عمومی درخواست را از API اختصاصی محیط جدا نگه دارید.

برای نمونه، handler می‌تواند یک پاسخ JSON بسازد و نسبتاً مستقل بماند، ولی روش دریافت secret در Workers با یک سرور محلی فرق دارد. اگر داخل handler از API مخصوص Bun استفاده می‌کنید، همان قسمت به Bun وابسته است. نام framework این وابستگی را حذف نمی‌کند. مرز محیط را با adapter یا لایه مشخص ثبت کنید تا هنگام جابه‌جایی بدانید کدام فایل‌ها به تغییر نیاز دارند. این کار از ادعای مبهم «روی هر Runtime اجرا می‌شود» قابل استفاده‌تر است.

Edge هم مجموعه محدودیت خودش را دارد. زمان اجرا، حافظه و مسیر ارتباط با سرویس بیرونی به provider وابسته‌اند. API کوچک Hono ممکن است در Edge مناسب باشد، ولی کتابخانه همراهش به Node.js API نیاز داشته باشد. انتخاب dependency را با مقصد هماهنگ کنید. اگر برای یک کار ساده بسته سنگینی وارد می‌شود، اندازه و رفتار آن را بررسی کنید. سرعت framework فقط یک بخش از زمان پاسخ است و در انتظار شبکه یا Query دور می‌تواند سهم کمی داشته باشد.

Context و مسیرها را روشن نگه دارید

Hono برای هر درخواست context می‌دهد که خواندن ورودی و ساخت پاسخ را ساده می‌کند. مسیر، روش HTTP و handler باید قرارداد مشخصی داشته باشند. GET برای دریافت داده با POST برای ایجاد یا پردازش درخواست یکی نیست. اگر مسیر از پارامتر استفاده می‌کند، اعتبارسنجی آن را جدا بنویسید. دریافت مقدار از URL به معنی مجاز یا معتبر بودن آن نیست. نام واضح handler و شکل ثابت خطا به نگهداری کمک می‌کنند، به‌ویژه وقتی چند عضو تیم مسیرهای تازه اضافه می‌کنند.

برای پاسخ، وضعیت HTTP و داده را هماهنگ کنید. پاسخ موفق نباید فقط یک پیام خطا با وضعیت 200 باشد، مگر قرارداد خاص و مستندی داشته باشید. client باید بتواند میان داده ناموجود، ورودی نامعتبر و خطای داخلی فرق بگذارد. پیغام داخلی خطا را بی‌دلیل به کاربر ندهید، چون ممکن است ساختار یا داده حساس را آشکار کند. یک شناسه پیگیری و ثبت رخداد مناسب می‌تواند بررسی داخلی را ممکن کند، بدون اینکه جزئیات همه exceptionها به پاسخ عمومی منتقل شوند.

نمونه کامل روی Bun

نمونه زیر عمداً برای Bun نوشته شده است. به نصب Bun و package هونوی Hono نیاز دارد. در پوشه تازه فرمان bun init -y را اجرا کنید و سپس bun add hono را وارد کنید. فایل index.ts را با کد زیر بسازید و با bun run index.ts اجرا کنید. نشانی http://localhost:3000/ پاسخ معرفی را می‌دهد و مسیر http://localhost:3000/api/hello?name=Reza یک پیام JSON برمی‌گرداند. فایل به تصویر، database یا API بیرونی وابسته نیست و نمونه تولید محسوب نمی‌شود.

import { Hono } from 'hono';
import { logger } from 'hono/logger';

const app = new Hono();
app.use('*', logger());
app.get('/', (c) => c.json({ message: 'Hono API' }));
app.get('/api/hello', (c) => {
  const name = c.req.query('name') ?? 'Guest';
  if (name.length < 1 || name.length > 80) {
    return c.json({ error: 'name must contain 1 to 80 characters' }, 400);
  }
  return c.json({ message: `Hello, ${name}` });
});
app.notFound((c) => c.json({ error: 'Not found' }, 404));
app.onError((error, c) => {
  console.error(error.name);
  return c.json({ error: 'Internal server error' }, 500);
});
const port = Number(process.env.PORT ?? 3000);
if (!Number.isInteger(port) || port < 1 || port > 65535) {
  throw new Error('PORT must be an integer between 1 and 65535');
}
Bun.serve({ port, fetch: app.fetch });
console.log(`Listening at http://localhost:${port}`);

در این فایل، logger پیش از مسیرها ثبت می‌شود تا درخواست‌ها از آن عبور کنند. مسیر hello طول name را کنترل می‌کند و در نبود نام از مقدار پیش‌فرض استفاده می‌کند. نمونه فقط GET دارد و مسیر ناشناخته پاسخ 404 می‌دهد. مقدار PORT اختیاری است و باید عدد معتبر باشد. برای استقرار Node.js، این فایل را عیناً کپی نکنید؛ Bun.serve API اختصاصی است و adapter مربوط به Node.js لازم می‌شود. راهنمای رسمی Bun این مقصد اجرا را توضیح می‌دهد.

نمونه name را در JSON برمی‌گرداند و آن را داخل HTML خام قرار نمی‌دهد. اگر بعداً پاسخ HTML ساختید، escaping داده ورودی را جدی بگیرید. قرارداد نمونه فقط یک greeting است؛ احراز هویت یا مجوز داده ندارد. برای سرویس واقعی، نیازها را یکی‌یکی اضافه کنید و رفتار خطا را مشخص نگه دارید. مقاله الگوهای طراحی API به طراحی همین قرارداد کمک می‌کند. framework قرار نیست تصمیم محصول درباره مجوز و داده را از روی نام مسیر حدس بزند.

Middleware ترتیب اجرا دارد

راهنمای middleware در Hono مسیر اجرای قبل و بعد از next را توضیح می‌دهد. Middleware می‌تواند درخواست را بررسی کند، داده‌ای به context بدهد یا پاسخ را تغییر دهد. ترتیب ثبت مهم است، چون یک مرحله ممکن است به نتیجه مرحله قبلی وابسته باشد. اگر احراز هویت پیش از ثبت context لازم است، این رابطه را در کد روشن نگه دارید. اضافه‌کردن middlewareهای متعدد بدون فهم ترتیب، عیب‌یابی خطاهای پاسخ را دشوار می‌کند.

Middleware داخلی، سفارشی و ثالث را از هم جدا بخوانید. وجود یک قابلیت در اکوسیستم به معنی حضور آن در هسته نیست. Rate Limiting به محل ذخیره وضعیت و محیط اجرا وابسته است؛ یک شمارنده در حافظه فرایند میان چند نمونه مستقل مشترک نیست. اگر provider چند invocation ایجاد می‌کند، محدودیت مبتنی بر همان حافظه ممکن است رفتار مورد انتظار را ندهد. راهکار باید با معماری توزیع و قرارداد محصول هماهنگ باشد، نه فقط با یک نام plugin در فهرست ابزارها.

CORS هم سازوکار مجوز کاربر نیست. تنظیم Origin تعیین می‌کند مرورگر چه پاسخ‌هایی را در اختیار کد صفحه بگذارد، اما سرور باید مستقل احراز هویت و مجوز را کنترل کند. اجازه‌دادن به همه Originها برای داده عمومی ممکن است شرایط متفاوتی از endpoint حساب کاربر داشته باشد. تنظیم را با نیاز واقعی انتخاب کنید. کپی یک گزینه عمومی از نمونه آموزشی به API حساس می‌تواند مرزها را مبهم کند. امنیت از قرارداد کامل درخواست می‌آید و به یک middleware محدود نمی‌شود.

اعتبارسنجی و TypeScript

TypeScript به قرارداد زمان توسعه کمک می‌کند، اما داده شبکه باید در زمان اجرا بررسی شود. یک type برای body تضمین نمی‌کند client همان شکل را فرستاده باشد. فیلدهای لازم، طول، عدد مجاز و رفتار داده اضافه را تعریف کنید. Hono می‌تواند با ابزار اعتبارسنجی مورد انتخاب تیم ترکیب شود؛ انتخاب package را با مقصد اجرا و روش مدیریت خطا هماهنگ کنید. برای مثال ساده ممکن است بررسی دستی کافی باشد، ولی قرارداد پیچیده به ساختار مشترک نیاز دارد.

اگر نوع داده پاسخ میان client و server به اشتراک گذاشته می‌شود، مراقب انتقال dependency مخصوص server به bundle مرورگر باشید. اشتراک type باید با مرز اجرا سازگار باشد. مقاله TypeScript با React درباره قراردادهای component و داده ادامه مرتبطی دارد. داشتن تکمیل خودکار خوب، صحت همه مسیرهای runtime را ثابت نمی‌کند. Typeها و بررسی ورودی دو لایه متفاوت‌اند که هر دو می‌توانند برای پروژه لازم باشند.

خطا، لاگ و عملیات تولید

onError محل تعریف پاسخ عمومی خطاست، اما لاگ داخلی باید اطلاعات لازم برای پیگیری داشته باشد. داده حساس، توکن و body کامل را صرفاً برای راحتی چاپ نکنید. مسیر، شناسه درخواست و نوع خطا معمولاً از حجم زیاد داده بی‌هدف مفیدترند. مدت نگهداری و دسترسی به لاگ را مشخص کنید. مثال logger این مقاله برای مشاهده درخواست آموزشی است و سیاست کامل logging تولید را تعریف نمی‌کند. هنگام افزودن ابزار پایش، هزینه و نوع داده ارسالی را هم بررسی کنید.

محدودیت body باید با Runtime هم هماهنگ باشد. راهنمای Body Limit درباره تعامل محدودیت middleware با بعضی محیط‌ها توضیح دارد. اگر Runtime پیش از Hono درخواست را رد کند، handler خطای شما ممکن است همان مسیر را نبیند. این نکته نشان می‌دهد پاسخ‌های عمومی و وضعیت‌ها در چند لایه ساخته می‌شوند. برای گزارش مشکل، محل ردشدن درخواست را مشخص کنید و همه رفتارها را به framework نسبت ندهید.

مقایسه عملکرد را به workload وصل کنید

عدد سرعت برای پاسخ ثابت، تجربه API متصل به database را نمایندگی نمی‌کند. روش routing، Runtime، تعداد اتصال و اندازه پاسخ باید در مقایسه برابر باشند. اگر یک گزارش نسخه و کد ندارد، آن را مبنای قول عملکرد به مشتری نگذارید. زمان شروع فرایند و زمان پاسخ پایدار هم معیارهای جدا هستند. برای پروژه کوچک، خوانایی کد و سازگاری وابستگی ممکن است از اختلاف جزئی یک benchmark مهم‌تر باشد. انتخاب باید نشان دهد کدام محدودیت پروژه را حل می‌کند.

برای شروع، یک مسیر مستقل بسازید و قرارداد ورودی، خروجی و خطای آن را بنویسید. مقصد اجرا را مشخص کنید و dependencyها را با همان مقصد تطبیق دهید. سپس مسیر عملیات، secret و logging را اضافه کنید. مقاله راهنمای Bun به تفکیک Runtime و ابزارهای همراه کمک می‌کند. Hono می‌تواند ساختار ساده‌ای برای چنین API بدهد؛ مناسب‌بودن آن از این قراردادها و توان نگهداری تیم روشن می‌شود، نه از عنوان «بهترین framework».

واکنش به این مقاله

اشتراک‌گذاری این مقاله:
دیدگاه‌ها 0
۰ / ۲۰۰۰

هنوز دیدگاهی ثبت نشده

اولین نفری باشید که نظر می‌دهد!