API چیست؟ راهنمای قرارداد، امنیت و طراحی API وب

کاربر فروشگاه ایرانی روی «پرداخت» می‌زند؛ درخواست به درگاه می‌رود، اما اینترنت موبایل او درست پیش از دریافت پاسخ قطع می‌شود. اپ دوباره همان درخواست را می‌فرستد. آیا دو سفارش ساخته می‌شود؟ آیا مبلغ دو بار رزرو می‌شود؟ آیا تیم پشتیبانی می‌تواند بفهمد کدام مرحله شکست خورده است؟ پاسخ این پرسش‌ها را واژه جادویی «API» نمی‌دهد؛ کیفیت قرارداد، امنیت و رفتار API در شکست تعیین می‌کند.

در این راهنما، API را نه «پل نامرئی» و نه یک محصول آماده، بلکه یک قرارداد قابل‌آزمایش میان مصرف‌کننده و ارائه‌دهنده می‌بینیم. از معناشناسی HTTP و تفاوت REST، GraphQL و gRPC شروع می‌کنیم؛ سپس به طراحی Resource، احراز هویت و مجوز، Idempotency، خطا و Retry، تست، Observability و چرخه عمر می‌رسیم. مثال‌ها برای فروشگاه، SaaS و سرویس‌های ایرانی نوشته شده‌اند و در استفاده از سرویس خارجی، مسیر دورزدن محدودیت‌ها پیشنهاد نمی‌شود.

API چیست؟ تعریف عملی، نه استعاره

API یا Application Programming Interface رابطی تعریف‌شده است که به یک نرم‌افزار اجازه می‌دهد قابلیت یا داده نرم‌افزار دیگری را طبق قواعد مشخص درخواست کند. این قواعد باید روشن کنند چه عملیاتی وجود دارد، ورودی و خروجی چه شکلی است، هویت و دسترسی چگونه کنترل می‌شود، خطا چه معنایی دارد و تغییرات آینده چگونه اعلام می‌شود.

API الزاماً تحت وب نیست. یک کتابخانه برنامه‌نویسی، سیستم‌عامل و مرورگر نیز API دارند. موضوع این مقاله Web API است؛ یعنی قراردادی که معمولاً روی شبکه و با HTTP در دسترس است. مصرف‌کننده می‌تواند وب‌سایت، اپ موبایل، پنل شریک تجاری، یک سرویس داخلی یا سامانه زمان‌بندی‌شده باشد.

مفهومپرسشی که باید جواب دهدنمونه
قابلیتمصرف‌کننده چه کاری می‌تواند انجام دهد؟ساخت سفارش یا مشاهده موجودی
قرارداد دادهفیلد، نوع، واحد و محدودیت چیست؟amount_rial عدد صحیح مثبت
معناشناسیتکرار درخواست و هر وضعیت چه معنایی دارد؟تکرار امن ساخت پرداخت با کلید Idempotency
اعتمادچه کسی درخواست می‌دهد و چه اجازه‌ای دارد؟کاربر احراز‌شده فقط سفارش خودش را می‌بیند
عملیاتچگونه خطا، ظرفیت و تغییر را مدیریت می‌کنیم؟Timeout، Retry، SLO و اعلام Deprecation

یک درخواست از چه مسیرهایی می‌گذرد؟

درخواست فقط از «کلاینت به سرور» نمی‌پرد. در یک معماری واقعی ممکن است از DNS، اتصال TLS، CDN یا WAF، Load Balancer و API Gateway عبور کند؛ سپس احراز هویت، مجوز، اعتبارسنجی ورودی، منطق کسب‌وکار، پایگاه داده و یک سرویس ثالث را طی کند. پاسخ نیز باید کد وضعیت، Header و Body قابل‌فهم داشته باشد و در تمام این مسیر، شناسه هم‌بستگی و Telemetry امکان عیب‌یابی را فراهم کند.

هر مرحله Failure mode خودش را دارد: DNS حل نمی‌شود، TLS منقضی است، Gateway نرخ را محدود می‌کند، Token معتبر ولی فاقد Scope است، رکورد هم‌زمان تغییر کرده، درگاه پاسخ نامعلوم داده یا پاسخ سالم در مسیر برگشت گم شده است. طراحی API یعنی این شکست‌ها را پیش از رخ‌دادن نام‌گذاری و مدیریت کنیم.

API چه چیزی را تضمین نمی‌کند؟

  • Microservice بودن، مقیاس‌پذیری یا نگهداشت‌پذیری را خودکار نمی‌کند؛ توزیع سامانه هزینه شبکه و عملیات را هم اضافه می‌کند.
  • جداشدن Frontend و Backend لزوماً UX سریع‌تر نمی‌سازد؛ Payload بزرگ، Waterfall درخواست‌ها و JavaScript سنگین می‌تواند نتیجه را بدتر کند.
  • API Gateway جای مجوز در سطح Object و Business rule را نمی‌گیرد.
  • HTTPS محتوای مسیر را رمز می‌کند، اما ورودی مخرب، دسترسی اضافه یا Secret افشاشده را درمان نمی‌کند.
  • API-first بدون مالک، تست قرارداد و سیاست تغییر فقط یک شعار است.

قرارداد HTTP: متد، Header، وضعیت و Body

