بلاگ فنی با تمرکز بر فرانت‌اند

بازگشت به فهرست مطالب

ای‌پی‌آی (API)

[ منبع ]
ای‌پی‌آی (API)

طراحی API#

چرا

هدف از این بخش، طراحی رابط‌های RESTful است که منطقی و ساده باشند تا اعضای تیم و مشتریان بتوانند به‌سادگی و به‌صورت یکنواخت از آن‌ها استفاده کنند.

چرا

نبود هماهنگی و سادگی می‌تواند هزینه‌های یکپارچه‌سازی و نگهداری را به‌طور چشمگیری افزایش دهد. به همین دلیل، طراحی API در این مستند گنجانده شده است.

  • ما عمدتاً از طراحی مبتنی بر منابع (Resource-Oriented Design) پیروی می‌کنیم که شامل سه عنصر اصلی است: 
    1. منابع (Resources):
      • شامل داده‌هایی هستند که می‌توانند به‌صورت تو در تو (nested) سازمان‌دهی شوند.
      • برای هر منبع، متدهایی برای انجام عملیات مختلف تعریف می‌شود.
    2. مجموعه‌ها (Collections):
      • گروهی از منابع (Resources)، یک مجموعه (Collections) را تشکیل می‌دهند.
    3. آدرس‌های اینترنتی (URLs):
      • مکان آنلاین یک منبع (Resource) یا مجموعه (Collection) را مشخص می‌کنند.

چرا

این شیوه یک استاندارد شناخته‌شده در بین توسعه‌دهندگان (مصرف‌کنندگان اصلی API) است. علاوه بر خوانایی و سادگی استفاده، اجازه می‌دهد کتابخانه‌ها و اتصال‌دهنده‌های عمومی بسازیم، بدون این‌که از ابتدا بدانیم API دقیقاً چه می‌کند.

  • در طراحی مسیرهای (URL) از kebab-case استفاده کنید.

  • برای پارامترهای موجود در Query String یا فیلدهای منبع، از camelCase استفاده کنید.

  • نام منابع در URL باید به صورت kebab-case و جمع (plural) باشد.

  • همیشه از اسامی جمع برای آدرس (URL)‌هایی که به یک مجموعه اشاره می‌کنند استفاده کنید: /users

چرا

به این دلیل که خوانایی بیشتری دارد و آدرس‌ها را یکدست نگه می‌دارد. (برای اطلاعات بیشتر…)

  • در سورس کد، اسامی جمع را به متغیرها و پراپرتی‌هایی با پسوند «List» تبدیل کنید. (مانند userList به جای users)

چرا

استفاده از اسامی جمع در URL مناسب است، اما در سورس کد ممکن است به دلیل شباهت ظاهری به اسامی مفرد، منجر به اشتباهات و خطاهای برنامه‌نویسی شود.

  • همیشه در طراحی URLها، ابتدا نام مجموعه (collection) را به صورت جمع ذکر کنید و سپس با استفاده از یک شناسه یکتا (identifier)، به یک منبع خاص در آن مجموعه اشاره کنید:
/students/245743
/airports/kjfk
plaintext
  • از URLهایی مانند این اجتناب کنید:
GET /blogs/:blogId/posts/:postId/summary
bash

چرا

این نوع URL به جای اشاره به یک منبع (resource)، به یک ویژگی (property) یا خلاصه‌ای از منبع اشاره می‌کند. در RESTful APIها، URLها باید منابع (resource) را نمایندگی کنند، نه ویژگی‌های خاص آن‌ها را. برای دسترسی به ویژگی‌های خاص یا خلاصه‌ای از یک منبع، می‌توانید از کوئری پارامترها (query parameters) استفاده کنید تا بتوانید پاسخ را بر اساس نیاز خود تنظیم و محدود کنید.

  • افعال را از URLهای منابع خود حذف کنید.

چرا

زیرا اگر برای هر عملیات resource از یک فعل استفاده کنید، به زودی با لیستی بزرگ از URLها مواجه خواهید شد که الگوی ثابتی ندارند و یادگیری را برای توسعه‌دهندگان دشوار می‌کنند. علاوه بر این، ما از افعال برای چیز دیگری استفاده می‌کنیم.

  • معمولاً از اسامی (nouns) در مسیرهای URL برای اشاره به منابع استفاده می‌شود. با این حال، در مواردی که نیاز به انجام عملیاتی دارید که به منبع خاصی مرتبط نیست (Non-resource) و در واقع یک عملکرد یا تابع را اجرا کرده و نتیجه را برمی‌گرداند، می‌توانید از افعال (verbs) در مسیرهای URL استفاده کنید. این عملیات‌ها مربوط به CRUD (ایجاد، دریافت، به‌روزرسانی، حذف) نیستند:
