✍️مرور چند روش رایج صفحه‌بندی (Pagination) در API Design

✍️مرور چند روش رایج صفحه‌بندی (Pagination) در API Design


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


و این خیلی مهم می‌شه که روش استاندارد و یکسانی داشته باشیم تا یکی شماره صفحه و تعداد رو توی GET نگیره یکی توی POST تا بتونیم هم جلو بار اضافی به سرور رو بگیریم هم کاربری یا توسعه‌دهنده‌ای که با API سر و کار داره دیوانه نشه!


1️⃣ روش Offset & Limit (یا Page & Size)


GET /users?limit=10&offset=20

این روش ساده و سرراسته، و امکان دسترسی مستقیم به صفحه دلخواه رو فراهم می‌کنه. ولی وقتی دیتاست خیلی بزرگ شه، مستعد درخواست‌های عمدی یا سهوی کند کننده است! یا اگر داده‌ها مرتب تغییر کنن، شما یا یه چیزی از قلم می‌ندازی یا تا بری صفحه ۲، دیتای صفحه ۱ افتاده توی صفحه ۲! (نرخ بالای تغییرات) کوئری‌اش هم اینجوریه:

-- SQL Server:

SELECT * FROM users

ORDER BY id

OFFSET 20 ROWS

FETCH NEXT 10 ROWS ONLY;


-- PostgreSQL:

SELECT * FROM users

ORDER BY id

LIMIT 10 OFFSET 20;


-- MongoDB:

db.users.find()

.sort({ id: 1 })

.skip(20)

.limit(10);


2️⃣ روش Cursor-based Pagination

توی این روش به جای استفاده از offset، از یک نشانگر (cursor) استفاده می‌کنیم که به عنوان مرجع برای ادامه‌ی داده‌ها عمل می‌کنه. معمولاً این cursor می‌تونه آخرین مقدار یک فیلد کلیدی مثل id یا تاریخ ایجاد باشه.

GET /users?limit=10&cursor=eyJpZCI6IDIwMH0=

این روش کارایی بهتری توی داده‌های بزرگ داره، یکی از دلایلش اینه که نیازی سورت کردن داده و بعدش رد کردن تعدادی رکورد نداره! بلکه مستقیم (با کمک ایندکس البته) می‌ره سراغ رکورد مورد نظر و تامام. اگر داده‌ها مرتب تغییر کنن، باز کاربر مسیر خودش رو پیمایش می‌کنه و داده‌های تکراری نمی‌بینه یا داده‌هایی رو از دست نمی‌ده.

ولی: پیچیدگی پیاده‌سازیش بیشتره. صفحه ۵ الزامان برای همه و در همه زمان‌ها یک داده‌ رو نشون نمی‌ده.

برای مقیاس بزرگ این روش مناسب‌تره، اون بار تحمیلی سورت و skip توی مقیاس بزرگ کمرشکنه!


3️⃣روش Seek-pagination (Keyset Pagination)

روش seek-pagination یا keyset pagination به روش cursor-based شباهت داره، ولی به صورت صریح از شرایط WHERE استفاده می‌کنه تا رکوردهایی که بعد از آخرین مقدار دیده شده قرار دارن رو برگردونه.

GET /orders?limit=10&last_id=1000

در اینجا فرض می‌کنیم که last_id نشان‌دهنده‌ی آخرین id دیده شده در صفحه قبلی هست. سرور از شرط WHERE id > 1000 استفاده می‌کنه تا رکوردهای بعدی رو برگردونه.


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



4️⃣روش Time-Based Pagination

GET /events?since=2023-10-01&until=2023-10-08


5️⃣روش Hypermedia (HATEOAS) Links

Link: ; rel="next", ; rel="prev"


6️⃣روش Metadata in Responses

{

"data": [...],

"pagination": {

"total": 1000,

"next_cursor": "def456",

"has_more": true

}

}


💬 اولندش بحث؟ نظر؟ تجربه؟ دُوُمَندش ۴ و ۵ و ۶ رو توضیح ندادم که الکی مثلا کنجکاوی ایجاد کنم، هزاران و میلیان‌ها ری‌اکشن ⚙️ بدید که توضیح بدم و بگم چرا روش HATEOAS روش منطبق با REST principles ذیل discoverability است!! 😅 سِوُمَندش دون بپاشم برای پرداختن عمیق‌تر به اهمیت ایندکس و درک ساختاریش، بعد از یخورده مطلب حول طراحی API نوشتم...


#API_Design