برای HTTP API، نخست باید معناشناسی خود HTTP را بفهمیم. RFC 9110 معنی متدها، کدهای وضعیت و داده‌های کنترلی را تعریف می‌کند. Framework می‌تواند Route بسازد، اما معنای درست درخواست را تیم محصول و مهندسی تعیین می‌کنند.

Safe و Idempotent با «فقط یک‌بار اجرا» فرق دارند

متدکاربرد معمولSafe؟Idempotent در معناشناسی HTTP؟نکته طراحی
GETخواندن نمایش منبعبلهبلهنباید اثر تجاری مانند ثبت سفارش ایجاد کند
HEADHeaderهای GET بدون Bodyبلهبلهبرای متادیتا و بررسی سبک مفید است
POSTساخت یا فرمان پردازشیخیربه‌طور پیش‌فرض خیربرای عملیات مالی کلید Idempotency طراحی کنید
PUTجایگزینی وضعیت منبعخیربلهقرارداد باید معنای فیلدهای حذف‌شده را روشن کند
PATCHتغییر بخشی از منبعخیروابسته به قالب و عملیاتافزایش موجودی با «+۱» با Set کردن مقدار برابر نیست
DELETEحذف منبعخیربلهاثر پاسخ‌های تکراری و Soft delete را مستند کنید

Idempotent یعنی چند درخواست یکسان، از منظر اثر موردنظر سرور، نتیجه نهایی یکسانی داشته باشد؛ نه اینکه شبکه فقط یک بار بسته را بفرستد یا همه پاسخ‌ها دقیقاً یکسان باشند. یک DELETE ممکن است بار اول ۲۰۴ و بار دوم ۴۰۴ بدهد، ولی منبع در هر دو حالت حذف‌شده باقی بماند.

کد وضعیت را به بخشی از قرارداد تبدیل کنید

کدمعنای پیشنهادی در APIخطای رایج
200 / 201 / 202 / 204موفق، ساخته‌شده، پذیرفته‌شده برای پردازش، موفق بدون Bodyاستفاده از ۲۰۰ برای Jobی که هنوز تمام نشده
400درخواست از نظر نحو یا ساختار نامعتبرقرار دادن همه خطاهای کسب‌وکار زیر ۴۰۰
401اعتبار احراز هویت موجود نیست یا پذیرفته نشدمعادل‌گرفتن با «اجازه نداری»
403هویت شناخته شده ولی مجوز این عمل را نداردافشای بیش از حد وجود یک منبع حساس
404منبع یافت نشد یا بنا بر سیاست نباید آشکار شودبرگرداندن ۲۰۰ و success:false
409تعارض با وضعیت فعلی، نسخه یا قاعده یکتایینادیده‌گرفتن Concurrent update
422ساختار فهمیده شده اما مقدار از نظر دامنه پذیرفتنی نیستوابسته‌کردن کلاینت به متن فارسی خطا
429محدودیت مصرف؛ ترجیحاً همراه Retry-AfterRetry فوری و هم‌زمان همه کلاینت‌ها
500 / 502 / 503 / 504خطای داخلی، Gateway، عدم دسترس‌پذیری موقت، Timeout بالادستیارسال Stack trace و اطلاعات حساس

برای خطاهای ماشین‌خوان، یک ساختار پایدار تعریف کنید. RFC 9457 قالب application/problem+json را با فیلدهایی مانند type، title، status، detail و instance ارائه می‌دهد. متن detail می‌تواند برای انسان ترجمه شود، اما کلاینت نباید منطقش را از روی جمله فارسی استخراج کند؛ یک type یا کد دامنه پایدار لازم است.

{
  "type": "https://api.example.ir/problems/inventory-conflict",
  "title": "موجودی کافی نیست",
  "status": 409,
  "detail": "از کالای درخواستی فقط ۲ عدد باقی مانده است.",
  "instance": "https://api.example.ir/problem-instances/01J...",
  "code": "INVENTORY_CONFLICT",
  "trace_id": "8b9f..."
}

REST، GraphQL، gRPC، SOAP و Event: چه چیزی را مقایسه می‌کنیم؟

REST یک سبک معماری است، GraphQL زبان Query و Runtime است، gRPC چارچوب RPC است و Webhook الگوی اعلان رویداد به مصرف‌کننده محسوب می‌شود. پس جدول انتخاب باید مسئله، مصرف‌کننده و محدودیت عملیاتی را مقایسه کند، نه اینکه یک برنده همیشگی اعلام کند.

گزینهتناسب معمولمزیتهزینه یا ریسک
HTTP Resource-oriented (RESTful)API عمومی، CRUD دامنه، Cache و ابزارهای رایج وبمعناشناسی آشنا و اکوسیستم وسیعبدون طراحی درست ممکن است Chatty یا ناسازگار شود
GraphQLUIهای متنوع با نیاز انتخاب Field و Graph پیچیدهدرخواست داده متناسب با View و Schema تایپ‌شدهمجوز Field/Object، پیچیدگی Query، Cache و هزینه اجرا
gRPCارتباط داخلی سرویس‌ها، Streaming و کلاینت‌های کنترل‌شدهIDL، تولید Client و Streaming کارآمدمرورگر و Debug دستی محدودتر، نیاز به Gateway در برخی مرزها
SOAPیکپارچه‌سازی Legacy یا اکوسیستم متکی به WS-* و قرارداد XMLاستانداردهای بالغ برای برخی محیط‌های سازمانیپیچیدگی و Payload بیشتر برای بسیاری از کاربردهای جدید
WebSocket / SSEبه‌روزرسانی پیوسته یا Push از سرورارتباط بلادرنگ یا جریان یک‌طرفهاتصال طولانی، Backpressure، Resume و عملیات پیچیده‌تر
Webhook / Eventاطلاع از پرداخت، ارسال یا تغییر وضعیت بدون PollingCoupling زمانی کمترامضا، تکرار، ترتیب، Replay و تحویل حداقل یک‌بار