/translate?text=Hallo
bash

چرا

برای عملیات‌های CRUD، از متدهای HTTP مانند GET، POST، PUT و DELETE بر روی URLهای منابع (resource) یا مجموعه‌ها (collection) استفاده می‌کنیم. اما در مواردی که عملیاتی خاص انجام می‌دهید که به منبع خاصی مرتبط نیست، استفاده از افعال در مسیرهای URL می‌تواند مناسب باشد. این نوع مسیرها معمولاً به عنوان “کنترلر” شناخته می‌شوند و تعداد آن‌ها در APIها معمولاً کم است. (برای اطلاعات بیشتر…)

  • اگر درخواست (Request Body) یا پاسخ (Response) در قالب JSON است، از camelCase برای نام پراپرتی‌های JSON پیروی کنید تا یکپارچگی و انسجام حفظ شود.

چرا

این راهنما و دستورالعمل برای پروژه‌ای مبتنی بر جاوااسکریپت تنظیم شده است. در نتیجه، فرض بر این است که تولید و پردازش JSON هم در همین زبان انجام می‌شود و پیروی از camelCase منطقی است.

  • هرچند یک منبع (resource) مفهومی یکتا و مفرد است که مشابه با یک نمونه شیء یا رکورد در پایگاه داده می‌باشد، اما نباید از نام جدول‌های پایگاه داده (table_name) برای نام‌گذاری منابع و از نام ستون‌ها (column_name) برای ویژگی‌های منابع استفاده کنید.

چرا

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

  • مجدداً تأکید می‌شود، در نام‌گذاری مسیر (URL) فقط از «اسم» استفاده کنید و از توضیح عملکرد آن بپرهیزید.

چرا

فقط از اسامی در URLهای منبع استفاده کنید و از مسیرهایی نظیر /addNewUser یا /updateUser خودداری کنید. همچنین از ارسال عملیات منبع به‌عنوان یک پارامتر خودداری کنید.

  • عملیات‌های CRUD را با متدهای HTTP شرح دهید:

چگونه

  • متد GET: برای بازیابی نمایشی از یک منبع.
  • متد POST: برای ایجاد منابع و زیرمنابع جدید.
  • متد PUT: برای به‌روزرسانی منابع موجود.
  • متد PATCH: برای به‌روزرسانی منابع موجود. فقط فیلدهای ارائه‌شده را به‌روزرسانی می‌کند و بقیه را دست‌نخورده می‌گذارد.
  • متد DELETE: برای حذف منابع موجود.
  • برای منابع تو در تو (Nested Resources)، توصیه می‌شود رابطه بین آن‌ها را از طریق شناسه‌ها (id)، در ساختار URL منعکس کنید. به‌عنوان مثال، با استفاده از id ارتباط یک کارمند با شرکت را نشان دهید.

چرا

این رویکردی طبیعی برای قابل پیمایش کردن منابع است.

چگونه

  • درخواست GET /schools/2/students باید لیست تمام دانش‌آموزان مدرسه 2 را بازگرداند.
  • درخواست GET /schools/2/students/31 باید اطلاعات دانش‌آموز شماره 31 مدرسه 2 را بازگرداند.
  • درخواست DELETE /schools/2/students/31 باید دانش‌آموز شماره 31 مدرسه 2 را حذف کند.
  • درخواست PUT /schools/2/students/31 باید اطلاعات دانش‌آموز شماره 31 را به‌روزرسانی کند. از PUT روی مسیر منبع (resource) استفاده می‌کنیم نه روی مسیر مجموعه (collection).
  • درخواست POST /schools باید یک مدرسه جدید بسازد و جزئیات مدرسه تازه ایجاد شده را برگرداند. از POST روی آدرس مجموعه (collection) استفاده می‌کنیم.
  • از یک عدد ترتیبی ساده با پیشوند v برای نسخه‌دهی استفاده کنید (مثال: v1, v2) و این نسخه را در ابتدای URL قرار دهید تا بیشترین دامنه تأثیرگذاری را داشته باشد:
http://api.domain.com/v1/schools/3/students
bash

چرا

وقتی APIهای شما به‌طور عمومی برای اشخاص دیگر در دسترس هستند، انجام تغییرات ناسازگار (breaking changes)، می‌تواند باعث ایجاد اختلال در عملکرد محصولات یا خدماتی شود که از این APIهای استفاده می‌کنند. استفاده از نسخه‌دهی در URL می‌تواند از بروز چنین مشکلاتی جلوگیری کند. (توضیحات بیشتر …)

  • پیام‌های پاسخ (Response) باید به‌خوبی توصیف‌کننده باشند، به‌طوری‌که گیرنده بتواند به‌راحتی مفهوم آن‌ها را درک کند. یک پیام خطای خوب می‌تواند به این شکل باشد:
{
  "code": 1234,
	"message": "Something bad happened",
	"description": "More details"
}
json

