REST API چیست؟ آموزش کامل طراحی و ساخت RESTful API با مثال عملی
REST API یکی از رایجترین روشهای ارتباط میان نرمافزارهاست. در این راهنمای عملی، اصول RESTful API، طراحی Endpoint، متدهای HTTP، مدیریت خطا، صفحهبندی و ساخت یک API واقعی متصل به درواره را یاد میگیرید.
اگر یک وبسایت، اپلیکیشن موبایل، پنل مدیریتی، فروشگاه اینترنتی یا سرویس هوش مصنوعی ساخته باشید، احتمالاً با عبارتهایی مانند REST API، RESTful API، Endpoint، Request، Response و HTTP Method مواجه شدهاید.
REST API یکی از رایجترین روشهای برقراری ارتباط میان نرمافزارهاست. فرانتاند یک درخواست برای سرور میفرستد، سرور درخواست را پردازش میکند و نتیجه را معمولاً در قالب JSON برمیگرداند.
اما REST فقط به معنی «ارسال JSON روی HTTP» نیست. REST یک سبک معماری برای طراحی سیستمهای توزیعشده است و مجموعهای از اصول را پیشنهاد میکند که رعایت آنها باعث میشود API قابلفهمتر، مقیاسپذیرتر و نگهداری آن سادهتر شود.
در این مقاله، REST API را از مفاهیم پایه تا طراحی حرفهای بررسی میکنیم و در پایان یک API واقعی با پایتون و FastAPI میسازیم که برای پردازش متن به API هوش مصنوعی درواره متصل میشود.
REST API چیست؟
REST مخفف Representational State Transfer است. این مفهوم در رساله دکتری Roy Fielding بهعنوان یک سبک معماری برای سیستمهای شبکهای معرفی شد.
در معماری REST، دادهها و قابلیتهای سیستم به شکل «منبع» یا Resource مدلسازی میشوند. هر منبع یک آدرس مشخص دارد و کلاینت با استفاده از متدهای استاندارد HTTP با آن تعامل میکند.
برای مثال، در یک فروشگاه اینترنتی میتوانیم منابع زیر را داشته باشیم:
/users
/products
/orders
/categories
یک محصول مشخص نیز ممکن است با چنین آدرسی در دسترس باشد:
/products/42
درخواست زیر فهرست محصولات را دریافت میکند:
GET /products
درخواست زیر محصول شماره ۴۲ را دریافت میکند:
GET /products/42
درخواست زیر محصول جدیدی ایجاد میکند:
POST /products
نکته مهم این است که REST یک پروتکل مستقل، زبان برنامهنویسی یا فرمت فایل نیست. REST یک سبک معماری است که معمولاً روی HTTP پیادهسازی میشود.
تفاوت API، Web API، HTTP API و REST API
این اصطلاحات به یکدیگر نزدیکاند، اما دقیقاً یک معنی ندارند.
| اصطلاح | تعریف |
|---|---|
| API | رابطی برای ارتباط میان دو بخش نرمافزاری |
| Web API | یک API که از طریق شبکه و فناوریهای وب در دسترس است |
| HTTP API | یک API که از پروتکل HTTP استفاده میکند |
| REST API | یک HTTP API که تا حد قابلقبولی از اصول معماری REST پیروی میکند |
| RESTful API | اصطلاحی برای API طراحیشده بر اساس اصول REST |
هر REST API معمولاً یک HTTP API است، اما هر HTTP API الزاماً RESTful نیست.
برای مثال، این Endpoint از HTTP استفاده میکند:
POST /executeDeleteUser
اما طراحی آن Resource-oriented نیست و عمل حذف را هم در نام مسیر و هم در متد درخواست بیان میکند. شکل RESTfulتر آن چنین است:
DELETE /users/42
Resource یا منبع چیست؟
Resource یکی از مهمترین مفاهیم REST است. منبع چیزی است که API آن را مدیریت یا نمایش میدهد.
نمونههایی از Resource عبارتاند از:
- کاربر
- محصول
- سفارش
- مقاله
- پیام
- فایل
- گزارش
- گفتوگوی هوش مصنوعی
هر منبع میتواند یک یا چند نمایش یا Representation داشته باشد. JSON رایجترین قالب نمایش منابع در APIهای امروزی است، اما REST استفاده از JSON را اجباری نمیکند.
یک نمایش JSON از یک محصول ممکن است چنین باشد:
{
"id": "prd_42",
"name": "کیبورد مکانیکی",
"price": 3500000,
"currency": "IRR",
"available": true
}
این JSON خود محصول نیست؛ بلکه نمایش دادهای آن محصول برای انتقال میان سرور و کلاینت است.
شش محدودیت اصلی معماری REST
برای آنکه یک معماری واقعاً به REST نزدیک باشد، باید محدودیتهای اصلی آن را بشناسیم.
۱. جداسازی Client و Server
کلاینت و سرور مسئولیتهای جداگانه دارند.
کلاینت معمولاً رابط کاربری و تجربه کاربر را مدیریت میکند. سرور مسئول دادهها، قوانین کسبوکار، احراز هویت و پردازش درخواستهاست.
این جداسازی اجازه میدهد فرانتاند و بکاند مستقل از یکدیگر توسعه پیدا کنند.
برای مثال، یک REST API میتواند همزمان توسط موارد زیر استفاده شود:
- وبسایت React
- اپلیکیشن موبایل
- پنل مدیریت
- افزونه وردپرس
- ابزار خط فرمان
- سرویس خودکارسازی
۲. Stateless یا بدون وضعیت
هر درخواست باید اطلاعات لازم برای پردازش خود را همراه داشته باشد. سرور نباید برای فهمیدن درخواست فعلی به وضعیت موقت درخواست قبلی وابسته باشد.
برای مثال، اگر API از توکن دسترسی استفاده میکند، کلاینت باید آن را در هر درخواست ارسال کند:
Authorization: Bearer YOUR_ACCESS_TOKEN
Stateless بودن به این معنی نیست که سرور نمیتواند اطلاعات کاربران را در پایگاه داده ذخیره کند. منظور این است که درخواست مستقل از یک Session موقت روی یک سرور مشخص قابل پردازش باشد.
این ویژگی توزیع درخواستها میان چند سرور را سادهتر میکند.
۳. Cacheable یا قابلیت کش شدن
پاسخ باید مشخص کند آیا قابلیت Cache شدن دارد یا خیر.
کش مناسب میتواند:
- تعداد درخواستهای تکراری را کاهش دهد
- سرعت پاسخگویی را افزایش دهد
- مصرف پهنای باند را کم کند
- بار سرور را کاهش دهد
هدرهایی مانند Cache-Control، ETag و Last-Modified برای کنترل Cache استفاده میشوند.
نمونه:
Cache-Control: public, max-age=300
این هدر اعلام میکند پاسخ میتواند تا ۳۰۰ ثانیه کش شود.
پاسخهای حاوی اطلاعات حساس یا شخصی نباید بدون سیاست مشخص Cache شوند.
۴. Uniform Interface یا رابط یکنواخت
رابط API باید قواعدی یکپارچه و قابلپیشبینی داشته باشد.
برای مثال:
- منابع با URL مشخص میشوند
- عملیات با متدهای HTTP بیان میشوند
- پاسخها ساختار ثابت دارند
- کدهای وضعیت HTTP بهدرستی استفاده میشوند
- ارتباط میان منابع قابل فهم است
یک API یکنواخت باعث میشود توسعهدهنده مجبور نباشد رفتار هر Endpoint را از ابتدا حدس بزند.
۵. Layered System یا معماری لایهای
کلاینت لازم نیست بداند مستقیماً با سرور اصلی ارتباط دارد یا درخواست از لایههای دیگری عبور میکند.
این لایهها میتوانند شامل موارد زیر باشند:
- Load Balancer
- Reverse Proxy
- API Gateway
- CDN
- سیستم Cache
- سرویس احراز هویت
- سرویس مانیتورینگ
این ویژگی برای توسعه و مقیاسپذیری سامانههای بزرگ اهمیت زیادی دارد.
۶. Code on Demand
سرور در صورت نیاز میتواند کدی برای اجرا در کلاینت ارسال کند. این محدودیت اختیاری است و در بیشتر REST APIهای متداول نقش اصلی ندارد.
Endpoint چیست؟
Endpoint آدرس مشخصی در API است که کلاینت درخواست خود را برای آن ارسال میکند.
برای مثال:
https://api.example.com/v1/products
این آدرس از چند بخش تشکیل شده است:
https://api.example.com Base URL
/v1 API Version
/products Resource Path
در درواره، آدرس پایه API به شکل زیر است:
https://api.darvareh.ir/v1
Endpoint مربوط به Chat Completions نیز چنین است:
https://api.darvareh.ir/v1/chat/completions
طراحی صحیح URL در REST API
در طراحی مسیرها بهتر است نام منبع را بنویسیم، نه عملیاتی که قرار است انجام شود.
طراحی نامناسب:
/getAllUsers
/createNewUser
/updateUser
/deleteUser
طراحی مناسبتر:
/users
/users/{user_id}
متد HTTP نوع عملیات را مشخص میکند:
GET /users
POST /users
GET /users/42
PATCH /users/42
DELETE /users/42
از اسم استفاده کنید، نه فعل
مسیر زیر فعل دارد:
/createOrder
نسخه Resource-oriented آن:
/orders
ایجاد سفارش با متد POST مشخص میشود:
POST /orders
نامگذاری را یکپارچه نگه دارید
اگر برای منابع از حالت جمع استفاده میکنید، این قرارداد را در کل API رعایت کنید:
/users
/products
/orders
/articles
ترکیب نامهای جمع و مفرد باعث ابهام میشود:
/user
/products
/order
/articles
REST اجبار رسمی برای جمع یا مفرد بودن نامها ندارد؛ ثبات مهمتر از انتخاب یکی از این دو است.
روابط را با مسیر قابلفهم نمایش دهید
برای دریافت سفارشهای یک کاربر میتوان نوشت:
/users/42/orders
برای دریافت یک سفارش خاص:
/users/42/orders/981
با این حال، مسیرها را بیش از حد عمیق نکنید. مسیر زیر نگهداری سختی دارد:
/users/42/orders/981/items/5/reviews/17/comments
در بسیاری از موارد بهتر است منبع مقصد مستقیماً قابل دسترسی باشد:
/order-items/5
/reviews/17
از Query Parameter برای فیلتر و مرتبسازی استفاده کنید
نمونه فیلتر:
/products?category=laptop&available=true
نمونه مرتبسازی:
/products?sort=-created_at
علامت منفی میتواند نشاندهنده ترتیب نزولی باشد، البته این قرارداد باید در مستندات API توضیح داده شود.
نمونه جستوجو:
/articles?search=REST+API
نمونه انتخاب صفحه:
/articles?page=2&limit=20
متدهای HTTP در REST API
متد HTTP هدف درخواست را مشخص میکند.
| متد | کاربرد متداول | نمونه |
|---|---|---|
| GET | دریافت یک یا چند منبع | GET /products |
| POST | ایجاد منبع یا شروع یک پردازش | POST /products |
| PUT | جایگزینی کامل منبع | PUT /products/42 |
| PATCH | بهروزرسانی بخشی از منبع | PATCH /products/42 |
| DELETE | حذف منبع | DELETE /products/42 |
| HEAD | دریافت هدرها بدون بدنه پاسخ | HEAD /products/42 |
| OPTIONS | دریافت قابلیتهای ارتباطی Endpoint | OPTIONS /products |
دریافت فهرست منابع با GET
GET /v1/articles?status=published&limit=20
پاسخ:
{
"data": [
{
"id": "art_101",
"title": "REST API چیست؟",
"status": "published"
},
{
"id": "art_102",
"title": "آموزش HTTP",
"status": "published"
}
],
"pagination": {
"limit": 20,
"next_cursor": null,
"has_more": false
}
}
درخواست GET نباید برای ایجاد، حذف یا تغییر داده استفاده شود.
ایجاد منبع با POST
POST /v1/articles
Content-Type: application/json
بدنه درخواست:
{
"title": "آموزش طراحی REST API",
"status": "draft"
}
پاسخ مناسب در صورت ایجاد منبع:
HTTP/1.1 201 Created
Location: /v1/articles/art_103
{
"id": "art_103",
"title": "آموزش طراحی REST API",
"status": "draft"
}
جایگزینی کامل با PUT
PUT معمولاً برای جایگزینی کامل نمایش یک منبع استفاده میشود:
PUT /v1/articles/art_103
Content-Type: application/json
{
"title": "راهنمای کامل REST API",
"status": "published",
"tags": ["api", "programming"]
}
اگر کلاینت فقط یک فیلد را ارسال کند، در یک طراحی مبتنی بر جایگزینی کامل ممکن است فیلدهای ارسالنشده حذف یا به مقدار پیشفرض بازگردانده شوند. رفتار دقیق باید در قرارداد API مشخص باشد.
بهروزرسانی جزئی با PATCH
برای تغییر بخشی از یک منبع معمولاً PATCH مناسبتر است:
PATCH /v1/articles/art_103
Content-Type: application/json
{
"status": "published"
}
حذف با DELETE
DELETE /v1/articles/art_103
اگر عملیات موفق باشد و بدنهای برگردانده نشود، پاسخ زیر مناسب است:
HTTP/1.1 204 No Content
در پاسخ 204 نباید بدنه ارسال شود.
رابطه CRUD با متدهای HTTP
CRUD مخفف Create، Read، Update و Delete است.
| عملیات CRUD | متد HTTP متداول |
|---|---|
| Create | POST |
| Read | GET |
| Update کامل | PUT |
| Update جزئی | PATCH |
| Delete | DELETE |
REST فقط معادل CRUD نیست. بعضی Endpointها عملیات محاسباتی، جستوجو، تولید محتوا یا اجرای Job انجام میدهند و الزاماً یک رکورد پایگاه داده را مدیریت نمیکنند.
برای مثال، درخواست تولید پاسخ هوش مصنوعی یک پردازش محاسباتی است:
POST /v1/chat/completions
Safe و Idempotent بودن متدها
در طراحی API باید با دو مفهوم مهم آشنا باشیم.
متد Safe
یک متد Safe نباید هدفش تغییر وضعیت سرور باشد. GET، HEAD و OPTIONS معمولاً Safe محسوب میشوند.
ممکن است سرور هنگام یک درخواست GET لاگ ثبت کند، اما این اثر جانبی نباید رفتار اصلی و مورد انتظار درخواست باشد.
متد Idempotent
اگر یک درخواست Idempotent چند بار با ورودی یکسان اجرا شود، اثر نهایی آن باید با یک بار اجرا شدن برابر باشد.
برای مثال:
DELETE /users/42
بار اول کاربر را حذف میکند. درخواستهای بعدی ممکن است پاسخ 404 بدهند، اما اثر نهایی همچنان حذف بودن کاربر است.
بهطور معمول:
| متد | Safe | Idempotent |
|---|---|---|
| GET | بله | بله |
| HEAD | بله | بله |
| PUT | خیر | بله |
| DELETE | خیر | بله |
| POST | خیر | معمولاً خیر |
| PATCH | خیر | وابسته به پیادهسازی |
جلوگیری از اجرای تکراری درخواست POST
فرض کنید کاربر دکمه پرداخت یا ثبت سفارش را دو بار فشار دهد. تکرار یک درخواست POST ممکن است دو سفارش ایجاد کند.
برای عملیات حساس میتوان در APIهایی که از این قابلیت پشتیبانی میکنند از Idempotency Key استفاده کرد:
Idempotency-Key: order-7c8a2f51
سرور کلید را برای مدت مشخص نگه میدارد و درخواست تکراری با همان کلید را دوباره اجرا نمیکند.
وجود این قابلیت را نباید بدون بررسی مستندات یک API فرض کرد. همچنین Retry خودکار روی درخواستهای غیر Idempotent باید با احتیاط انجام شود.
مهمترین کدهای وضعیت در REST API
کد وضعیت HTTP نتیجه کلی درخواست را مشخص میکند.
پاسخهای موفق
| کد | معنی | کاربرد |
|---|---|---|
| 200 | OK | درخواست با موفقیت پردازش شده است |
| 201 | Created | منبع جدید ایجاد شده است |
| 202 | Accepted | درخواست پذیرفته شده اما پردازش هنوز کامل نشده است |
| 204 | No Content | عملیات موفق بوده و بدنهای وجود ندارد |
خطاهای سمت کلاینت
| کد | معنی | کاربرد |
|---|---|---|
| 400 | Bad Request | ساختار درخواست نامعتبر است |
| 401 | Unauthorized | اطلاعات احراز هویت ارسال نشده یا معتبر نیست |
| 403 | Forbidden | هویت مشخص است اما مجوز کافی وجود ندارد |
| 404 | Not Found | منبع پیدا نشده است |
| 409 | Conflict | درخواست با وضعیت فعلی منبع تعارض دارد |
| 415 | Unsupported Media Type | فرمت بدنه پشتیبانی نمیشود |
| 422 | Unprocessable Content | ساختار قابل خواندن است اما دادهها معتبر نیستند |
| 429 | Too Many Requests | تعداد درخواستها از محدودیت عبور کرده است |
خطاهای سمت سرور یا سرویس بالادستی
| کد | معنی | کاربرد |
|---|---|---|
| 500 | Internal Server Error | خطای داخلی پیشبینینشده |
| 502 | Bad Gateway | پاسخ نامعتبر از سرویس بالادستی |
| 503 | Service Unavailable | سرویس موقتاً در دسترس نیست |
| 504 | Gateway Timeout | سرویس بالادستی در زمان مشخص پاسخ نداده است |
بازگرداندن 200 OK برای تمام پاسخها و قراردادن خطا در بدنه، طراحی مناسبی نیست:
{
"success": false,
"error": "User not found"
}
اگر منبع پیدا نشده است، پاسخ باید کد 404 داشته باشد تا مرورگر، SDK، Proxy، سیستم مانیتورینگ و کلاینت بتوانند آن را درست تفسیر کنند.
طراحی ساختار استاندارد خطا
ساختار خطا باید ثابت، قابلپردازش و برای توسعهدهنده قابلفهم باشد.
نمونه پیشنهادی:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "اطلاعات ارسالشده معتبر نیست.",
"details": [
{
"field": "email",
"reason": "فرمت ایمیل صحیح نیست."
}
],
"request_id": "req_f71b8c2a"
}
}
هر بخش هدف مشخصی دارد:
code: کد پایدار و قابلپردازش برای نرمافزارmessage: توضیح قابلخواندن برای توسعهدهنده یا کاربرdetails: جزئیات فیلدهای نامعتبرrequest_id: شناسهای برای پیگیری درخواست در لاگها
نام کلاس، Stack Trace، Query پایگاه داده، مسیر فایل و اطلاعات داخلی سرور را در پاسخ عمومی نمایش ندهید.
Validation یا اعتبارسنجی درخواست
اعتبارسنجی فقط بررسی وجود فیلدها نیست. میتوان آن را در چند سطح انجام داد.
اعتبارسنجی ساختار
{
"email": "user@example.com",
"age": 30
}
در این مرحله بررسی میشود که:
emailرشته باشدageعدد صحیح باشد- فیلدهای ضروری وجود داشته باشند
اعتبارسنجی محدودیتها
برای مثال:
- عنوان حداقل ۵ کاراکتر باشد
- تعداد آیتمها از ۱۰۰ بیشتر نباشد
- تاریخ پایان قبل از تاریخ شروع نباشد
اعتبارسنجی قوانین کسبوکار
برای مثال:
- محصول موجود باشد
- سفارش قبلاً لغو نشده باشد
- نام کاربری تکراری نباشد
- کاربر مجوز انجام عملیات را داشته باشد
اگر خروجی مدل هوش مصنوعی در یک فرایند نرمافزاری استفاده میشود، آن خروجی نیز باید پیش از ذخیره یا اجرای عملیات اعتبارسنجی شود.
طراحی Pagination یا صفحهبندی
بازگرداندن هزاران رکورد در یک پاسخ باعث افزایش زمان پاسخ، مصرف حافظه و هزینه شبکه میشود.
دو روش رایج برای صفحهبندی وجود دارد.
صفحهبندی Offset-based
/articles?offset=40&limit=20
یا:
/articles?page=3&limit=20
مزایا:
- پیادهسازی ساده
- امکان رفتن مستقیم به یک صفحه
- مناسب برای دادههای کوچک و نسبتاً ثابت
معایب:
- در دادههای بزرگ ممکن است کند شود
- با اضافه یا حذف شدن رکوردها احتمال جابهجایی نتایج وجود دارد
صفحهبندی Cursor-based
/articles?limit=20&cursor=eyJpZCI6MTAwfQ
پاسخ:
{
"data": [
{
"id": "art_101",
"title": "REST API چیست؟"
}
],
"pagination": {
"next_cursor": "eyJpZCI6MTAxfQ",
"has_more": true
}
}
مزایا:
- مناسب برای دادههای بزرگ
- رفتار پایدارتر هنگام اضافه شدن رکوردهای جدید
- کارایی بهتر برای Feed و پیمایش پیوسته
معایب:
- رفتن مستقیم به صفحه دلخواه دشوارتر است
- پیادهسازی سمت سرور پیچیدهتر میشود
| معیار | Offset | Cursor |
|---|---|---|
| سادگی پیادهسازی | بیشتر | کمتر |
| مناسب داده بزرگ | متوسط | بله |
| رفتن مستقیم به صفحه | بله | معمولاً خیر |
| پایداری در داده متغیر | کمتر | بیشتر |
| مناسب Infinite Scroll | متوسط | بله |
فیلتر، جستوجو و مرتبسازی
بهتر است قرارداد مشخصی برای Query Parameterها تعریف کنید.
فیلتر:
/orders?status=paid
چند فیلتر:
/orders?status=paid¤cy=IRR
مرتبسازی نزولی بر اساس زمان ایجاد:
/orders?sort=-created_at
مرتبسازی چندگانه:
/orders?sort=-created_at,total
جستوجو:
/articles?search=هوش+مصنوعی
محدود کردن فیلدهای پاسخ:
/users?fields=id,name,email
همه پارامترها، مقادیر مجاز، مقدار پیشفرض و حداکثر Limit باید در مستندات API ثبت شوند.
نسخهبندی REST API
تغییرات ناسازگار میتوانند کلاینتهای قبلی را از کار بیندازند. نسخهبندی به API اجازه میدهد تغییرات بزرگ را کنترل کند.
یکی از روشهای رایج، قرار دادن نسخه در URL است:
https://api.example.com/v1/products
نسخه بعدی:
https://api.example.com/v2/products
درواره نیز نسخه API را در URL قرار میدهد:
https://api.darvareh.ir/v1
روش دیگر استفاده از Header است:
Accept: application/vnd.example.v2+json
هیچ روش واحدی برای تمام پروژهها بهترین نیست. مهم این است که قرارداد انتخابشده:
- واضح باشد
- در تمام Endpointها رعایت شود
- تاریخ توقف نسخههای قدیمی اعلام شود
- تغییرات ناسازگار بدون نسخه جدید منتشر نشوند
اضافه کردن یک فیلد اختیاری معمولاً تغییر سازگار است، اما حذف یا تغییر نوع یک فیلد میتواند Breaking Change باشد.
REST API و JSON چه ارتباطی دارند؟
بیشتر REST APIهای مدرن از JSON استفاده میکنند، زیرا:
- برای انسان نسبتاً خواناست
- تقریباً در تمام زبانها پشتیبانی میشود
- نسبت به XML ساختار فشردهتری دارد
- برای وب و اپلیکیشن موبایل مناسب است
درخواست JSON معمولاً این هدر را دارد:
Content-Type: application/json
کلاینت میتواند قالب پاسخ مورد انتظار را اعلام کند:
Accept: application/json
REST به JSON محدود نیست و میتواند از XML، متن، تصویر، فایل باینری یا فرمتهای دیگر استفاده کند.
اتصال به REST API هوش مصنوعی درواره
درواره یک زیرساخت API برای دسترسی برنامهنویسی به مدلهای هوش مصنوعی است. آدرس پایه API:
https://api.darvareh.ir/v1
نمونه درخواست به Chat Completions:
curl --request POST \
--url https://api.darvareh.ir/v1/chat/completions \
--header "Authorization: Bearer YOUR_DARVAREH_API_KEY" \
--header "Content-Type: application/json" \
--data '{
"model": "YOUR_MODEL_ID",
"messages": [
{
"role": "system",
"content": "پاسخها را دقیق، کوتاه و به زبان فارسی بنویس."
},
{
"role": "user",
"content": "REST API را در سه جمله توضیح بده."
}
]
}'
در این درخواست:
- متد
POSTبرای ارسال ورودی پردازشی استفاده شده است - هدر
Authorizationکلید API را حمل میکند Content-Typeقالب JSON را مشخص میکندmodelشناسه مدل درواره استmessagesپیامهای ورودی مدل را مشخص میکند
برای مشاهده مدلهای در دسترس و قیمت بهروز آنها، صفحه مدلها و قیمتهای درواره را بررسی کنید.
ساخت REST API با FastAPI و اتصال آن به درواره
در این بخش یک سرویس خلاصهسازی متن میسازیم. کلاینت متن را به API ما میفرستد، بکاند آن را به درواره ارسال میکند و خلاصه را برمیگرداند.
این معماری یک مزیت مهم دارد: کلید درواره فقط در بکاند قرار میگیرد و وارد مرورگر یا اپلیکیشن عمومی نمیشود.
ساختار ساده پروژه:
rest-summary-api/
├── app.py
├── requirements.txt
└── .env
ایجاد محیط مجازی
در لینوکس و macOS:
python -m venv .venv
source .venv/bin/activate
در ویندوز PowerShell:
python -m venv .venv
.venv\Scripts\Activate.ps1
نصب وابستگیها
فایل requirements.txt:
fastapi
uvicorn[standard]
httpx
python-dotenv
سپس اجرا کنید:
pip install -r requirements.txt
تنظیم متغیرهای محیطی
فایل .env:
DARVAREH_API_KEY=YOUR_DARVAREH_API_KEY
DARVAREH_MODEL_ID=YOUR_MODEL_ID
فایل .env را در مخزن عمومی Git قرار ندهید. آن را به .gitignore اضافه کنید:
.env
.venv/
__pycache__/
نوشتن کد API
فایل app.py:
import os
import httpx
from dotenv import load_dotenv
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel, Field
load_dotenv()
DARVAREH_API_KEY = os.getenv("DARVAREH_API_KEY")
DARVAREH_MODEL_ID = os.getenv("DARVAREH_MODEL_ID")
DARVAREH_URL = "https://api.darvareh.ir/v1/chat/completions"
if not DARVAREH_API_KEY:
raise RuntimeError("متغیر DARVAREH_API_KEY تنظیم نشده است.")
if not DARVAREH_MODEL_ID:
raise RuntimeError("متغیر DARVAREH_MODEL_ID تنظیم نشده است.")
app = FastAPI(
title="Persian Summary API",
version="1.0.0",
description="REST API ساده برای خلاصهسازی متن فارسی با درواره",
)
class SummaryRequest(BaseModel):
text: str = Field(
min_length=20,
max_length=10_000,
description="متنی که باید خلاصه شود",
)
max_sentences: int = Field(
default=3,
ge=1,
le=10,
description="حداکثر تعداد جملههای خلاصه",
)
class SummaryResponse(BaseModel):
summary: str
model: str
class HealthResponse(BaseModel):
status: str
@app.get("/health", response_model=HealthResponse)
async def health_check():
return {"status": "ok"}
@app.post("/v1/summaries", response_model=SummaryResponse)
async def create_summary(payload: SummaryRequest):
request_body = {
"model": DARVAREH_MODEL_ID,
"messages": [
{
"role": "system",
"content": (
"تو یک ویراستار فارسی هستی. "
"متن ورودی را دقیق و بدون افزودن اطلاعات جدید خلاصه کن."
),
},
{
"role": "user",
"content": (
f"متن زیر را حداکثر در {payload.max_sentences} جمله "
f"خلاصه کن:\n\n{payload.text}"
),
},
],
}
headers = {
"Authorization": f"Bearer {DARVAREH_API_KEY}",
"Content-Type": "application/json",
}
timeout = httpx.Timeout(
connect=10.0,
read=60.0,
write=20.0,
pool=10.0,
)
try:
async with httpx.AsyncClient(timeout=timeout) as client:
response = await client.post(
DARVAREH_URL,
headers=headers,
json=request_body,
)
except httpx.TimeoutException as exc:
raise HTTPException(
status_code=504,
detail={
"code": "UPSTREAM_TIMEOUT",
"message": "سرویس هوش مصنوعی در زمان تعیینشده پاسخ نداد.",
},
) from exc
except httpx.RequestError as exc:
raise HTTPException(
status_code=502,
detail={
"code": "UPSTREAM_CONNECTION_ERROR",
"message": "ارتباط با سرویس هوش مصنوعی برقرار نشد.",
},
) from exc
if response.status_code == 429:
raise HTTPException(
status_code=503,
detail={
"code": "UPSTREAM_RATE_LIMIT",
"message": "سرویس موقتاً با محدودیت درخواست مواجه شده است.",
},
)
if response.status_code >= 500:
raise HTTPException(
status_code=502,
detail={
"code": "UPSTREAM_ERROR",
"message": "سرویس هوش مصنوعی پاسخ معتبری برنگرداند.",
},
)
if response.status_code >= 400:
raise HTTPException(
status_code=502,
detail={
"code": "UPSTREAM_REQUEST_REJECTED",
"message": "درخواست توسط سرویس بالادستی پذیرفته نشد.",
},
)
try:
result = response.json()
summary = result["choices"][0]["message"]["content"].strip()
except (ValueError, KeyError, IndexError, TypeError) as exc:
raise HTTPException(
status_code=502,
detail={
"code": "INVALID_UPSTREAM_RESPONSE",
"message": "ساختار پاسخ سرویس هوش مصنوعی معتبر نبود.",
},
) from exc
if not summary:
raise HTTPException(
status_code=502,
detail={
"code": "EMPTY_UPSTREAM_RESPONSE",
"message": "سرویس هوش مصنوعی پاسخ خالی برگرداند.",
},
)
return SummaryResponse(
summary=summary,
model=DARVAREH_MODEL_ID,
)
اجرای REST API
uvicorn app:app --reload
سرویس بهصورت پیشفرض روی آدرس زیر اجرا میشود:
http://127.0.0.1:8000
مستندات تعاملی FastAPI نیز در این آدرس در دسترس است:
http://127.0.0.1:8000/docs
آزمایش Health Check
curl http://127.0.0.1:8000/health
پاسخ:
{
"status": "ok"
}
ارسال درخواست خلاصهسازی
curl --request POST \
--url http://127.0.0.1:8000/v1/summaries \
--header "Content-Type: application/json" \
--data '{
"text": "REST یک سبک معماری برای طراحی سیستمهای توزیعشده است. در این معماری منابع با URL مشخص میشوند و کلاینت با استفاده از متدهای HTTP با آنها تعامل میکند. طراحی درست REST API باعث میشود ارتباط میان سرویسها قابلفهمتر و نگهداری آن سادهتر شود.",
"max_sentences": 2
}'
نمونه پاسخ:
{
"summary": "REST یک سبک معماری برای طراحی سیستمهای توزیعشده است که منابع را از طریق URL و متدهای HTTP در دسترس قرار میدهد. رعایت اصول آن به ساخت APIهای قابلفهم و نگهداریپذیر کمک میکند.",
"model": "YOUR_MODEL_ID"
}
مقدار واقعی model همان شناسهای خواهد بود که در فایل .env تنظیم کردهاید.
چرا درخواست مستقیم از فرانتاند مناسب نیست؟
قرار دادن کلید API در JavaScript مرورگر باعث میشود کاربران بتوانند آن را از ابزارهای توسعه مرورگر، فایلهای Bundle یا درخواستهای شبکه استخراج کنند.
طراحی نامناسب:
const apiKey = "YOUR_DARVAREH_API_KEY";
حتی اگر کد Minify یا Obfuscate شود، کلید همچنان قابل بازیابی است؛ زیرا مرورگر برای ارسال درخواست باید به مقدار واقعی آن دسترسی داشته باشد.
معماری مناسبتر:
مرورگر یا اپلیکیشن
↓
بکاند شما
↓
API درواره
↓
مدل هوش مصنوعی
بکاند شما میتواند:
- کلید API را محرمانه نگه دارد
- ورودیها را اعتبارسنجی کند
- محدودیت نرخ درخواست اعمال کند
- هزینه مصرف را کنترل کند
- Timeout و Retry را مدیریت کند
- خروجی مدل را بررسی کند
- لاگ و شناسه پیگیری تولید کند
Timeout در REST API
هر درخواست شبکه باید Timeout مشخص داشته باشد. بدون Timeout ممکن است Worker یا Connection برای مدت نامحدود منتظر بماند.
بهتر است Timeoutها جداگانه تنظیم شوند:
- Connect Timeout: زمان برقراری اتصال
- Read Timeout: زمان انتظار برای دریافت پاسخ
- Write Timeout: زمان ارسال درخواست
- Pool Timeout: زمان انتظار برای دریافت اتصال از Pool
مقدار مناسب به نوع عملیات بستگی دارد. یک درخواست ساده پایگاه داده با تولید محتوای هوش مصنوعی زمان پاسخ یکسانی ندارد.
Retry را چگونه پیادهسازی کنیم؟
Retry برای خطاهای موقت مفید است، اما نباید هر خطایی را بدون محدودیت تکرار کرد.
موارد مناسبتر برای Retry:
- خطای موقت شبکه
502 Bad Gateway503 Service Unavailable504 Gateway Timeout- در برخی شرایط
429 Too Many Requests
موارد نامناسب:
- بیشتر خطاهای
400 - کلید نامعتبر
- ورودی نامعتبر
- منبع پیدا نشده
- درخواست POST غیر Idempotent بدون سازوکار جلوگیری از تکرار
بین تلاشها از Exponential Backoff استفاده کنید:
تلاش اول: بدون تأخیر
تلاش دوم: حدود ۱ ثانیه
تلاش سوم: حدود ۲ ثانیه
تلاش چهارم: حدود ۴ ثانیه
برای جلوگیری از Retry همزمان تعداد زیادی کلاینت، مقداری Jitter یا تأخیر تصادفی نیز اضافه میشود.
پردازش همزمان و غیرهمزمان
بعضی عملیات سریعاند و میتوان نتیجه را در همان پاسخ برگرداند. بعضی عملیات مانند پردازش فایل بزرگ، تولید ویدئو یا اجرای گزارش طولانی بهتر است به Job تبدیل شوند.
نمونه ایجاد Job:
POST /v1/jobs
پاسخ:
HTTP/1.1 202 Accepted
{
"id": "job_8451",
"status": "queued",
"status_url": "/v1/jobs/job_8451"
}
کلاینت میتواند وضعیت را بررسی کند:
GET /v1/jobs/job_8451
پاسخ در زمان پردازش:
{
"id": "job_8451",
"status": "processing"
}
پاسخ بعد از پایان:
{
"id": "job_8451",
"status": "completed",
"result_url": "/v1/results/result_992"
}
برای کاربردهای بلادرنگ میتوان از Streaming استفاده کرد، اما برای Jobهای طولانی معمولاً Polling کنترلشده یا Webhook مناسبتر است.
مستندسازی REST API با OpenAPI
OpenAPI یک قالب استاندارد برای توصیف APIهای HTTP است. با OpenAPI میتوان موارد زیر را مشخص کرد:
- مسیرها
- متدها
- پارامترها
- ساختار درخواست
- ساختار پاسخ
- روش احراز هویت
- کدهای خطا
- Schema دادهها
نمونه ساده:
openapi: 3.1.0
info:
title: Persian Summary API
version: 1.0.0
paths:
/v1/summaries:
post:
summary: خلاصهسازی متن فارسی
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- text
properties:
text:
type: string
minLength: 20
maxLength: 10000
max_sentences:
type: integer
minimum: 1
maximum: 10
default: 3
responses:
"200":
description: خلاصه با موفقیت تولید شد
content:
application/json:
schema:
type: object
required:
- summary
- model
properties:
summary:
type: string
model:
type: string
"422":
description: داده ورودی معتبر نیست
"502":
description: پاسخ سرویس بالادستی معتبر نبود
"504":
description: سرویس بالادستی در زمان تعیینشده پاسخ نداد
OpenAPI و REST یک مفهوم نیستند:
- REST سبک معماری است
- OpenAPI قالب توصیف قرارداد API است
یک API میتواند RESTful باشد اما سند OpenAPI نداشته باشد. همچنین میتوان یک HTTP API غیر RESTful را با OpenAPI مستند کرد.
REST API در مقایسه با GraphQL و RPC
REST تنها روش طراحی API نیست.
| معیار | REST | GraphQL | RPC |
|---|---|---|---|
| محور طراحی | Resource | Schema و Query | عملیات و Function |
| مسیرها | معمولاً چند Endpoint | اغلب یک Endpoint | مسیرهای عملیاتی |
| انتخاب فیلدها | تحت کنترل سرور یا Query Parameter | تحت کنترل کلاینت | وابسته به قرارداد |
| Cache مبتنی بر HTTP | سادهتر | نیازمند طراحی بیشتر | وابسته به پیادهسازی |
| یادگیری اولیه | نسبتاً ساده | متوسط | نسبتاً ساده |
| مناسب برای | API عمومی، CRUD، وب و موبایل | دادههای مرتبط و نیازهای متغیر UI | عملیات مشخص و سرویسهای داخلی |
REST انتخاب خوبی است اگر:
- منابع مشخصی دارید
- میخواهید از قابلیتهای استاندارد HTTP استفاده کنید
- سادگی ابزارها و سازگاری گسترده مهم است
- API عمومی یا چندکلاینتی میسازید
GraphQL میتواند مناسبتر باشد اگر کلاینتها به ترکیبهای متفاوتی از دادههای مرتبط نیاز دارند.
RPC برای عملیات مشخصی مانند generateReport یا calculatePrice گاهی بیان طبیعیتری دارد. هدف، انتخاب معماری متناسب با مسئله است؛ نه RESTful نشان دادن اجباری تمام عملیات.
آیا Chat Completions یک REST API خالص است؟
در دنیای واقعی بسیاری از APIها از HTTP، JSON، URL نسخهبندیشده و قراردادهای استاندارد استفاده میکنند، اما تمام عملیاتشان دقیقاً به CRUD روی منابع پایدار محدود نیست.
Endpoint زیر یک پردازش تولید متن را اجرا میکند:
POST /v1/chat/completions
میتوان آن را یک HTTP API سازگار با الگوی رایج سرویسهای هوش مصنوعی دانست. REST یک طیف از رعایت محدودیتهای معماری است و استفاده از واژه RESTful نباید باعث شود کیفیت قرارداد، ثبات Schema و تجربه توسعهدهنده نادیده گرفته شود.
اشتباهات رایج در طراحی REST API
استفاده از فعل در تمام URLها
نامناسب:
/getProducts
/createProduct
/updateProduct
مناسبتر:
/products
/products/{product_id}
استفاده از GET برای تغییر داده
نامناسب:
GET /users/42/delete
این درخواست ممکن است توسط Cache، Crawler یا ابزارهای پیشبارگذاری فراخوانی شود.
مناسب:
DELETE /users/42
برگرداندن کد ۲۰۰ برای همهچیز
کد وضعیت باید نتیجه واقعی درخواست را نشان دهد. برای منبع ناموجود 404 و برای اعتبارسنجی ناموفق 400 یا 422 مناسبتر است.
تغییر مداوم ساختار خطا
اگر یک Endpoint فیلد message و دیگری error_text برگرداند، کلاینت مجبور میشود برای هر مسیر منطق جداگانه بنویسد.
نداشتن محدودیت برای فهرستها
Endpoint زیر نباید بدون محدودیت میلیونها رکورد برگرداند:
GET /events
برای فهرستهای بزرگ Pagination و حداکثر Limit تعریف کنید.
افشای ساختار پایگاه داده
API نباید صرفاً نسخه اینترنتی جدولهای پایگاه داده باشد. مدل عمومی API باید بر اساس نیاز مصرفکننده طراحی شود، نه جزئیات داخلی ذخیرهسازی.
وابسته کردن کلاینت به ترتیب فیلدهای JSON
ترتیب اعضای Object در JSON نباید بخشی از قرارداد API محسوب شود. کلاینت باید فیلدها را بر اساس نام بخواند.
نبود Timeout
درخواست بدون Timeout میتواند منابع سرور را برای زمان نامحدود اشغال کند.
Retry کورکورانه
تکرار بیقیدوشرط درخواست POST ممکن است عملیات را چند بار اجرا کند.
قرار دادن کلید API در فرانتاند
کلید سرویسهای بیرونی باید در بکاند، Secret Manager یا متغیر محیطی نگهداری شود.
بازگرداندن مستقیم خروجی مدل
اگر خروجی هوش مصنوعی قرار است وارد پایگاه داده یا فرایند دیگری شود، باید Schema، طول، نوع و مقادیر مجاز آن اعتبارسنجی شود.
چکلیست طراحی RESTful API
پیش از انتشار API این موارد را بررسی کنید:
- منابع اصلی سیستم مشخص شدهاند
- URLها بر اساس اسم منابع طراحی شدهاند
- نامگذاری مسیرها یکپارچه است
- متدهای HTTP معنای درست دارند
- GET برای تغییر داده استفاده نشده است
- کدهای وضعیت واقعی برگردانده میشوند
- ساختار خطا ثابت است
- تمام ورودیها اعتبارسنجی میشوند
- فهرستهای بزرگ Pagination دارند
- فیلتر و مرتبسازی قرارداد مشخص دارند
- نسخه API مشخص است
- تغییرات ناسازگار مدیریت میشوند
- Timeout برای درخواستهای خروجی تنظیم شده است
- Retry فقط برای شرایط مناسب انجام میشود
- کلید API در فرانتاند قرار ندارد
- اطلاعات داخلی در خطاها افشا نمیشود
- مستندات OpenAPI یا معادل آن وجود دارد
- نمونه درخواست و پاسخ واقعی ارائه شده است
- API روی ورودیها و پاسخهای نامعتبر آزمایش شده است
- لاگها دارای Request ID هستند
- مدل و قیمت سرویسهای مصرفی از منبع بهروز بررسی میشوند
پرسشهای متداول
REST API چه تفاوتی با RESTful API دارد؟
در کاربرد روزمره این دو اصطلاح اغلب به جای یکدیگر استفاده میشوند. RESTful معمولاً تأکید میکند که API بر اساس محدودیتها و اصول REST طراحی شده است.
آیا REST یک پروتکل است؟
خیر. REST یک سبک معماری است. بیشتر REST APIها از HTTP استفاده میکنند، اما REST با HTTP یکسان نیست.
آیا REST API حتماً باید JSON برگرداند؟
خیر. JSON رایجترین قالب است، اما REST میتواند XML، متن، تصویر یا داده باینری نیز منتقل کند.
آیا هر API مبتنی بر HTTP یک REST API است؟
خیر. یک API ممکن است از HTTP استفاده کند اما ساختار آن RPC، GraphQL یا یک قرارداد اختصاصی باشد.
تفاوت PUT و PATCH چیست؟
PUT معمولاً برای جایگزینی کامل نمایش یک منبع و PATCH برای تغییر جزئی آن استفاده میشود. رفتار دقیق هر دو باید در مستندات API مشخص شود.
تفاوت POST و PUT چیست؟
POST معمولاً منبعی با شناسه تعیینشده توسط سرور ایجاد میکند یا یک پردازش را شروع میکند. PUT اغلب برای ایجاد یا جایگزینی کامل منبع در یک URI مشخص استفاده میشود و معمولاً Idempotent است.
آیا GET میتواند بدنه داشته باشد؟
وابسته کردن API به بدنه GET انتخاب مناسبی نیست. بسیاری از ابزارها، Cacheها و واسطهها رفتار قابلاتکایی برای آن ارائه نمیکنند. برای پارامترهای دریافت داده از URL و Query Parameter استفاده کنید.
برای ساخت REST API چه زبانی مناسب است؟
REST به زبان خاصی وابسته نیست. میتوانید از Python، JavaScript، TypeScript، PHP، Java، Go، C# یا زبانهای دیگر استفاده کنید. انتخاب زبان به تیم، زیرساخت و نیاز پروژه بستگی دارد.
آیا FastAPI فقط برای هوش مصنوعی است؟
خیر. FastAPI یک فریمورک عمومی برای ساخت API با پایتون است، اما به دلیل پشتیبانی مناسب از Type Hint، Validation، Async و OpenAPI در پروژههای هوش مصنوعی نیز محبوب است.
آدرس API درواره چیست؟
Base URL درواره:
https://api.darvareh.ir/v1
Endpoint مربوط به Chat Completions:
https://api.darvareh.ir/v1/chat/completions
از کجا شناسه و قیمت مدلها را ببینیم؟
برای مشاهده مدلهای قابل استفاده، Model ID و قیمت بهروز، صفحه مدلهای درواره را بررسی کنید.
جمعبندی
REST API یک روش ساده اما قدرتمند برای طراحی ارتباط میان نرمافزارهاست. یک API خوب فقط مجموعهای از URLها نیست؛ بلکه قراردادی پایدار، قابلپیشبینی و مستند میان سرور و کلاینت است.
برای طراحی یک RESTful API حرفهای باید منابع را درست مدل کنید، از متدها و کدهای وضعیت HTTP با معنای واقعی آنها استفاده کنید، ورودیها را اعتبارسنجی کنید، پاسخ خطا را یکپارچه نگه دارید و برای Pagination، Versioning، Timeout و تغییرات ناسازگار برنامه داشته باشید.
اگر قصد دارید قابلیتهایی مانند چت، خلاصهسازی، تولید محتوا، تحلیل متن یا پردازش هوشمند را به REST API خود اضافه کنید، میتوانید از زیرساخت API درواره استفاده کنید. کلید را در بکاند نگه دارید، مدل مناسب را بر اساس کیفیت و هزینه انتخاب کنید و خروجی مدل را قبل از استفاده در فرایندهای نرمافزاری اعتبارسنجی کنید.
برای شروع، وارد وبسایت درواره شوید و مدلها و قیمتهای بهروز را در صفحه مدلهای درواره مشاهده کنید.
منابع رسمی
- رساله معماری REST نوشته Roy Fielding
- استاندارد معنای HTTP، RFC 9110
- مرجع متدهای HTTP در MDN
- راهنمای طراحی Resource-oriented API گوگل
- مشخصات رسمی OpenAPI
- مستندات FastAPI
مقالات مرتبط
- API هوش مصنوعی چیست و چه کاربردی دارد؟
- API سازگار با OpenAI چیست؟
- راهنمای کامل API Gateway
- آموزش اتصال به API درواره با cURL
- آموزش تست API درواره با Postman
- ساخت API هوش مصنوعی آماده محیط Production
- هوش مصنوعی با پایتون و API؛ راهنمای کامل
برای مطالعه شرایط استفاده و محدودیتهای مسئولیت، صفحه «سلب مسئولیت» را مشاهده کنید.