برای جزئیات Trade-offهای Query و Cache، راهنمای مقایسه GraphQL و REST را ببینید. تعریف رسمی GraphQL آن را زبان Query و Execution engine برای مدل داده کلاینت ـ سرور می‌داند؛ نسخه منتشرشده فعلی در مشخصات GraphQL سپتامبر ۲۰۲۵ قابل بررسی است. مستندات رسمی gRPC نیز آن را چارچوب RPC با Protocol Buffers و قابلیت‌های Lifecycle/Streaming معرفی می‌کند.

ماتریس تصمیم کوتاه

  • API عمومی برای توسعه‌دهنده ثالث: HTTP/JSON و OpenAPI معمولاً نقطه شروع قابل‌دسترس‌تری است.
  • چند UI با نیازهای داده متفاوت: GraphQL می‌تواند مفید باشد، اگر Budget پیچیدگی Query و مجوز دقیق دارید.
  • ارتباط داخلی کم‌تأخیر و تیم‌های کنترل‌شده: gRPC را کنار Service discovery، Deadline و Observability ارزیابی کنید.
  • اعلان تغییر وضعیت: Webhook یا Event را مکمل API خواندن وضعیت بدانید؛ اعلان به‌تنهایی Source of truth نیست.
  • شریک Legacy: هزینه جایگزینی SOAP شاید از ادامه قرارداد موجود بیشتر باشد.

در معماری محصول نیز انتخاب API از انتخاب تجربه کاربر جدا نیست. اگر هدف نصب‌پذیری، Offline یا Push است، راهنمای تصمیم و معماری PWA مرزهای Client، Cache و Backend را روشن‌تر می‌کند.

طراحی قرارداد: Contract-first، Code-first یا API-first؟

API-first یک جهت سازمانی است: API را محصولی با مصرف‌کننده، مالک و چرخه عمر می‌بینید. Contract-first یعنی پیش از Implementation، قرارداد قابل‌بررسی نوشته و با مصرف‌کنندگان هم‌راستا شود. Code-first یعنی قرارداد از کد استخراج شود. هیچ‌کدام کیفیت را تضمین نمی‌کنند؛ کیفیت از Review، تست سازگاری و بازخورد مصرف‌کننده می‌آید.

OpenAPI Specification یک توصیف استاندارد و مستقل از زبان برای HTTP API فراهم می‌کند تا انسان و ابزار، قابلیت‌های سرویس را بدون خواندن Source بفهمند. در زمان نگارش، شاخه منتشرشده جدید ۳.۲.۰ است؛ اما نسخه سند را بر اساس پشتیبانی Toolchain خود انتخاب و Pin کنید، نه صرفاً عدد جدیدتر.

قرارداد حداقلی چه چیزهایی دارد؟

  • Path و Operation با شناسه پایدار؛ Parameterهای Path، Query، Header و Cookie؛
  • Schema ورودی و خروجی، Required/Nullable، محدودیت طول، Enum و Pattern؛
  • نمونه معتبر و نامعتبر، همه وضعیت‌های موفق و خطا، Headerهای مهم؛
  • روش احراز هویت و Scope لازم برای هر Operation؛
  • قواعد Pagination، Rate limit، Idempotency و Cache؛
  • مالک، کانال پشتیبانی، SLO، سیاست نسخه و تغییر ناسازگار.

Specification را Lint کنید، Mock server بسازید و در CI اختلاف Implementation با قرارداد را بشکنید. Generated client مفید است، اما اگر قرارداد مبهم یا بیش‌ازحد بزرگ باشد، فقط ابهام را به کد بیشتری تبدیل می‌کند.

Resource و Action را از زبان دامنه استخراج کنید

نام‌ها باید برای تیم کسب‌وکار و توسعه یک معنا داشته باشند: /orders/{order_id} بهتر از /doOrderAction است. با این حال همه رفتارها CRUD نیستند. تأیید سفارش یا Refund یک Transition با قواعد تجاری است؛ Endpoint عملی مانند POST /payments/{id}/refunds می‌تواند از PATCH مبهم روشن‌تر باشد.

در طراحی هر Operation پاسخ دهید:

  1. Precondition چیست و چه کسی مجاز است؟
  2. اثر تجاری و مرز تراکنش کجاست؟
  3. تکرار درخواست چه نتیجه‌ای دارد؟
  4. اگر Dependency پاسخ نداد، وضعیت نهایی معلوم است یا نامعلوم؟
  5. کاربر و اپراتور چگونه نتیجه را Reconcile می‌کنند؟

داده ایرانی: ریال، تومان، تقویم و متن فارسی