یا برای خطاهای اعتبارسنجی:

{
	"code": 2314,
	"message": "Validation Failed",
	"errors": [
		{
			"code": 1233,
			"field": "email",
			"message": "Invalid email"
		},
		{
			"code": 1234,
			"field": "password",
			"message": "No password provided"
		}
	]
}
json

چرا

توسعه‌دهندگان در زمان‌هایی که نیاز به عیب‌یابی و حل مشکلات دارند، به پیام‌های خطای طراحی‌شده و واضح متکی هستند. این پیام‌ها می‌توانند اطلاعات کافی برای رفع سریع مشکلات ارائه دهند.

  • از این کدهای وضعیت HTTP (Status Code) برای توصیف وضعیت پاسخ‌ها استفاده کنید تا نشان دهید آیا همه چیز درست انجام شده، خطایی از سمت کلاینت رخ داده، یا مشکلی در API وجود دارد:

کدام‌ یک

  • کد 200 OK برای نشان دادن موفقیت عملیات‌های GET، PUT یا POST
  • کد 201 Created برای مواقعی که یک نمونه جدید ساخته شده است (مثلاً پس از ایجاد منبع با متد POST، کد 201 برگردانید)
  • کد 204 No Content برای نشان دادن موفقیت عملیات اما بدون داشتن محتوای بازگشتی (مثلاً بعد از موفقیت در DELETE)
  • کد 304 Not Modified برای کاهش حجم انتقال داده زمانی که گیرنده قبلاً نسخه کش‌شده را دارد
  • کد 400 Bad Request برای زمانی که درخواست پردازش نشده است، زیرا سرور متوجه منظور کلاینت نشده است. (درخواست کلاینت قابل پردازش نیست.)
  • کد 401 Unauthorized برای زمانی که درخواست شامل اعتبارنامه (Credentials) معتبر نیست و باید با اعتبارنامه جدید تکرار شود
  • کد 403 Forbidden زمانی که سرور درخواست را می‌فهمد ولی از انجام آن امتناع می‌کند
  • کد 404 Not Found زمانی که منبع درخواستی پیدا نشود
  • کد 500 Internal Server Error نشان می‌دهد درخواست معتبر بوده اما سرور به‌دلیل شرایط پیش‌بینی‌نشده قادر به انجام آن نیست

چرا

اکثر ارائه‌دهندگان API از مجموعه محدودی از کدهای HTTP استفاده می‌کنند. برای مثال، API سرویس Google GData تنها از 10 کد وضعیت، Netflix از ۹ کد، و Digg تنها از 8 کد وضعیت استفاده می‌کنند. البته این پاسخ‌ها دربردارنده اطلاعات بیشتری در بدنه (Body) هستند. بیش از 70 کد وضعیت HTTP وجود دارد اما استفاده از کدهای وضعیت متداول باعث می‌شود توسعه‌دهندگان بدون نیاز به جستجوی اطلاعات اضافی، راحت‌تر با API کار کنند. (توضیحات بیشتر …)

  • در پاسخ (Response)، تعداد کل منابع را اعلام کنید.

  • پارامترهای limit و offset را برای کنترل اندازه پاسخ (Response) پشتیبانی کنید.

  • توجه داشته باشید که ممکن است کاربر همیشه به نمایش کامل منبع نیاز نداشته باشد. برای محدود کردن فیلدهای موردنظر، از پارامتر fields استفاده کنید که مقادیر آن با کاما جدا می‌شوند:

GET /students?fields=id,name,age,class
bash
  • پیاده‌سازی قابلیت «صفحه‌بندی» (Pagination)، «فیلتر کردن» (Filtering) و «مرتب‌سازی» (Sorting) در ابتدای کار برای همه منابع ضروری نیست. اما منابعی که این قابلیت‌ها را دارند باید مستند (از طریق Document) شوند.

امنیت ای‌پی‌آی (API security)#

این‌ها برخی از بهترین روش‌های امنیتی پایه هستند:

  • از احراز هویت پایه (Basic Authentication) تنها در ارتباطات امن (HTTPS) استفاده کنید. توکن‌های احراز هویت (Authentication token) نباید در URL ارسال شوند:
GET /users/123?token=asdf....
bash

چرا

