معالجة الأخطاء
التقط الاستثناءات من فئات أخطاء listingsapi-js، واقرأ رموز أخطاء المنصة، وأنشئ آلية لتأخير إعادة المحاولة تراعي retryAfter.
كل خطأ تطرحه حزمة SDK مشتق من ListingsAPIError. تؤدي حالات فشل API إلى طرح APIError أو إحدى فئاته الفرعية، سواء أبلغت عنها المنصة برمز حالة HTTP خارج النطاق 2xx أو ضمن جسم استجابة HTTP 200. تؤدي حالات فشل الشبكة (أخطاء DNS ورفض الاتصالات وانتهاء المهلة) إلى طرح APIConnectionError. لا تحتاج أبدًا إلى فحص علامات success أو الحمولات الخام: اكتمال promise بنجاح يعني نجاح الاستدعاء.
التسلسل الهرمي للأخطاء
ListingsAPIError base class for everything the SDK throws├── APIError any failure reported by the API│ ├── AuthenticationError 401, or invalid-token error payloads│ ├── PermissionDeniedError 403, or permission error payloads│ ├── NotFoundError 404, resource not found│ ├── ValidationError 400/422, mutations reporting success=false│ │ or errors[], and client-side payload checks│ ├── RateLimitError 429, carries retryAfter│ └── InternalServerError 5xx, retry a read; read back a write first└── APIConnectionError no response arrived: outcome unknownتُصدَّر جميع الفئات من جذر الحزمة:
import {ListingsAPIError,APIError,AuthenticationError,PermissionDeniedError,NotFoundError,ValidationError,RateLimitError,InternalServerError,APIConnectionError,} from 'listingsapi-js';خصائص APIError
import { ListingsAPI, APIError, APIConnectionError } from 'listingsapi-js'; const client = new ListingsAPI(); try {await client.fetchAllLocations({ first: 10 });} catch (error) {if (error instanceof APIError) { console.error(error.statusCode); // 401, 429, 500, or 200 for payload errors console.error(error.code); // first platform code, e.g. 'SY10005' console.error(error.errors); // [{ code, message, context }, ...] console.error(error.responseBody); // raw body text} else if (error instanceof APIConnectionError) { // No response arrived. A read is safe to retry; for a write, read the // record back before repeating it. console.error('No response from the API:', error.message);}}التقاط الأخطاء حسب النوع
حالات فشل المصادقة
import { AuthenticationError } from 'listingsapi-js'; try {await client.fetchAllLocations({ first: 1 });} catch (error) {if (error instanceof AuthenticationError) { console.error('Invalid API key. Rotate it under API Keys in the dashboard.');}}الموارد غير الموجودة
import { NotFoundError } from 'listingsapi-js'; try {await client.fetchPremiumListings(99999999);} catch (error) {if (error instanceof NotFoundError) { console.error('Location not found.');}}حدود معدل الطلبات
يحتوي RateLimitError.retryAfter على قيمة Retry-After الواردة من الخادم بالثواني، أو null عند غياب الترويسة:
import { RateLimitError } from 'listingsapi-js'; try {await client.fetchAllLocations({ first: 100 });} catch (error) {if (error instanceof RateLimitError) { const waitSeconds = error.retryAfter ?? 5; console.error('Rate limited, retry in ' + waitSeconds + 's');}}حالات فشل التحقق من صحة البيانات
تتضمن حالات فشل التحقق من صحة البيانات من جانب الخادم عناصر الأخطاء الواردة من المنصة:
import { ValidationError } from 'listingsapi-js'; try {await client.updateLocation({ id: 16808, phone: 'not-a-phone-number' });} catch (error) {if (error instanceof ValidationError) { for (const entry of error.errors) { console.error((entry.code ?? 'unknown') + ': ' + entry.message); }}}الأخطاء ضمن استجابات HTTP 200
تُبلغ المنصة عن بعض حالات الفشل برمز HTTP 200 وحمولة خطأ ضمن جسم الاستجابة. تفحص حزمة SDK كل استجابة وتطرح أخطاء في هذه الحالات أيضًا، لذا لا تعني الحالة 200 أبدًا "افحص الجسم بنفسك":
في عمليات الكتابة (الدالتان المساعدتان POST وDELETE)، تفحص حزمة SDK كل كائن نتيجة في الاستجابة؛ وإذا أشار أي منها إلى success: false أو تضمّن errors[]، فإنها تطرح ValidationError مع تعيين statusCode إلى 200.
import { ValidationError } from 'listingsapi-js'; try {const result = await client.archiveLocations([16808]);console.log('Archived:', result);} catch (error) {if (error instanceof ValidationError) { // The API answered 200 but rejected the mutation console.error(error.statusCode); // 200 console.error(error.message);}}التحقق من صحة البيانات من جانب العميل في الدوال المساعدة للمنشورات
تتحقق الدوال المساعدة للمنشورات (createAnnouncement وcreateEvent وcreateOffer وbulkPublish) من صحة حمولتها محليًا وتطرح ValidationError قبل إجراء أي استدعاء عبر الشبكة:
- يجب ألا يكون
nameفارغًا أو مكوَّنًا من مسافات بيضاء فقط، ويجب ألا يكونlocationIdsفارغًا. - يجب أن يكون
sitesمجموعة فرعية غير فارغة من المجموعة التي تضمGOOGLEوFACEBOOK. messageمطلوب؛ وعندما يكون خريطة بقيم منفصلة لكل موقع، يجب أن يكون لكل موقع مستهدف عنصر غير فارغ ولا يقتصر على مسافات بيضاء.- يجب توفير
ctaTypeوctaUrlمعًا، ويجب أن تكون قيمةctaTypeإحدى القيمBOOKأوORDERأوSHOPأوLEARN_MOREأوSIGN_UPأوGET_OFFER. - يتطلب
createEventوcreateOfferأيضًا توفيرtitle. - يجب أن تكون قيمة
postType(فيbulkPublish) إحدى القيمANNOUNCEMENTأوEVENTأوOFFERأوCOVID19أوPRODUCT.
تتضمن هذه الأخطاء المحلية statusCode: null، وهو ما يتيح تمييزها عن الرفض من جانب الخادم. ترسل الطريقة منخفضة المستوى createPost(body) جسمها الخام دون إجراء هذه الفحوص.
import { ListingsAPI, ValidationError } from 'listingsapi-js'; const client = new ListingsAPI(); try {await client.createAnnouncement({ name: 'Grand opening', locationIds: [16808], message: 'Acme Dental is now open in Midtown!', ctaType: 'BOOK', // ctaUrl missing: throws before any request is sent});} catch (error) {if (error instanceof ValidationError && error.statusCode === null) { console.error(error.message); // Invalid post: ctaType and ctaUrl must be provided together}}إعادة المحاولة المدمجة
يعيد العميل نفسه المحاولة عند تلقي 429 واستجابات 5xx (مع الالتزام بالترويسة Retry-After) وعند فشل الشبكة، وذلك مرتين افتراضيًا. لا تصل الأخطاء إلى شفرتك إلا بعد استنفاد تلك المحاولات. راجع الإعدادات للاطلاع على الخيارين maxRetries وtimeout.
مثال لإعادة المحاولة بفترات انتظار مع مراعاة retryAfter
للمهام طويلة التشغيل التي ينبغي أن تستمر بعد استنفاد محاولات العميل المدمجة، ضع الاستدعاءات داخل حلقة إعادة محاولة بفترات انتظار تعطي الأولوية لتوجيه الخادم retryAfter، وتستخدم فترات انتظار متزايدة أُسّيًا عند غيابه:
import {ListingsAPI,RateLimitError,InternalServerError,APIConnectionError,} from 'listingsapi-js'; const client = new ListingsAPI(); async function withBackoff<T>(fn: () => Promise<T>,maxAttempts = 5,): Promise<T> {for (let attempt = 1; attempt <= maxAttempts; attempt++) { try { return await fn(); } catch (err) { const retryable = err instanceof RateLimitError || err instanceof InternalServerError || err instanceof APIConnectionError; if (!retryable || attempt === maxAttempts) throw err; let delayMs = 500 * 2 ** (attempt - 1); if (err instanceof RateLimitError && err.retryAfter !== null) { delayMs = err.retryAfter * 1000; } await new Promise((resolve) => setTimeout(resolve, delayMs)); }}throw new Error('unreachable');} const page = await withBackoff(() => client.fetchAllLocations({ first: 25 }));لا تُعاد المحاولة عمدًا عند حدوث NotFoundError أو ValidationError أو AuthenticationError أو PermissionDeniedError: لا يمكن أن تنجح هذه الطلبات عند تكرارها دون تغيير المدخلات أو المفتاح.
حالات فشل الشبكة وانتهاء المهلة
يؤدي أي أمر يمنع وصول الاستجابة (فشل DNS أو رفض الاتصال أو انتهاء المهلة المحددة لكل طلب) إلى طرح APIConnectionError. لا تمرر حزمة SDK أبدًا TypeError أو AbortError الأصليين الصادرين من fetch إلى الشفرة المستدعية.
APIConnectionError لا يعني أن الطلب لم يصل إلى الخادم. يتوقف الطلب بالفعل قبل وصوله إلى API عند فشل DNS أو رفض الاتصال، لكن انتهاء المهلة يندرج ضمن الفئة نفسها، وقد يكون الطلب قد وصل واكتمل بعد أن توقف العميل عن الانتظار. تعامل مع الحالة على أن النتيجة غير معروفة: يمكنك إعادة محاولة القراءة دون قيود، أما في عملية الكتابة فاقرأ السجل مجددًا قبل تكرارها، لأن API لا تكتشف عمليات الكتابة المكررة.
تُطبَّق المهلة داخليًا باستخدام AbortSignal.timeout في كل محاولة، وتبلغ افتراضيًا 240000 مللي ثانية، ويمكن ضبطها عبر خيار المُنشئ timeout. تُعاد المحاولة التي انتهت مهلتها مثل أي حالة فشل اتصال أخرى؛ ولا يُطرح إلا الخطأ الناتج عن الفشل النهائي:
import { ListingsAPI, APIConnectionError } from 'listingsapi-js'; const client = new ListingsAPI({ timeout: 15000 }); try {await client.fetchAllLocations({ first: 10 });} catch (error) {if (error instanceof APIConnectionError) { console.error('Network failure or timeout:', error.message);}}موارد ذات صلة
- الإعداد، الخياران
timeoutوmaxRetries - البدء السريع، مثال مبسط على معالجة الأخطاء
- حالات الاستخدام، معالجة الأخطاء في حالات عملية ضمن برامج نصية كاملة