موضوعقرارداد پیشنهادیچرا؟
پولعدد صحیح + کد Currency صریح؛ مثلاً amount: 1250000 و currency: IRR«۱۲۵ هزار تومان» در UI نباید به حدس واحد در API تبدیل شود
زمانTimestamp استاندارد با Offset/UTC؛ منطقه زمانی نمایش جدامرز روز، DST سیستم خارجی و Jobها مبهم نمی‌شود
تقویمتاریخ Canonical در قرارداد؛ تبدیل جلالی در لایه ارائه مگر دامنه خلافش را بخواهدمحاسبه و یکپارچه‌سازی پایدارتر می‌ماند
شناسهرشته Opaque، نه شماره موبایل یا کدملی در URLکاهش Coupling و افشای داده شخصی
متنUTF-۸، قواعد Normalization و محدودیت طول روشنی/ک فارسی و عربی، نیم‌فاصله و جست‌وجو قابل‌کنترل می‌شود
شماره تلفنقالب Canonical مانند E.۱۶۴ با سیاست اعتبارسنجی مشخصورودی ۰۹ و +۹۸ به دو هویت تبدیل نمی‌شود

Pagination، Filter و ترتیب پایدار

برگرداندن «همه سفارش‌ها» API را دیر یا زود می‌شکند. برای داده متغیر، Cursor pagination معمولاً از Offset در صفحات عمیق پایدارتر است؛ ولی Cursor باید Opaque و ترتیب یکتا باشد، مثلاً ترکیب زمان و ID. اگر Offset مناسب است، سقف limit، ترتیب پیش‌فرض و معنای Total count را مستند کنید. Filter و Sort فقط روی Fieldهای مجاز و Index‌شده اجرا شوند تا Query دلخواه کاربر به حمله مصرف منابع تبدیل نشود.

امنیت API: هویت کافی نیست؛ مجوز هر عمل و هر Object مهم است

Authentication می‌پرسد «چه هویت یا Clientی این درخواست را فرستاده؟» و Authorization می‌پرسد «این هویت روی این Object و این Action چه اجازه‌ای دارد؟». API key معمولاً پروژه یا Application را شناسایی می‌کند؛ به‌تنهایی اثبات هویت کاربر و مجوز مشاهده یک سفارش نیست.

مکانیزمکاربرد مناسبملاحظه
Session cookieوب‌اپ First-partySecure/HttpOnly/SameSite و دفاع CSRF لازم است
API keyشناسایی Client، Quota یا دسترسی ساده Server-to-serverبه کاربر نچسبانید؛ Rotate، Scope و محدود کنید
OAuth 2.0اعطای دسترسی محدود به Client برای ResourceAuthorization framework است، نه Login protocol
OpenID Connectاحراز هویت Federated روی OAuthID token را جای Access token به Resource API نفرستید
mTLS / Private-key JWTClient authentication پرریسک یا Server-to-serverچرخه عمر Certificate/Key و عملیات پیچیده‌تر است

RFC 9700 راهنمای امنیتی به‌روز OAuth ۲.۰ است؛ از Authorization Code، تطبیق دقیق Redirect URI و محدودکردن امتیاز Token دفاع می‌کند و برخی الگوهای ناامن قدیمی را کنار می‌گذارد. Flow را از روی نوع Client و Threat model انتخاب کنید؛ عبارت «از OAuth استفاده می‌کنیم» به‌تنهایی Evidence امنیت نیست.

مجوز را در سه سطح بررسی کنید

  • Function: آیا نقش Support حق Export یا Refund دارد؟
  • Object: آیا کاربر A می‌تواند با تغییر order_id سفارش B را بخواند؟
  • Property: آیا کاربر مجاز است فیلد role یا credit_limit را در Body بنویسد؟

این کنترل‌ها باید در مسیر دسترسی داده اجرا شوند، نه فقط با مخفی‌کردن دکمه در UI. فهرست OWASP API Security Top 10 2023 علاوه بر Broken authorization، مصرف بی‌رویه منابع، SSRF، مدیریت نادرست Inventory و اعتماد ناامن به APIهای ثالث را برجسته می‌کند. برای Threat model و کنترل اجرایی، راهنمای داخلی امنیت API بر پایه OWASP، OAuth و JWT را کنار این مقاله بخوانید.

CORS دیوار امنیتی API نیست

CORS به مرورگر می‌گوید کدام Origin اجازه دارد پاسخ Cross-origin را در JavaScript بخواند. ابزار خط فرمان، Bot و Backend مهاجم به CORS متکی نیستند. بنابراین CORS جای Authentication، Authorization، CSRF defense یا اعتبارسنجی ورودی نیست. Originهای مجاز را محدود کنید و Credential را با Wildcard ترکیب نکنید.

Secret، Log و داده شخصی

  • Secret را در Frontend، Mobile bundle، URL، Git، Ticket یا نمونه مستندات قرار ندهید.
  • کلیدها را در Secret manager نگه دارید؛ مالک، تاریخ ایجاد، Scope، Rotation و مسیر ابطال داشته باشند.
  • Token، Cookie، شماره کارت، کدملی و متن حساس را پیش از Log ماسک یا حذف کنید.
  • Response فقط داده لازم برای Use case را برگرداند؛ Serializing کامل مدل داخلی همان Excessive data exposure است.
  • TLS را برای همه Hopهای لازم فعال و اعتبار Certificate را بررسی کنید؛ خاموش‌کردن Verification راه‌حل عملیاتی نیست.

Webhook را مثل ورودی اینترنتی نامطمئن ببینید