اطلاعات احراز هویت مانند شناسه کاربری و رمز عبور و یا توکن، حتی اگر کدگذاری شده باشد (Base64)، به‌عنوان متن ساده قابل خواندن است (زیرا قابل بازگشایی است) و امنیت کافی ندارد. بنابراین، روش احراز هویت پایه (Basic Auth) ایمن نیست. (توضیحات بیشتر …)

  • توکن‌ها باید در هر درخواست، از طریق هدر Authorization ارسال شوند:
Authorization: Bearer xxxxxx, Extra yyyyy
bash
  • مدت اعتبار کد احراز هویت (Authorization Code) باید کوتاه باشد.

  • هرگونه درخواست غیر ایمن (غیر TLS) را رد کرده و به آن پاسخ ندهید. به درخواست‌های HTTP با کد 403 Forbidden پاسخ دهید تا از هرگونه تبادل ناامن جلوگیری کنید.

  • محدودیت نرخ درخواست (Rate Limiting) را اعمال کنید.

چرا

برای محافظت از API در برابر ربات‌هایی که می‌توانند هزاران بار در ساعت درخواست ارسال کنند، بهتر است محدودیت نرخ درخواست‌ (Rate Limiting) را از همان ابتدای پیاده‌سازی مدنظر قرار دهید.

  • هدرهای HTTP را به‌درستی تنظیم کنید تا امنیت وب‌اپلیکیشن شما افزایش یابد. (اطلاعات بیشتر…)

  • داده‌های دریافت‌شده را به فرم استاندارد تبدیل کنید یا در صورت خطا یا نامعتبر بودن، آن درخواست را با کد وضعیت 400 Bad Request همراه با جزئیات خطا، رد کنید.

  • تمام داده‌هایی که با REST API مبادله می‌شوند، باید توسط خود همان API اعتبارسنجی شوند.

  • داده‌های JSON خود را سریال‌سازی (Serialize) کنید.

چرا

این کار از اجرای کدهای مخرب جاوااسکریپت که ممکن است از طریق داده‌های JSON ارسال شوند، جلوگیری می‌کند. در نتیجه بسیار مهم است که از سریالایزر معتبر JSON استفاده کنید تا مانع اجرای داده‌های ارسال‌شده از کاربر در مرورگر یا سرور شود.

  • نوع محتوا (Content-Type) را اعتبارسنجی کنید و عموماً از application/*json استفاده کنید.

چرا

به‌عنوان مثال، اگر application/x-www-form-urlencoded را بپذیرید، مهاجم می‌تواند با ساخت یک فرم ساده، درخواست POST ارسال کند. سرور نباید نوع محتوای ارسالی را حدس بزند. در صورت فقدان یا نامعتبر بودن هدر Content-Type، سرور باید آن درخواست را با کد 4XX رد کند.

مستندسازی ای‌پی‌آی (API documentation)#

  • بخش مرجع API را در قالب فایل README.md برای API تکمیل کنید

  • روش‌های احراز هویت API را به همراه یک نمونه کد توضیح دهید.

  • ساختار URL (فقط مسیر بدون دامنه اصلی) را توضیح دهید و همچنین نوع درخواست (Method) را مشخص کنید.

برای هر Endpoint، موارد زیر را توضیح دهید:

  • اگر پارامترهایی در URL وجود دارند، آن‌ها را مطابق نام ذکرشده در بخش URL مشخص کنید:
موارد اجباری: id=[integer]
موارد اختیاری: photo_id=[alphanumeric]
plaintext
  • در صورت استفاده از نوع درخواست POST، نمونه‌های عملی از درخواست را ارائه دهید. قوانین مربوط به پارامترهای URL برای این بخش نیز اعمال می‌شود. این قسمت باید به دو بخش اختیاری و ضروری تقسیم شود.

  • وضعیت کد (Status Code) و اطلاعات بازگشتیِ پاسخ موفقیت‌آمیز (Success Response) را مشخص کنید. این اطلاعات زمانی مفید است که کاربران نیاز دارند بدانند که چه چیزی را از پاسخ دریافت خواهند کرد:

Code: 200
Content: { id : 12 }
bash
  • اغلب endpointها ممکن است به دلایل مختلفی دچار خطا شوند (مانند دسترسی غیرمجاز یا پارامترهای نادرست). همه این خطاها باید در این بخش (API documentation) ذکر شوند. ممکن است تکراری به نظر برسد، اما به جلوگیری از فرضیات نادرست کمک می‌کند. به عنوان مثال:
{
	"code": 401,
	"message": "Authentication failed",
	"description": "Invalid username or password"
}
json
  • برای مستندسازی بهتر API، از ابزارهای متن‌باز متعددی که برای مستندسازی وجود دارند، استفاده کنید. ابزارهایی مانند API Blueprint و Swagger نمونه‌های خوبی هستند.