التقسيم إلى صفحات في واجهة مُجيب بالمؤشر nextCursor
التقسيم إلى صفحات في واجهة مُجيب البرمجية يتم بمؤشر (cursor): كل قائمة تعيد صفحة من النتائج ومعها nextCursor، فتمرّره في الطلب التالي باسم cursor حتى تصبح قيمته null. حجم الصفحة 50 عنصرًا، ويمكنك رفعه إلى 100.
يعمل هذا في القائمتين: GET /api/v1/contacts وGET /api/v1/bookings. أما طلبات العنصر الواحد فلا صفحات فيها.
كيف تعمل الصفحات في الواجهة؟
رابط القسم «كيف تعمل الصفحات في الواجهة؟»ترسل الطلب الأول دون cursor، فيعود الرد بالعناصر في data وبمؤشر الصفحة التالية في nextCursor:
{ "data": [ { "id": "5d8f2a61-0c4e-4b7a-9f13-6e2b8c0d4a57" }, { "id": "9b1e3c74-2a5d-4f08-8e6b-1c7d0a3f5e92" } ], "nextCursor": "MjAyNi0xMC0yOFQxNDowNToxMi40ODFafDliMWUzYzc0LTJhNWQtNGYwOC04ZTZiLTFjN2QwYTNmNWU5Mg"}(العناصر هنا مختصرة إلى id للتوضيح. شكلها الكامل في صفحة كل طلب.)
| المعامل | معناه |
|---|---|
limit | عدد العناصر في الصفحة، من واحد إلى 100. إذا لم ترسله فهو 50 |
cursor | قيمة nextCursor من الصفحة السابقة، كما هي حرفًا بحرف |
- عندما تكون
nextCursorقيمتهاnullفقد وصلت إلى آخر صفحة. - قد تأتي الصفحة الأخيرة بعناصر أقل من
limit، وقد تأتيdataفارغة إذا لم يطابق الطلب شيئًا. - المؤشر نص مغلق: لا تحاول قراءته أو تركيبه بنفسك. المؤشر الذي لا يُقرأ يعود بالخطأ 400 ورسالة
Invalid cursor.
كيف أقرأ كل الصفحات حتى النهاية؟
رابط القسم «كيف أقرأ كل الصفحات حتى النهاية؟»اطلب الصفحة، واجمع عناصرها، ثم اطلب التالية بمؤشرها، وتوقّف عندما يصبح المؤشر null. هذه دالة تفعل ذلك وتنتظر إذا وصلت إلى حدود الطلبات:
const BASE = 'https://api.mujib.ai/api/v1';const headers = { Authorization: `Bearer ${process.env.MUJIB_API_KEY}` };
async function fetchAll(path, params = {}) { const items = []; let cursor = null; for (;;) { const url = new URL(BASE + path); for (const [k, v] of Object.entries({ ...params, limit: 100 })) url.searchParams.set(k, String(v)); if (cursor) url.searchParams.set('cursor', cursor); const res = await fetch(url, { headers }); if (res.status === 429) { const wait = Number(res.headers.get('retry-after') ?? '60'); await new Promise((r) => setTimeout(r, wait * 1000)); continue; } const body = await res.json(); if (!res.ok) throw new Error(`${res.status} ${body.error.code}: ${body.error.message}`); items.push(...body.data); if (body.nextCursor === null) return items; cursor = body.nextCursor; }}
const bookings = await fetchAll('/bookings', { from: '2026-11-01', to: '2026-11-30' });console.log(bookings.length);import osimport timeimport requests
BASE = "https://api.mujib.ai/api/v1"HEADERS = {"Authorization": f"Bearer {os.environ['MUJIB_API_KEY']}"}
def fetch_all(path, params=None): items, cursor = [], None while True: query = {**(params or {}), "limit": 100} if cursor: query["cursor"] = cursor res = requests.get(BASE + path, params=query, headers=HEADERS, timeout=30) if res.status_code == 429: time.sleep(int(res.headers.get("Retry-After", "60"))) continue body = res.json() if not res.ok: raise RuntimeError(f"{res.status_code} {body['error']['code']}: {body['error']['message']}") items.extend(body["data"]) if body["nextCursor"] is None: return items cursor = body["nextCursor"]
contacts = fetch_all("/contacts", {"since": "2026-10-01T00:00:00Z"})print(len(contacts))بأي ترتيب تأتي النتائج؟
رابط القسم «بأي ترتيب تأتي النتائج؟»لكل قائمة ترتيب ثابت لا يتغيّر بين الصفحات:
| القائمة | الترتيب |
|---|---|
| العملاء | الأحدث أولًا، بحسب createdAt (أول ظهور للعميل)، ثم بالمعرّف id عند التساوي |
| الحجوزات | الأقدم أولًا، بحسب الوقت المحلي للموعد في فرعه (localTime)، ثم بالمعرّف id عند التساوي |
إذا كانت فروعك في مناطق زمنية مختلفة، فترتيب الحجوزات يتبع الوقت المحلي لكل فرع، لا اللحظة بتوقيت UTC.
هل يتغيّر شيء إذا أُضيفت بيانات أثناء القراءة؟
رابط القسم «هل يتغيّر شيء إذا أُضيفت بيانات أثناء القراءة؟»لا يتكرر عنصر قرأته ولا يُتخطّى عنصر بسبب إضافة جديدة، لأن المؤشر يحفظ موضعك بقيمة آخر عنصر قرأته لا برقم صفحة.
- عميل جديد أثناء القراءة يظهر في أول القائمة، فلا تراه في الصفحات التالية. تلتقطه في المزامنة التالية بالمعامل
since. - حجز جديد يقع وقته بعد موضعك يظهر في صفحة تالية، والذي يقع قبله تراه في القراءة التالية.
- حجز نُقل موعده أثناء القراءة ينتقل إلى موضعه الجديد في الترتيب، فقد تراه مرتين أو لا تراه. اعتمد على
idلتمنع التكرار.
كيف أجلب الجديد فقط بدل القائمة كلها؟
رابط القسم «كيف أجلب الجديد فقط بدل القائمة كلها؟»احفظ موضعك من المزامنة السابقة، واطلب ما بعده فقط.
- العملاء: احفظ أحدث قيمة
createdAtقرأتها، وأرسلها في المزامنة التالية باسمsince. المعامل يشمل اللحظة نفسها، فقد يعود آخر عميل قرأته مرة أخرى؛ تجاهله بمعرّفه. - الحجوزات: اطلب نافذة أيام بـ
fromوto، مثل الأيام السبعة القادمة. لا يوجد معامل لما تغيّر حديثًا، فإذا أردت أن تعرف بكل حجز جديد أو ملغى لحظة حدوثه فاستخدم Webhooks. - أبقِ معاملات الاختيار نفسها في كل صفحات القراءة الواحدة: المؤشر يحفظ الموضع فقط، ولا يحفظ
sinceأوfromأوtoأوstatus.
الأسئلة الشائعة
هل أستطيع طلب الصفحة الثالثة مباشرة؟
لا. لا توجد أرقام صفحات. تصل إلى كل صفحة بالمؤشر الذي أعادته الصفحة التي قبلها.
كيف أعرف أنني وصلت إلى آخر صفحة؟
عندما تكون قيمة nextCursor في الرد null. قد تأتي الصفحة الأخيرة بعناصر أقل من limit.
هل أستطيع تغيير limit بين صفحة وأخرى؟
نعم. المؤشر يحفظ موضعك لا حجم الصفحة. أما المعاملات التي تختار النتائج، مثل since وfrom وto وstatus، فأبقِها كما هي.
ما أكبر حجم للصفحة؟
100 عنصر. وإذا لم ترسل limit فحجم الصفحة 50.
مُجيب منتج شركة المجيب الذكي لتكنولوجيا المعلومات (ذات مسؤولية محدودة)، عمّان، الأردن. رقم التسجيل82888.