برای Callback پرداخت یا حمل، امضای پیام را روی Raw body و Secret اختصاصی بررسی کنید؛ Timestamp و پنجره زمانی جلوی Replay قدیمی را بگیرد؛ Event ID را Deduplicate کنید؛ IP allowlist را فقط کنترل کمکی بدانید؛ و پس از دریافت سریع، رویداد را در Queue پردازش کنید. هیچ‌گاه صرف دریافت Callback، مبلغ و وضعیت سفارش را بدون تطبیق با داده محلی و در صورت لزوم استعلام مستقل نپذیرید. راهنمای اتصال امن درگاه پرداخت چرخه Initiate، Callback، Verify و Reconciliation را کامل‌تر توضیح می‌دهد.

سرویس خارجی برای کسب‌وکار ایرانی: فنی و حقوقی را با هم بسنجید

پیش از انتخاب نقشه، پیامک، ایمیل، هوش مصنوعی یا Cloud API، شرایط استفاده و Eligibility کشور/کسب‌وکار، روش پرداخت مجاز، محل و مالکیت داده، Subprocessorها، محدودیت نرخ، SLA، مسیر Support، Export و حذف داده و برنامه خروج را بررسی کنید. ممکن است قابلیت فنی امروز در دسترس و فردا محدود شود. این مقاله هیچ روش دورزدن محدودیت، هویت یا پرداخت ارائه نمی‌کند؛ گزینه‌ای انتخاب کنید که استفاده شما را صریحاً مجاز بداند و جایگزین عملی داشته باشد.

Idempotency و وضعیت نامعلوم: قلب API پرداخت و سفارش

در شبکه، «پاسخ نگرفتم» مساوی «عمل انجام نشد» نیست. شاید سرور پرداخت را ثبت کرده و پاسخ در راه برگشت گم شده باشد. اگر Client همان POST را کورکورانه تکرار کند، Duplicate محتمل است. Idempotency key به سرور اجازه می‌دهد تلاش‌های یک عملیات منطقی را تشخیص دهد.

الگوی عملی Idempotency key

  1. Client برای یک Intent تجاری، کلید تصادفی با Entropy کافی می‌سازد و در همه Retryهای همان Intent ثابت نگه می‌دارد.
  2. سرور کلید را در Scope مشخص—مثلاً Merchant + Operation—به‌صورت اتمیک ثبت می‌کند.
  3. Hash پارامترهای مهم کنار کلید ذخیره می‌شود؛ استفاده دوباره همان کلید با مبلغ یا سفارش متفاوت باید Conflict بدهد.
  4. نتیجه نهایی یا شناسه عملیات ذخیره و در تکرار برگردانده می‌شود. درخواست هم‌زمان دوم باید منتظر یا با وضعیت قابل‌فهم پاسخ داده شود.
  5. TTL بر اساس طول Retry، Reconciliation و ریسک دامنه تعیین و مستند می‌شود؛ حذف زودهنگام رکورد، Duplicate دیرهنگام می‌سازد.
POST /payment-attempts
Idempotency-Key: 01JAZ...

{
  "order_id": "ord_01J...",
  "amount": 1250000,
  "currency": "IRR"
}

Idempotency به‌تنهایی Exactly-once delivery ایجاد نمی‌کند. تراکنش پایگاه داده، Unique constraint، Outbox/Inbox، Deduplication مصرف‌کننده و Job تطبیق همچنان لازم‌اند. برای پرداخت نامعلوم، Endpoint خواندن وضعیت و Job Reconciliation با شناسه مرجع ارائه‌دهنده طراحی کنید؛ پشتیبان نباید وضعیت مالی را صرفاً با حدس دستی تغییر دهد.

Timeout، Deadline و Retry budget

هر فراخوانی شبکه Timeout می‌خواهد. Connect timeout را از Read/overall deadline جدا کنید. در زنجیره سرویس‌ها، سرویس پایین‌دستی نباید Deadline طولانی‌تر از بودجه باقی‌مانده درخواست بگیرد. Retry فقط برای خطاهای موقت و عملیات امن/Idempotent انجام شود و محدود باشد.

وضعیتRetry خودکار؟رفتار پیشنهادی
Timeout قبل از دانستن نتیجه POST مالیفقط با Idempotency و سیاست ارائه‌دهندههمان کلید، سپس Query/Reconcile
429معمولاً بلهاحترام به Retry-After، Backoff و Jitter
۵۰۲/۵۰۳/۵۰۴ موقتمحدود و مشروطRetry budget، Jitter و Circuit breaker
400/401/403/422خیر، بدون تغییر درخواست/اعتباراصلاح ورودی، Permission یا Token
Conflict 409وابسته به دامنهخواندن نسخه جدید یا نمایش تعارض به کاربر

Backoff نمایی بدون Jitter می‌تواند همه Instanceها را دوباره هم‌زمان بیدار کند. Retry در چند لایه نیز تعداد فراخوانی را انفجاری می‌کند؛ یک لایه مالک Retry شود و Budget کل داشته باشد. Circuit breaker، Bulkhead، محدودیت Concurrency و Queue ابزارند، نه نسخه ثابت. ابتدا Failure mode و SLO را مشخص کنید.

Queue و Event: تحویل دوباره را عادی فرض کنید

