Gestion des erreurs
Interceptez les classes d’erreurs listingsapi-js, lisez les codes d’erreur des plateformes et créez une temporisation progressive adaptée à retryAfter.
Toutes les erreurs levées par le SDK dérivent de ListingsAPIError. Les échecs de l’API lèvent APIError ou l’une de ses sous-classes, que la plateforme les signale par un statut HTTP hors de la plage 2xx ou dans le corps d’une réponse HTTP 200. Les échecs réseau (erreurs DNS, connexions refusées, délais d’attente dépassés) lèvent APIConnectionError. Vous n’avez jamais à examiner les indicateurs success ni les données brutes : une promesse résolue signifie que l’appel a réussi.
Hiérarchie des erreurs
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 unknownToutes les classes sont exportées depuis la racine du package :
import {ListingsAPIError,APIError,AuthenticationError,PermissionDeniedError,NotFoundError,ValidationError,RateLimitError,InternalServerError,APIConnectionError,} from 'listingsapi-js';Propriétés de 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);}}Intercepter les erreurs par type
Échecs d’authentification
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.');}}Ressources manquantes
import { NotFoundError } from 'listingsapi-js'; try {await client.fetchPremiumListings(99999999);} catch (error) {if (error instanceof NotFoundError) { console.error('Location not found.');}}Limites de débit
RateLimitError.retryAfter contient la valeur Retry-After du serveur en secondes, ou null si l’en-tête était absent :
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');}}Échecs de validation
Les échecs de validation côté serveur contiennent les entrées d’erreur de la plateforme :
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); }}}Erreurs dans les réponses HTTP 200
La plateforme signale certains échecs par un statut HTTP 200 et des données d’erreur dans le corps de la réponse. Le SDK examine chaque réponse et lève également une exception dans ces cas ; un statut 200 ne signifie donc jamais « vérifiez vous-même le corps de la réponse » :
Pour les écritures (fonctions utilitaires POST et DELETE), le SDK vérifie chaque objet de résultat dans la réponse ; si l’un d’eux signale success: false ou contient errors[], il lève ValidationError avec statusCode défini sur 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);}}Validation côté client dans les fonctions utilitaires de publication
Les fonctions utilitaires de publication (createAnnouncement, createEvent, createOffer et bulkPublish) valident leurs données localement et lèvent ValidationError avant tout appel réseau :
namene doit être ni vide ni composé uniquement d’espaces, etlocationIdsne doit pas être vide.sitesdoit être un sous-ensemble non vide de l’ensemble formé parGOOGLEetFACEBOOK.messageest obligatoire ; lorsqu’il s’agit d’une table de correspondance par site, chaque site ciblé doit avoir une entrée qui ne soit ni vide ni composée uniquement d’espaces.ctaTypeetctaUrldoivent être fournis ensemble, etctaTypedoit avoir l’une des valeurs suivantes :BOOK,ORDER,SHOP,LEARN_MORE,SIGN_UP,GET_OFFER.createEventetcreateOffernécessitent égalementtitle.postType(surbulkPublish) doit avoir l’une des valeurs suivantes :ANNOUNCEMENT,EVENT,OFFER,COVID19,PRODUCT.
Ces erreurs locales ont statusCode: null, ce qui vous permet de les distinguer d’un rejet côté serveur. La méthode de bas niveau createPost(body) envoie son corps brut sans effectuer ces vérifications.
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}}Nouvelles tentatives intégrées
Le client effectue lui-même de nouvelles tentatives pour 429 et les réponses 5xx (en respectant l’en-tête Retry-After), ainsi que pour les échecs réseau, à raison de deux nouvelles tentatives par défaut. Les erreurs ne sont transmises à votre code qu’une fois ces tentatives épuisées. Consultez Configuration pour les options maxRetries et timeout.
Exemple de temporisation tenant compte de retryAfter
Pour les tâches de longue durée qui doivent se poursuivre au-delà des nouvelles tentatives intégrées au client, placez les appels dans une boucle de temporisation qui utilise en priorité l’indication retryAfter du serveur et, à défaut, des délais exponentiels :
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 et PermissionDeniedError ne donnent délibérément lieu à aucune nouvelle tentative : répéter ces requêtes ne peut pas réussir sans modifier les données d’entrée ou la clé.
Échecs réseau et dépassements du délai d’attente
Tout événement empêchant la réception d’une réponse (échec DNS, connexion refusée ou dépassement du délai d’attente de la requête) lève APIConnectionError. Le SDK ne laisse jamais se propager les exceptions natives TypeError ou AbortError de fetch.
APIConnectionError ne signifie pas que la requête n’a jamais atteint le serveur. Un échec DNS ou une connexion refusée a bien interrompu la requête avant qu’elle n’atteigne l’API, mais un dépassement du délai d’attente utilise la même classe, et la requête peut avoir été reçue et traitée après que le client a cessé d’attendre. Considérez que le résultat est inconnu : vous pouvez relancer une lecture sans restriction, et pour une écriture, relisez l’enregistrement avant de la répéter, car l’API ne détecte pas les écritures répétées.
Les délais d’attente sont appliqués en interne avec AbortSignal.timeout à chaque tentative ; leur valeur par défaut est de 240000 ms et peut être configurée au moyen de l’option timeout du constructeur. Une tentative dont le délai d’attente est dépassé est relancée comme pour tout autre échec de connexion ; seul l’échec final est levé :
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);}}Ressources connexes
- Configuration, les options
timeoutetmaxRetries - Démarrage rapide, exemple minimal de gestion des erreurs
- Cas d’utilisation, gestion des erreurs en situation réelle dans des scripts complets