في عالم تطوير التطبيقات الحديثة، تعمل واجهات برمجة التطبيقات (APIs) من نوع REST كطبقة اتصال أساسية، مما يتيح للأنظمة المتباينة تبادل البيانات بسلاسة. مع نمو التطبيقات في الحجم والتعقيد، يزداد حجم البيانات التي تتعامل معها. طلب مجموعة بيانات كاملة، قد تحتوي على ملايين أو حتى مليارات السجلات، في استدعاء API واحد هو أمر غير فعال وغير موثوق به ويشكل عنق زجاجة كبير في الأداء. هنا تأتي تقنية حاسمة في تصميم وتطوير واجهات برمجة التطبيقات: ترقيم صفحات واجهات برمجة التطبيقات (REST API pagination). يقدم هذا الدليل نظرة عامة عميقة وشاملة حول تطبيق ترقيم الصفحات في واجهات برمجة التطبيقات REST، ويغطي كل شيء من المفاهيم الأساسية إلى التطبيقات المتقدمة في العالم الواقعي باستخدام مجموعات تقنيات متنوعة مثل Node.js و Python و .NET.
هل تريد منصة متكاملة وشاملة لفريق المطورين لديك للعمل معاً بأقصى إنتاجية؟
Apidog يلبي جميع متطلباتك، ويحل محل Postman بسعر معقول جداً!
أساسيات ترقيم صفحات واجهات برمجة التطبيقات REST
قبل الخوض في أمثلة الكود المعقدة وأنماط التصميم، من الضروري فهم ما هو ترقيم الصفحات ولماذا هو جانب غير قابل للتفاوض في تصميم واجهات برمجة التطبيقات الاحترافية.
ما هو ترقيم الصفحات في واجهات برمجة التطبيقات REST؟
في جوهره، ترقيم صفحات واجهات برمجة التطبيقات REST هو تقنية تستخدم لأخذ استجابة نقطة نهاية واجهة برمجة تطبيقات REST وتقسيمها إلى وحدات أصغر وأكثر قابلية للإدارة، وغالباً ما تسمى "صفحات". بدلاً من تقديم مجموعة بيانات ضخمة محتملة دفعة واحدة، تقوم واجهة برمجة التطبيقات بإرجاع جزء صغير يمكن التنبؤ به من البيانات. الأهم من ذلك، أن استجابة واجهة برمجة التطبيقات تتضمن أيضاً بيانات وصفية تسمح للعميل بجلب الأجزاء اللاحقة تدريجياً إذا احتاج إلى المزيد من البيانات.
هذه العملية تشبه صفحات الكتاب أو نتائج البحث على Google. يتم تقديم الصفحة الأولى من النتائج لك، بالإضافة إلى عناصر التحكم للانتقال إلى الصفحة الثانية، الثالثة، وهكذا. كما تشير مجتمعات المطورين مثل DEV Community ومنصات مثل Merge.dev، هذه هي عملية تقسيم مجموعة بيانات كبيرة إلى أجزاء أصغر، يمكن للعميل جلبها تدريجياً إذا كان يريد حقاً كل تلك البيانات. إنه مفهوم أساسي لبناء تطبيقات قوية وقابلة للتوسع.
لماذا يعتبر ترقيم الصفحات مطلباً أساسياً في تصميم واجهات برمجة التطبيقات الحديثة؟
الدافع الأساسي لترقيم الصفحات هو ضمان أن تكون استجابات واجهة برمجة التطبيقات أسهل في التعامل معها لكل من الخادم والعميل. بدونها، ستواجه التطبيقات قيوداً شديدة وتجربة مستخدم سيئة. تشمل الفوائد الرئيسية:
- تحسين الأداء وتقليل زمن الاستجابة: الميزة الأكثر أهمية هي السرعة. نقل حمولة JSON صغيرة تحتوي على 25 سجلاً أسرع بكثير من نقل حمولة تحتوي على 2.5 مليون سجل. هذا يؤدي إلى شعور سريع ومتجاوب للمستخدم النهائي.
- تعزيز موثوقية واجهة برمجة التطبيقات: الاستجابات الكبيرة لبروتوكول HTTP لديها احتمالية أعلى للفشل في منتصف النقل بسبب مهلات الشبكة، أو انقطاع الاتصالات، أو حدود ذاكرة العميل. يخلق ترقيم الصفحات طلبات أصغر وأكثر مرونة. إذا فشلت صفحة واحدة في التحميل، يمكن للعميل ببساطة إعادة محاولة هذا الطلب المحدد دون الحاجة إلى بدء نقل البيانات بالكامل من جديد.
- تقليل حمل الخادم: يمكن أن يؤدي إنشاء استجابة ضخمة إلى إجهاد كبير على موارد الخادم. قد يكون استعلام قاعدة البيانات بطيئاً، وتحويل ملايين السجلات إلى JSON يستهلك قدراً كبيراً من وحدة المعالجة المركزية والذاكرة. يسمح ترقيم الصفحات للخادم بأداء استعلامات أصغر وأكثر كفاءة، مما يحسن قدرته الإجمالية وقدرته على خدمة العديد من العملاء في وقت واحد.
- معالجة فعالة من جانب العميل: بالنسبة لتطبيقات العميل، خاصة تلك التي تعمل على الأجهزة المحمولة أو في متصفح الويب، يمكن أن يؤدي تحليل كائن JSON ضخم إلى تجميد واجهة المستخدم ويؤدي إلى تجربة محبطة. أجزاء البيانات الأصغر أسهل في التحليل والعرض، مما يؤدي إلى تطبيق أكثر سلاسة.
استراتيجيات وتقنيات ترقيم الصفحات الشائعة
هناك عدة طرق لتطبيق ترقيم الصفحات، ولكن استراتيجيتين رئيسيتين أصبحتا المعايير الفعلية في الصناعة. الاختيار بينهما له آثار كبيرة على الأداء، اتساق البيانات، وتجربة المستخدم.
ترقيم الصفحات القائم على الإزاحة (Offset): النهج الأساسي
ترقيم الصفحات القائم على الإزاحة (Offset)، والذي غالباً ما يسمى "ترقيم الصفحات برقم الصفحة"، هو غالباً أول نهج يتعلمه المطورون. إنه بسيط من الناحية المفاهيمية ويظهر في العديد من تطبيقات الويب. يعمل باستخدام معلمتين رئيسيتين:
limit(أوpage_size): الحد الأقصى لعدد النتائج التي سيتم إرجاعها في صفحة واحدة.offset(أوpage): عدد السجلات التي سيتم تخطيها من بداية مجموعة البيانات. إذا تم استخدام معلمةpage، يتم حساب الإزاحة عادةً كـ(page - 1) * limit.
يبدو الطلب النموذجي كما يلي: GET /api/products?limit=25&offset=50
سيترجم هذا إلى استعلام SQL مثل:SQL
SELECT * FROM products ORDER BY created_at DESC LIMIT 25 OFFSET 50;
يقوم هذا الاستعلام بتخطي أول 50 منتجاً واسترداد الـ 25 التالية (أي المنتجات من 51 إلى 75).
الإيجابيات:
- البساطة: هذه الطريقة سهلة التنفيذ، كما هو موضح في العديد من الدروس التعليمية مثل "Node.js REST API: Offset Pagination Made Easy".
- التنقل عديم الحالة (Stateless): يمكن للعميل الانتقال بسهولة إلى أي صفحة في مجموعة البيانات دون الحاجة إلى معلومات سابقة، مما يجعلها مثالية لواجهات المستخدم التي تحتوي على روابط صفحات مرقمة.
السلبيات والقيود:
- ضعف الأداء على مجموعات البيانات الكبيرة: العيب الأساسي هو بند
OFFSETفي قاعدة البيانات. لطلب بإزاحة كبيرة (مثلOFFSET 1000000)، لا تزال قاعدة البيانات مضطرة لجلب جميع السجلات البالغ عددها 1,000,025 من القرص، والعد خلال المليون الأولى لتخطيها، وفقط بعد ذلك تقوم بإرجاع الـ 25 الأخيرة. يمكن أن يصبح هذا بطيئاً بشكل لا يصدق مع زيادة رقم الصفحة. - عدم اتساق البيانات (Page Drift): إذا تمت كتابة سجلات جديدة إلى قاعدة البيانات أثناء قيام المستخدم بترقيم الصفحات، فإن مجموعة البيانات بأكملها تتحرك. قد يرى المستخدم الذي يتنقل من الصفحة 2 إلى الصفحة 3 سجلاً مكرراً من نهاية الصفحة 2، أو يفقد سجلاً تماماً. هذه مشكلة كبيرة للتطبيقات في الوقت الفعلي وهي موضوع شائع في منتديات المطورين مثل Stack Overflow عند مناقشة كيفية ضمان اتساق البيانات.
ترقيم الصفحات القائم على المؤشر (Cursor) (Keyset): الحل القابل للتوسع
ترقيم الصفحات القائم على المؤشر (Cursor)، المعروف أيضاً باسم keyset أو seek pagination، يحل مشاكل الأداء والاتساق لطريقة الإزاحة. بدلاً من رقم الصفحة، يستخدم "مؤشراً"، وهو مؤشر مستقر وغير شفاف لسجل معين في مجموعة البيانات.
التدفق هو كما يلي:
- يقوم العميل بطلب أولي لصفحة بيانات.
- يقوم الخادم بإرجاع صفحة البيانات، بالإضافة إلى مؤشر يشير إلى العنصر الأخير في تلك المجموعة.
- للصفحة التالية، يرسل العميل هذا المؤشر مرة أخرى إلى الخادم.
- يقوم الخادم بعد ذلك باسترداد السجلات التي تأتي بعد هذا المؤشر المحدد، مما يؤدي فعلياً إلى "البحث" عن تلك النقطة في مجموعة البيانات.
المؤشر عادةً ما يكون قيمة مشفرة مشتقة من العمود (الأعمدة) التي يتم الفرز عليها. على سبيل المثال، إذا كان الفرز حسب created_at (طابع زمني)، يمكن أن يكون المؤشر هو الطابع الزمني للسجل الأخير. للتعامل مع التساوي، غالباً ما يتم تضمين عمود ثانٍ فريد (مثل id السجل).
يبدو الطلب الذي يستخدم مؤشراً كما يلي: GET /api/products?limit=25&after_cursor=eyJjcmVhdGVkX2F0IjoiMjAyNS0wNi0wN1QxODowMDowMC4wMDBaIiwiaWQiOjg0N30=
سيترجم هذا إلى استعلام SQL أكثر كفاءة بكثير:SQL
SELECT * FROM products
WHERE (created_at, id) < ('2025-06-07T18:00:00.000Z', 847)
ORDER BY created_at DESC, id DESC
LIMIT 25;
يستخدم هذا الاستعلام فهرساً على (created_at, id) "للبحث" فوراً عن نقطة البداية الصحيحة، متجنباً مسح جدول كامل وجعله سريعاً باستمرار بغض النظر عن مدى عمق ترقيم المستخدم للصفحات.
الإيجابيات:
- عالي الأداء وقابل للتوسع: أداء قاعدة البيانات سريع وثابت، مما يجعله مناسباً لمجموعات البيانات بأي حجم.
- اتساق البيانات: نظراً لأن المؤشر مرتبط بسجل معين، وليس بموضع مطلق، فإن البيانات الجديدة التي تتم إضافتها أو إزالتها لن تتسبب في فقدان أو تكرار العناصر بين الصفحات.
السلبيات:
- تعقيد التنفيذ: منطق إنشاء وتحليل المؤشرات أكثر تعقيداً من حساب إزاحة بسيط.
- التنقل المحدود: يمكن للعميل التنقل فقط إلى الصفحة "التالية" أو "السابقة". ليس من الممكن الانتقال مباشرة إلى رقم صفحة محدد، مما يجعله أقل ملاءمة لأنماط واجهة المستخدم المعينة.
- يتطلب مفتاح فرز مستقر: التنفيذ مرتبط ارتباطاً وثيقاً بترتيب الفرز ويتطلب عموداً فريداً ومتسلسلاً واحداً على الأقل.
مقارنة بين نوعي ترقيم الصفحات الرئيسيين
يعتمد الاختيار بين ترقيم الصفحات بالإزاحة والمؤشر بالكامل على حالة الاستخدام.
| الميزة | ترقيم الصفحات بالإزاحة | ترقيم الصفحات بالمؤشر |
| الأداء | ضعيف للصفحات العميقة في مجموعات البيانات الكبيرة. | ممتاز وثابت على أي عمق. |
| اتساق البيانات | عرضة لفقدان/تكرار البيانات (انحراف الصفحة). | عالي؛ البيانات الجديدة لا تؤثر على ترقيم الصفحات. |
| التنقل | يمكن الانتقال إلى أي صفحة. | محدود بالصفحات التالية/السابقة. |
| التنفيذ | بسيط ومباشر. | أكثر تعقيداً؛ يتطلب منطق المؤشر. |
| حالة الاستخدام المثالية | مجموعات البيانات الصغيرة والثابتة؛ واجهات مستخدم الإدارة. | موجزات التمرير اللانهائي؛ مجموعات البيانات الكبيرة والديناميكية. |
أفضل الممارسات لتطبيق ترقيم الصفحات من جانب الخادم
بغض النظر عن الاستراتيجية المختارة، الالتزام بمجموعة من أفضل الممارسات سيؤدي إلى واجهة برمجة تطبيقات نظيفة، يمكن التنبؤ بها، وسهلة الاستخدام. هذا غالباً ما يكون جزءاً أساسياً من الإجابة على السؤال "ما هي أفضل ممارسة لترقيم الصفحات من جانب الخادم؟".
تصميم حمولة استجابة ترقيم الصفحات
خطأ شائع هو إرجاع مجرد مصفوفة من النتائج. يجب أن تكون حمولة استجابة ترقيم الصفحات المصممة جيداً عبارة عن كائن "يغلف" البيانات ويتضمن بيانات وصفية واضحة لترقيم الصفحات.JSON
{
"data": [
{ "id": 101, "name": "Product A" },
{ "id": 102, "name": "Product B" }
],
"pagination": {
"next_cursor": "eJjcmVhdGVkX2F0Ij...",
"has_next_page": true
}
}
بالنسبة لترقيم الصفحات بالإزاحة، ستبدو البيانات الوصفية مختلفة:JSON
{
"data": [
// ... results
],
"metadata": {
"total_results": 8452,
"total_pages": 339,
"current_page": 3,
"per_page": 25
}
}
هذا الهيكل يجعل من السهل جداً على العميل معرفة ما إذا كان هناك المزيد من البيانات لجلبها أو لعرض عناصر تحكم واجهة المستخدم.
استخدام روابط الوسائط الفائقة للتنقل (HATEOAS)
مبدأ أساسي في REST هو HATEOAS (Hypermedia as the Engine of Application State). هذا يعني أن واجهة برمجة التطبيقات يجب أن توفر للعملاء روابط للانتقال إلى موارد أو إجراءات أخرى. بالنسبة لترقيم الصفحات، هذا قوي بشكل لا يصدق. كما هو موضح في توثيق GitHub، طريقة موحدة للقيام بذلك هي باستخدام ترويسة HTTP Link.
Link: <https://api.example.com/items?page=3>; rel="next", <https://api.example.com/items?page=1>; rel="prev"
بدلاً من ذلك، يمكن وضع هذه الروابط مباشرة في نص استجابة JSON، وهو غالباً ما يكون أسهل لعملاء JavaScript استهلاكه:JSON"pagination": { "links": { "next": "https://api.example.com/items?limit=25&offset=75", "previous": "https://api.example.com/items?limit=25&offset=25" } }
هذا يحرر العميل من الحاجة إلى بناء عناوين URL يدوياً.
السماح للعملاء بالتحكم في حجم الصفحة
من الممارسات الجيدة السماح للعملاء بطلب صفحات إضافية من النتائج للاستجابات المرقّمة وأيضاً بتغيير عدد النتائج التي يتم إرجاعها في كل صفحة. يتم ذلك عادةً باستخدام معلمة استعلام limit أو per_page. ومع ذلك، يجب على الخادم دائماً فرض حد أقصى معقول (مثل 100) لمنع العملاء من طلب الكثير من البيانات دفعة واحدة وإثقال كاهل النظام.
الجمع بين ترقيم الصفحات والتصفية والفرز
نادرًا ما تقوم واجهات برمجة التطبيقات في العالم الواقعي بترقيم الصفحات فقط؛ بل تحتاج أيضاً إلى دعم التصفية والفرز. كما هو موضح في الدروس التعليمية التي تغطي تقنيات مثل .NET، إضافة هذه الميزات هو مطلب شائع.
قد يبدو الطلب المعقد كما يلي: GET /api/products?status=published&sort=-created_at&limit=50&page=2
عند تنفيذ ذلك، من الضروري اعتبار معلمات التصفية والفرز جزءاً من منطق ترقيم الصفحات. يجب أن يكون ترتيب sort مستقراً وحتمياً لكي يعمل ترقيم الصفحات بشكل صحيح. إذا كان ترتيب الفرز غير فريد، يجب إضافة عمود فريد ثانٍ (مثل id) للتعامل مع التساوي وضمان ترتيب متسق بين الصفحات.
أمثلة تطبيقية من العالم الواقعي
دعونا نستكشف كيفية تطبيق هذه المفاهيم في مختلف الأطر الشهيرة.
ترقيم صفحات واجهات برمجة التطبيقات REST في Python باستخدام Django REST Framework
أحد أكثر المجموعات شيوعاً لبناء واجهات برمجة التطبيقات هو Python مع Django REST Framework (DRF). يوفر DRF دعماً قوياً ومدمجاً لترقيم الصفحات، مما يجعله سهلاً للغاية للبدء. يقدم فئات لاستراتيجيات مختلفة:
PageNumberPagination: لترقيم الصفحات القياسي القائم على رقم الصفحة (إزاحة).LimitOffsetPagination: لتطبيق إزاحة أكثر مرونة.CursorPagination: لترقيم الصفحات عالي الأداء القائم على المؤشر.
يمكنك تكوين نمط ترقيم صفحات افتراضي عالمياً ثم ببساطة استخدام ListAPIView عام، ويتولى DRF الباقي. هذا مثال رئيسي على ترقيم صفحات واجهة برمجة تطبيقات Rest بلغة Python.Python
# In your settings.py
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.CursorPagination',
'PAGE_SIZE': 50
}
# In your views.py
class ProductListView(generics.ListAPIView):
queryset = Product.objects.all()
serializer_class = ProductSerializer
# DRF handles the entire pagination logic automatically!
بناء واجهة برمجة تطبيقات REST مرقّمة الصفحات باستخدام Node.js و Express و TypeScript
في بيئة Node.js، غالباً ما تبني منطق ترقيم الصفحات يدوياً، مما يمنحك تحكماً كاملاً. يقدم هذا القسم من الدليل نظرة عامة مفاهيمية حول بناء ترقيم الصفحات باستخدام Node.js و Express و TypeScript.
إليك مثال مبسط لتطبيق ترقيم الصفحات بالمؤشر:TypeScript
// In your Express controller
app.get('/products', async (req: Request, res: Response) => {
const limit = parseInt(req.query.limit as string) || 25;
const cursor = req.query.cursor as string;
let query = db.selectFrom('products').orderBy('createdAt', 'desc').orderBy('id', 'desc').limit(limit);
if (cursor) {
const { createdAt, id } = JSON.parse(Buffer.from(cursor, 'base64').toString('ascii'));
// Add the WHERE clause for the cursor
query = query.where('createdAt', '<=', createdAt).where('id', '<', id);
}
const products = await query.execute();
const nextCursor = products.length > 0
? Buffer.from(JSON.stringify({
createdAt: products[products.length - 1].createdAt,
id: products[products.length - 1].id
})).toString('base64')
: null;
res.json({
data: products,
pagination: { next_cursor: nextCursor }
});
});
ترقيم الصفحات في بيئة Java أو .NET
توفر الأطر في البيئات الأخرى أيضاً دعماً قوياً لترقيم الصفحات.
- Java (Spring Boot): يجعل مشروع Spring Data ترقيم الصفحات أمراً بسيطاً. باستخدام
PagingAndSortingRepository، يمكنك تعريف توقيع دالة مثلPage<Product> findAll(Pageable pageable);. يقوم Spring تلقائياً بتنفيذ الدالة، ويتعامل مع معلمات الطلبpageوsizeوsort، ويرجع كائنPageيحتوي على النتائج وجميع البيانات الوصفية اللازمة لترقيم الصفحات. هذه إجابة على أفضل الممارسات للسؤال "كيفية تطبيق ترقيم الصفحات في واجهة برمجة تطبيقات Java REST؟". - .NET: في عالم .NET، غالباً ما يستخدم المطورون امتدادات
IQueryableمع دوال مثل.Skip()و.Take()لتطبيق ترقيم الصفحات بالإزاحة. لسيناريوهات أكثر تقدماً، يمكن للمكتبات المساعدة في بناء حلول قائمة على المؤشر تترجم إلى استعلامات SQL فعالة.
### حالة استخدام من العالم الواقعي: ترقيم صفحات واجهة برمجة تطبيقات كتالوج المنتجات
فكر في موقع ويب للتجارة الإلكترونية يحتوي على "واجهة برمجة تطبيقات كتالوج المنتجات". هذه حالة استخدام حقيقية مثالية. الكتالوج كبير وديناميكي، مع إضافة منتجات جديدة بشكل متكرر.
- المشكلة: إذا كان الموقع يستخدم ترقيم الصفحات بالإزاحة لقائمة منتجاته، وتمت إضافة منتج جديد أثناء تصفح العميل من الصفحة 1 إلى الصفحة 2، فقد يرى العميل المنتج الأخير من الصفحة 1 مكرراً في أعلى الصفحة 2. هذه تجربة مستخدم مربكة.
- الحل: تطبيق ترقيم الصفحات القائم على المؤشر هو الحل المثالي. سيمرر زر "تحميل المزيد" في الواجهة الأمامية مؤشر المنتج الأخير المرئي. ستقوم واجهة برمجة التطبيقات بعد ذلك بإرجاع المجموعة التالية من المنتجات بعد ذلك المنتج المحدد، مما يضمن أن القائمة تنمو ببساطة دون أي تكرارات أو عناصر مفقودة للمستخدم.
مواضيع متقدمة ومشاكل شائعة
كما يكتشف المطورون غالباً على Stack Overflow و Reddit، يتطلب بناء نظام ترقيم صفحات قوي حقاً التعامل مع العديد من التفاصيل وحالات الحافة.
كيفية ضمان اتساق البيانات في واجهة برمجة تطبيقات مرقّمة الصفحات
هذا أحد أهم المواضيع المتقدمة. كما تمت مناقشته، الطريقة الوحيدة الموثوقة لضمان اتساق البيانات في نظام يتضمن كتابات متكررة هي استخدام ترقيم الصفحات keyset/cursor. تصميمه يمنع بطبيعته انحراف الصفحة. إذا كنت مضطراً لسبب ما إلى استخدام ترقيم الصفحات بالإزاحة، توجد بعض الحلول المعقدة، مثل إنشاء لقطة مؤقتة غير قابلة للتغيير لمعرفات مجموعة النتائج الكاملة وترقيم الصفحات من خلال تلك القائمة، ولكن هذا يعتمد بشكل كبير على الحالة وغير موصى به بشكل عام لواجهات برمجة التطبيقات REST.
التعامل مع حالات الحافة الغريبة
يجب أن تتعامل واجهة برمجة التطبيقات الجاهزة للإنتاج بشكل سلس مع المدخلات السيئة. ضع في اعتبارك حالات الحافة الشائعة هذه:
- يطلب العميل
page=0أوoffset=-50. يجب ألا ترجع واجهة برمجة التطبيقات خطأ 500. يجب أن ترجع400 Bad Requestمع رسالة خطأ واضحة. - يقدم العميل
cursorمشوهاً أو غير صالح. يجب أن ترجع واجهة برمجة التطبيقات مرة أخرى400 Bad Request. - يقدم العميل مؤشراً صالحاً، ولكن العنصر الذي يشير إليه تم حذفه. استراتيجية جيدة هي التعامل مع المؤشر على أنه يشير إلى "المساحة" التي كان فيها ذلك العنصر وإرجاع الصفحة التالية من النتائج من تلك النقطة.
التطبيق من جانب العميل
جانب العميل هو المكان الذي يتم فيه استهلاك منطق ترقيم الصفحات. استخدام JavaScript لجلب البيانات المرقّمة من واجهة برمجة تطبيقات REST باحترافية يتضمن قراءة بيانات ترقيم الصفحات الوصفية واستخدامها لإجراء طلبات لاحقة.
إليك مثال بسيط لـ fetch لزر "تحميل المزيد" باستخدام ترقيم الصفحات بالمؤشر:JavaScript
const loadMoreButton = document.getElementById('load-more');
let nextCursor = null; // Store the cursor globally or in component state
async function fetchProducts(cursor) {
const url = cursor ? `/api/products?cursor=${cursor}` : '/api/products';
const response = await fetch(url);
const data = await response.json();
// ... render the new products ...
nextCursor = data.pagination.next_cursor;
if (!nextCursor) {
loadMoreButton.disabled = true; // No more pages
}
}
loadMoreButton.addEventListener('click', () => fetchProducts(nextCursor));
// Initial load
fetchProducts(null);
مستقبل استرجاع بيانات واجهة برمجة التطبيقات ومعايير ترقيم الصفحات
بينما كان REST مهيمناً لسنوات، فإن المشهد يتطور باستمرار.
تطور معايير ترقيم صفحات واجهات برمجة التطبيقات REST
لا يوجد RFC رسمي واحد يحدد معايير ترقيم صفحات واجهات برمجة التطبيقات REST. ومع ذلك، ظهرت مجموعة من الاتفاقيات القوية، مدفوعة بواجهات برمجة التطبيقات العامة لشركات التكنولوجيا الكبرى مثل GitHub و Stripe و Atlassian. أصبحت هذه الاتفاقيات، مثل استخدام ترويسة Link وتوفير بيانات وصفية واضحة، هي المعيار الفعلي. الاتساق هو المفتاح؛ منصة واجهة برمجة تطبيقات مصممة جيداً ستستخدم نفس استراتيجية ترقيم الصفحات عبر جميع نقاط النهاية القائمة على القوائم.
تأثير GraphQL على ترقيم الصفحات
يقدم GraphQL نموذجاً مختلفاً. بدلاً من نقاط نهاية متعددة، لديه نقطة نهاية واحدة يرسل إليها العملاء استعلامات معقدة تحدد البيانات الدقيقة التي يحتاجونها. ومع ذلك، فإن الحاجة إلى ترقيم قوائم البيانات الكبيرة لا تختفي. لقد قام مجتمع GraphQL أيضاً بتوحيد ترقيم الصفحات القائم على المؤشر من خلال مواصفات رسمية تسمى Relay Cursor Connections Spec. تحدد هذه المواصفات هيكلاً دقيقاً لترقيم البيانات، باستخدام مفاهيم مثل first و after و last و before لتوفير ترقيم صفحات قوي للأمام والخلف.
الخلاصة: ملخص لأفضل ممارسات ترقيم الصفحات
إتقان ترقيم صفحات واجهات برمجة التطبيقات REST هو مهارة حاسمة لأي مطور خلفي. إنها تقنية أساسية لبناء تطبيقات قابلة للتوسع وعالية الأداء وسهلة الاستخدام.
لتلخيص أفضل ممارسات ترقيم صفحات واجهات برمجة التطبيقات REST:
- قم دائماً بترقيم الصفحات: لا ترجع أبداً قائمة غير محدودة من النتائج من نقطة نهاية واجهة برمجة التطبيقات.
- اختر الاستراتيجية الصحيحة: استخدم ترقيم الصفحات بالإزاحة البسيط لمجموعات البيانات الصغيرة، غير الحرجة، أو الثابتة. لأي شيء كبير، ديناميكي، أو موجه للمستخدم، فضل بشدة ترقيم الصفحات القائم على المؤشر لأدائه الفائق واتساق البيانات.
- قدم بيانات وصفية واضحة: يجب أن تتضمن حمولة الاستجابة الخاصة بك دائماً معلومات تخبر العميل بكيفية الحصول على الصفحة التالية من البيانات، سواء كان ذلك
next_cursorأو أرقام الصفحات والروابط. - استخدم الوسائط الفائقة (Hypermedia): استخدم ترويسة
Linkأو الروابط داخل نص JSON الخاص بك لجعل واجهة برمجة التطبيقات الخاصة بك أكثر قابلية للاكتشاف وأسهل في الاستخدام. - تعامل مع الأخطاء بلطف: تحقق من صحة جميع معلمات ترقيم الصفحات وأرجع أخطاء
400 Bad Requestواضحة للمدخلات غير الصالحة.
باتباع هذا الدليل واستيعاب هذه المبادئ، يمكنك تصميم وبناء واجهات برمجة تطبيقات REST احترافية وجاهزة للإنتاج يمكنها التوسع بفعالية لتلبية أي طلب.
الأسئلة الشائعة حول ترقيم صفحات واجهات برمجة التطبيقات REST
1. ما هو الفرق الرئيسي بين ترقيم الصفحات بالإزاحة والمؤشر؟
يكمن الفرق الرئيسي في كيفية تحديد أي مجموعة بيانات يتم استردادها. يستخدم ترقيم الصفحات بالإزاحة إزاحة رقمية (مثل "تخطي أول 50 عنصراً") للعثور على الصفحة التالية. يمكن أن يكون هذا بطيئاً لمجموعات البيانات الكبيرة لأن قاعدة البيانات لا تزال مضطرة للعد خلال العناصر التي يتم تخطيها. يستخدم ترقيم الصفحات بالمؤشر مؤشراً ثابتاً أو "مؤشراً" يشير إلى سجل معين (مثل "احصل على العناصر بعد معرف المنتج 857"). هذا أكثر كفاءة بكثير لأن قاعدة البيانات يمكنها استخدام فهرس للانتقال مباشرة إلى ذلك السجل.
2. متى يكون من المناسب استخدام ترقيم الصفحات بالإزاحة بدلاً من ترقيم الصفحات بالمؤشر؟
ترقيم الصفحات بالإزاحة مناسب لمجموعات البيانات الصغيرة، غير الحرجة من حيث الأداء، أو التي لا تتغير كثيراً. ميزته الأساسية هي البساطة والقدرة للمستخدم على الانتقال إلى أي رقم صفحة محدد (مثل "اذهب إلى الصفحة 10"). هذا يجعله مناسباً لأشياء مثل لوحات تحكم الإدارة أو الأدوات الداخلية حيث تكون تجربة المستخدم في الانتقال بين الصفحات أكثر أهمية من التعامل مع تغييرات البيانات في الوقت الفعلي.
3. كيف يمنع ترقيم الصفحات القائم على المؤشر مشكلة تخطي أو تكرار العناصر؟
يمنع ترقيم الصفحات القائم على المؤشر عدم اتساق البيانات لأنه يربط الطلب التالي بعنصر محدد، وليس بموضع رقمي. على سبيل المثال، إذا طلبت الصفحة بعد العنصر ذي ID=100، فلا يهم إذا تمت إضافة عناصر جديدة قبله؛ سيظل الاستعلام يبدأ الجلب من المكان الصحيح. مع ترقيم الصفحات بالإزاحة، إذا تمت إضافة عنصر جديد إلى الصفحة 1 أثناء عرضها، عند طلب الصفحة 2، سيصبح العنصر الأخير من الصفحة 1 هو العنصر الأول في الصفحة 2، مما يتسبب في تكرار.
4. هل يوجد معيار رسمي لاستجابات ترقيم صفحات واجهات برمجة التطبيقات REST؟
لا يوجد RFC رسمي واحد أو معيار رسمي يفرض كيفية تطبيق جميع ترقيم صفحات واجهات برمجة التطبيقات REST. ومع ذلك، ظهرت اتفاقيات قوية وأفضل الممارسات من الصناعة، وضعتها إلى حد كبير واجهات برمجة التطبيقات العامة الرئيسية مثل تلك من GitHub و Stripe. تشمل هذه الاتفاقيات استخدام ترويسة HTTP Link مع سمات rel="next" و rel="prev"، أو تضمين كائن pagination مع بيانات وصفية واضحة وروابط مباشرة في نص استجابة JSON.
5. كيف يجب أن أتعامل مع الفرز والتصفية مع نقاط النهاية المرقّمة؟
يجب تطبيق الفرز والتصفية قبل ترقيم الصفحات. يجب أن تكون النتائج المرقّمة "عرضاً" لمجموعة البيانات التي تم فرزها وتصفيتها بالفعل. من الضروري أن يكون ترتيب الفرز مستقراً وحتمياً. إذا قام المستخدم بالفرز حسب حقل غير فريد (مثل التاريخ)، يجب إضافة مفتاح فرز ثانوي فريد (مثل id السجل) ليكون بمثابة فاصل للتساوي. هذا يضمن أن ترتيب العناصر هو نفسه دائماً، وهو أمر أساسي لكي يعمل ترقيم الصفحات بالإزاحة والمؤشر بشكل صحيح.
هل تريد منصة متكاملة وشاملة لفريق المطورين لديك للعمل معاً بأقصى إنتاجية؟
Apidog يلبي جميع متطلباتك، ويحل محل Postman بسعر معقول جداً!