اغلب Queueها تحویل At-least-once دارند؛ Consumer باید Duplicate را تحمل کند. Event ID، Inbox، پردازش اتمیک و Dead-letter queue تعریف کنید. ترتیب جهانی را فرض نکنید؛ اگر ترتیب برای یک سفارش مهم است، Partition key و Version رویداد را طراحی کنید. Schema رویداد نیز قرارداد است و تغییر ناسازگار آن می‌تواند مصرف‌کننده خاموش را بشکند.

Cache: سرعت با معنای درست

Cache برای Response عمومی و تکرارشونده مفید است، ولی داده حساب کاربر یا موجودی لحظه‌ای سیاست جدا می‌خواهد. Cache-Control، Vary، ETag و درخواست شرطی را آگاهانه انتخاب کنید. پاسخ شخصی را در Cache اشتراکی نریزید و Invalidation را بخشی از طراحی بدانید. برای لایه‌ها و Headerها، راهنمای کش سایت و معماری Cache را ببینید.

تست API: از Schema تا شکست عمدی

تست Happy path کافی نیست. API قابل‌اعتماد باید ثابت کند قرارداد را رعایت می‌کند، دسترسی افقی و عمودی را می‌بندد، زیر بار رفتار کنترل‌شده دارد و هنگام قطع Dependency نتیجه نامعلوم را درست مدیریت می‌کند.

لایه تستچه چیزی را کشف می‌کند؟نمونه Gate
Unit / Domainقواعد کسب‌وکار و Edge case سریعRefund بیش از مبلغ پرداخت‌شده رد شود
Schema / Lintنقص و ناسازگاری سندResponse مستندنشده یا Required مبهم ممنوع
Contractتوافق Provider و Consumerتغییر حذف Field CI را Fail کند
Integrationپایگاه داده، Queue و Sandbox ثالثCallback تکراری فقط یک اثر داشته باشد
Authorization / SecurityBOLA/BFLA، Injection، SSRF و Secret leakکاربر A به Order B دسترسی نگیرد
Property/Fuzzورودی‌های پیش‌بینی‌نشده و BoundaryPayload بزرگ یا Unicode غیرعادی سرویس را نشکند
Load / Soakظرفیت، Leak و Tail latencySLO در بار هدف و مدت طولانی حفظ شود
Failure / ChaosTimeout، قطع Dependency و Recoveryقطعی درگاه سفارش را دو بار نسازد

Sandbox ارائه‌دهنده ثالث رفتار Production را کامل شبیه‌سازی نمی‌کند. Test double برای سناریوهای قطعی و تست محدود End-to-end برای Integration واقعی داشته باشید. Deploy را با Canary یا Feature flag کوچک آغاز کنید و Rollback قرارداد/داده را پیش از انتشار تمرین کنید.

Observability: آیا می‌توان یک درخواست را تا نتیجه تجاری دنبال کرد؟

Log زیاد مساوی Observability نیست. برای هر API حداقل Rate، Errors و Duration را با بُعدهای کنترل‌شده بسنجید؛ Trace توزیع‌شده و Correlation ID مسیر را بین Gateway، سرویس و Queue وصل کند؛ و Metric تجاری مانند نرخ موفق Initiate تا Verify پرداخت کنار Metric فنی دیده شود.

  • Latency: Median کافی نیست؛ p95/p99 و تفکیک Operation مهم است.
  • Error: خطای Client را از Server/Dependency و Error code دامنه جدا کنید.
  • Traffic و saturation: نرخ درخواست، Concurrency، Queue lag، Pool و ظرفیت Dependency را ببینید.
  • Trace: Spanها Deadline، Retry count و Dependency را نشان دهند، اما Payload حساس را ثبت نکنند.
  • SLO: شاخصی انتخاب کنید که موفقیت مورد انتظار مصرف‌کننده را بسنجد، نه صرفاً Up بودن Process.

برای معماری Telemetry و طراحی SLO، راهنمای Observability وب‌سایت را بخوانید. پایش بیرونی نیز باید مسیر حیاتی را از دید کاربر بررسی کند؛ راهنمای مانیتورینگ Uptime تفاوت Check سطحی با Synthetic journey را توضیح می‌دهد.

Cardinality و حریم خصوصی را کنترل کنید

قرار دادن user_id، URL کامل یا order_id در Label متریک هزینه و Cardinality را منفجر می‌کند؛ این شناسه‌ها در Trace/Log کنترل‌شده مناسب‌ترند. Retention، دسترسی و Redaction را بر اساس طبقه‌بندی داده تنظیم کنید. Observability نباید به انبار دائمی داده شخصی تبدیل شود.

نسخه، سازگاری و پایان عمر API

هر تغییر Contract شکستن نیست. افزودن Field اختیاری معمولاً سازگار است، اما مصرف‌کننده‌ای که Parser سخت‌گیر دارد شاید باز هم بشکند. حذف یا تغییر نوع Field، تنگ‌کردن Enum، تغییر معنای پیش‌فرض و الزام Scope جدید معمولاً ناسازگارند. سازگاری را با Consumer واقعی و تست قرارداد بسنجید.

Version فقط عدد در URL نیست

Version می‌تواند در Path، Header یا Media type باشد، اما مهم‌تر از محل آن سیاست تغییر است: چه چیزی Major محسوب می‌شود؟ چند نسخه پشتیبانی می‌شود؟ Migration guide و محیط تست چیست؟ مصرف کدام Endpointها پایش می‌شود؟ چه کسی با مصرف‌کنندگان تماس می‌گیرد؟

RFC 9745 هدر Deprecation را برای اعلام زمان منسوخ‌شدن منبع تعریف می‌کند و برای اعلام زمان توقف، استفاده از Sunset طبق RFC 8594 را توضیح می‌دهد. هدر به‌تنهایی کافی نیست؛ Link به راهنمای مهاجرت، اعلان مستقیم، داشبورد مصرف و دوره هم‌پوشانی لازم است.

مستندات و تجربه توسعه‌دهنده

مستند خوب باید زمان اولین درخواست موفق را کم کند و هنگام شکست، مسیر حل بدهد. Quickstart، Authentication، محیط‌ها، مثال‌های قابل‌اجرا، Error catalog، Rate limit، Idempotency، Webhook verification، Changelog و Status page اجزای پایه‌اند. نمونه‌ها باید Secret جعلی و داده غیرواقعی داشته باشند.

SDK رسمی زمانی ارزش دارد که Versioning، Retry و Error mapping را استاندارد کند؛ SDK رهاشده بدهی مضاعف است. برای هر زبان، حداقل نسخه Runtime، سیاست Release و End-of-support را روشن کنید. API Explorer را به محیط امن و داده آزمایشی محدود کنید.

حاکمیت API: موجودی، مالک و مرز مسئولیت

Shadow API و Endpoint فراموش‌شده از نبود Inventory می‌آیند. برای هر API، مالک فنی و محصول، مصرف‌کنندگان، سطح Exposure، طبقه داده، روش Auth، SLO، نسخه، Dependencyها و تاریخ بازبینی ثبت شود. Gateway discovery کمک می‌کند، اما Source of truth باید با Repository و Deploy pipeline هم‌راستا باشد.

داراییمالکEvidence لازمتناوب بازبینی
ContractProduct + Tech leadOpenAPI/IDL، Review و Compatibility testهر تغییر
دسترسیService owner + SecurityThreat model، Scope matrix و تست BOLAفصلی و هر قابلیت حساس
قابلیت اطمینانService owner/SRESLO، Runbook، Alert و بازیابی آزمایش‌شدهماهانه/پس از Incident
سرویس ثالثBusiness + Legal + TechEligibility، DPA/Terms، SLA و Exit testحداقل سالانه
چرخه عمرAPI product ownerUsage، Changelog، Deprecation و Migrationهر Release

سناریوی کامل: سفارش فروشگاه ایرانی

فرض کنید کاربر سبدی به مبلغ ۱۲۵ هزار تومان دارد. Frontend مبلغ را منبع حقیقت نمی‌داند؛ Backend قیمت را از Snapshot معتبر سبد محاسبه و در قرارداد پرداخت به 1,250,000 IRR تبدیل می‌کند. Client برای ساخت Payment attempt یک Idempotency key می‌فرستد. Backend رکورد را اتمیک می‌سازد و شناسه داخلی را پیش از تماس با درگاه نگه می‌دارد.

  1. اگر درگاه پاسخ موفق دهد، Reference در همان Workflow ثبت و وضعیت به pending_user_action می‌رود.
  2. اگر Timeout شود، وضعیت unknown است؛ همان Intent دوباره با کلید ثابت یا طبق قرارداد ارائه‌دهنده بررسی می‌شود، نه با ساخت کورکورانه Intent تازه.
  3. Callback امضاشده دریافت و Deduplicate می‌شود؛ مبلغ، Merchant و شناسه سفارش تطبیق می‌خورد.
  4. Backend مستقل Verify می‌کند؛ فقط نتیجه تأییدشده، سفارش را Paid می‌کند.
  5. Outbox رویداد PaymentVerified را منتشر می‌کند؛ موجودی و ارسال Consumerهای Idempotent دارند.
  6. اگر سرویس حمل قطع باشد، سفارش Paid باقی می‌ماند و Fulfillment با Queue/Retry و هشدار جدا ادامه می‌یابد.
  7. Job تطبیق، Paymentهای Unknown/Pending قدیمی را بررسی می‌کند و داشبورد اختلاف مالی دارد.

این طراحی اثر خطای شبکه را از اثر تجاری جدا می‌کند. همچنین داده‌های API می‌توانند به سنجش قیف و تشخیص Drop-off کمک کنند، به شرط آنکه Eventها تعریف و کنترل کیفیت شوند؛ برای لایه تحلیلی، راهنمای تحلیل داده بازاریابی و Warehouse مفید است.

برنامه ۳۰، ۶۰ و ۹۰ روزه بهبود API موجود

روز ۱ تا ۳۰: موجودی و ریسک‌های حیاتی

  • APIها، Endpointها، مالک، مصرف‌کننده، داده حساس و Dependency ثالث را فهرست کنید.
  • عملیات مالی/حساس را برای Idempotency، Timeout و مجوز سطح Object بررسی کنید.
  • Secretهای Hard-coded، Logهای حساس و Endpointهای بدون مالک را اصلاح یا مهار کنید.
  • یک Error format، Correlation ID و داشبورد Rate/Error/Duration پایه بسازید.

روز ۳۱ تا ۶۰: قرارداد و تست

  • OpenAPI/IDL را با Implementation هم‌راستا و در CI Lint/Validate کنید.
  • ماتریس Role × Action × Object و تست‌های منفی Authorization بسازید.
  • Contract test برای مصرف‌کنندگان حیاتی و Failure test برای درگاه/پیامک اجرا کنید.
  • Retry budget، Deadline و Runbook وضعیت نامعلوم را مستند کنید.

روز ۶۱ تا ۹۰: چرخه عمر و SLO

  • SLI/SLO مصرف‌کننده‌محور، Alert قابل‌اقدام و Error budget تعریف کنید.
  • سیاست Version، Deprecation، Sunset و Migration را اجرا و یک سناریو را تمرین کنید.
  • ارزیابی سرویس ثالث، برنامه خروج و بازیابی/Reconciliation را آزمایش کنید.
  • مرور معماری و امنیت را به Definition of Done قابلیت‌های API تبدیل کنید.

چک‌لیست طراحی و ممیزی API

  • □ Use case، مصرف‌کننده، مالک و سطح Exposure مشخص است.
  • □ قرارداد ماشین‌خوان Schema، مثال، وضعیت‌ها و Headerها را پوشش می‌دهد.
  • □ واحد پول، زمان، Nullability، Enum و Encoding مبهم نیست.
  • □ عملیات حساس Idempotency، قید یکتا و Reconciliation دارد.
  • □ Authentication از Authorization سطح Function/Object/Property جداست.
  • □ Token و Secret کم‌اختیار، قابل Rotation و خارج از Log/URL هستند.
  • □ CORS جای کنترل دسترسی یا CSRF defense فرض نشده است.
  • □ Timeout، Deadline، Retry budget، Backoff/Jitter و Circuit policy تعریف شده‌اند.
  • □ Webhook امضا، Timestamp، Replay defense و Deduplication دارد.
  • □ Pagination، Filter، Rate limit و سقف Payload جلوی مصرف بی‌رویه را می‌گیرند.
  • □ Cache خصوصی/عمومی، ETag و Invalidation آگاهانه طراحی شده است.
  • □ Contract، Integration، Authorization، Load و Failure test در Pipeline وجود دارد.
  • □ RED metrics، Trace، Correlation ID، SLO و Runbook قابل استفاده‌اند.
  • □ سرویس خارجی از نظر Eligibility، داده، SLA، هزینه و Exit بررسی شده است.
  • □ Changelog، Version، Deprecation/Sunset و راهنمای Migration دارید.

پرسش‌های متداول

API چیست و چه فرقی با Web Service دارد؟

API قرارداد استفاده از قابلیت نرم‌افزار است و می‌تواند محلی یا شبکه‌ای باشد. Web Service نوعی API شبکه‌ای است؛ در کاربرد امروز، Web API معمولاً با HTTP ارائه می‌شود. پس هر Web Service یک API است، اما هر API الزاماً Web Service نیست.

REST بهتر است یا GraphQL؟

برنده عمومی وجود ندارد. HTTP Resource-oriented برای API عمومی، Cache و ابزارهای متداول نقطه شروع خوبی است؛ GraphQL برای UIهای متنوع و انتخاب Fieldها ارزش دارد، اما مجوز، پیچیدگی Query، Cache و عملیات دقیق‌تری می‌خواهد. تصمیم را با مصرف‌کننده، مدل داده و توان عملیاتی بگیرید.

آیا API key برای احراز هویت کاربر کافی است؟

معمولاً خیر. API key بیشتر Client یا پروژه را معرفی می‌کند. هویت کاربر و مجوز دسترسی به Object باید با سازوکار متناسب مانند Session یا OAuth/OIDC و کنترل Authorization سمت سرور اجرا شود. کلید نیز باید Scope، Rotation و محدودیت مصرف داشته باشد.

چرا برای پرداخت به Idempotency key نیاز داریم؟

چون Timeout معلوم نمی‌کند عملیات انجام شده یا نه. کلید Idempotency تلاش‌های یک Intent را به هم وصل می‌کند تا Retry همان اثر تجاری را دوباره نسازد. ذخیره اتمیک، Hash پارامتر، TTL، Constraint و Reconciliation مکمل آن هستند.

حداقل مستندات یک API حرفه‌ای چیست؟

قرارداد ماشین‌خوان OpenAPI/IDL، Quickstart، Authentication و Scope، مثال‌های موفق و خطا، Error catalog، Pagination و Rate limit، Idempotency/Retry، Webhook verification، Changelog، SLO/Status و سیاست نسخه و پایان عمر حداقل‌های عملی‌اند.

جمع‌بندی

API خوب صرفاً Endpointی نیست که JSON برگرداند. قرارداد آن باید برای انسان و ماشین روشن، در برابر تغییر قابل‌آزمایش، در سطح Object مجاز، در شکست قابل‌بازیابی و در عملیات قابل‌مشاهده باشد. برای API بعدی، از انتخاب Framework شروع نکنید: Intent تجاری، مالک، داده، اثر تکرار، مرز اعتماد و Failure mode را بنویسید؛ سپس سبک و ابزار را انتخاب کنید.

اقدام بعدی: یک Endpoint مالی یا حساس را انتخاب کنید و پنج Evidence جمع کنید: Contract، ماتریس مجوز، تست Retry/Idempotency، Trace یک درخواست و Runbook وضعیت نامعلوم. هر موردی که وجود ندارد، ریسک واقعی است—نه صرفاً بدهی مستندات.

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *