Fehlerbehandlung

Fangen Sie listingsapi-js-Fehlerklassen ab, lesen Sie Plattformfehlercodes und implementieren Sie Backoff unter Berücksichtigung von retryAfter.

Jeder Fehler, den das SDK auslöst, ist von ListingsAPIError abgeleitet. API-Fehler lösen APIError oder eine seiner Unterklassen aus, unabhängig davon, ob die Plattform sie über einen HTTP-Status außerhalb des 2xx-Bereichs oder im Antworttext einer HTTP-200-Antwort meldet. Netzwerkfehler (DNS-Fehler, abgelehnte Verbindungen, Zeitüberschreitungen) lösen APIConnectionError aus. Sie müssen weder success-Flags noch unverarbeitete Nutzdaten prüfen: Ein erfolgreich erfülltes Promise bedeutet, dass der Aufruf erfolgreich war.

Fehlerhierarchie

Code
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

Alle Klassen werden aus dem Stammverzeichnis des Pakets exportiert:

TypeScript
import {ListingsAPIError,APIError,AuthenticationError,PermissionDeniedError,NotFoundError,ValidationError,RateLimitError,InternalServerError,APIConnectionError,} from 'listingsapi-js';

Eigenschaften von APIError

EigenschaftTypBeschreibung
statusCodenumber | nullHTTP-Status der fehlgeschlagenen Antwort: 401, 429, 500 oder 200, wenn der Fehler im Antworttext einer 200-Antwort übermittelt wurde. null bei clientseitigen Validierungsfehlern.
codestring | nullErster Fehlercode der Plattform, e.g. SY10005, sofern die API einen gesendet hat.
errorsApiErrorEntry[]Alle Fehlereinträge, auf { code, message, context } normalisiert.
responseBodystring | nullUnverarbeiteter Antworttext als Zeichenfolge.
retryAfternumber | nullNur bei RateLimitError. Wartezeit in Sekunden vor einem erneuten Versuch, aus dem Retry-After-Header ausgelesen, oder null, wenn der Header fehlt.
messagestringFür Menschen lesbare Zusammenfassung mit angehängten Fehlereinträgen (oder unverarbeitetem Antworttext).
namestringDer konkrete Klassenname, e.g. "RateLimitError".
TypeScript
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);}}

Fehler nach Typ abfangen

Authentifizierungsfehler

TypeScript
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.');}}

Fehlende Ressourcen

TypeScript
import { NotFoundError } from 'listingsapi-js'; try {await client.fetchPremiumListings(99999999);} catch (error) {if (error instanceof NotFoundError) {  console.error('Location not found.');}}

Anfragelimits

RateLimitError.retryAfter enthält den Retry-After-Wert des Servers in Sekunden oder null, wenn der Header fehlt:

TypeScript
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');}}

Validierungsfehler

Serverseitige Validierungsfehler enthalten die Fehlereinträge der Plattform:

TypeScript
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);  }}}

Fehler in HTTP-200-Antworten

Die Plattform meldet einige Fehler mit HTTP 200 und Fehlerdaten im Antworttext. Das SDK prüft jede Antwort und löst auch in diesen Fällen einen Fehler aus, sodass ein 200-Status niemals bedeutet: „Prüfen Sie den Antworttext selbst“:

Fehlerindikator in den NutzdatenAusgelöst als
errors[] auf oberster Ebene mit Code SY90005 oder SY90001AuthenticationError
errors[] auf oberster Ebene mit Code SY90003PermissionDeniedError
errors[] auf oberster Ebene mit Code RATE_LIMITEDRateLimitError
Jedes andere errors[] auf oberster EbeneAPIError
Ergebnis eines Schreibvorgangs mit errors[] oder success: falseValidationError

Bei Schreibvorgängen (mit den Hilfsfunktionen POST und DELETE) prüft das SDK jedes Ergebnisobjekt in der Antwort; wenn eines davon success: false meldet oder errors[] enthält, löst es ValidationError aus, wobei statusCode auf 200 gesetzt ist.

TypeScript
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);}}

Clientseitige Validierung in den post-Hilfsfunktionen

Die post-Hilfsfunktionen (createAnnouncement, createEvent, createOffer und bulkPublish) validieren ihre Nutzdaten lokal und lösen vor jedem Netzwerkaufruf ValidationError aus:

  • name darf weder leer sein noch ausschließlich aus Leerraumzeichen bestehen, und locationIds darf nicht leer sein.
  • sites muss eine nicht leere Teilmenge aus GOOGLE und FACEBOOK sein.
  • message ist erforderlich; wenn es sich um eine Zuordnung nach Website handelt, benötigt jede Zielwebsite einen Eintrag, der weder leer ist noch ausschließlich aus Leerraumzeichen besteht.
  • ctaType und ctaUrl müssen zusammen angegeben werden, und ctaType muss einen der Werte BOOK, ORDER, SHOP, LEARN_MORE, SIGN_UP, GET_OFFER haben.
  • createEvent und createOffer erfordern zusätzlich title.
  • postType (bei bulkPublish) muss einen der Werte ANNOUNCEMENT, EVENT, OFFER, COVID19, PRODUCT haben.

Diese lokalen Fehler haben statusCode: null, woran Sie sie von einer serverseitigen Ablehnung unterscheiden können. Die systemnahe Methode createPost(body) sendet ihren unverarbeiteten Anfragetext ohne diese Prüfungen.

TypeScript
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}}

Integrierte Wiederholungsversuche

Der Client wiederholt Anfragen bei 429- und 5xx-Antworten (unter Berücksichtigung des Retry-After-Headers) sowie bei Netzwerkfehlern selbstständig, standardmäßig zweimal. Fehler werden erst an Ihren Code weitergegeben, wenn diese Versuche ausgeschöpft sind. Informationen zu den Optionen maxRetries und timeout finden Sie unter Konfiguration.

Ein Backoff-Beispiel, das retryAfter berücksichtigt

Für lang laufende Aufgaben, die über die integrierten Wiederholungsversuche des Clients hinaus weiterlaufen sollen, führen Sie Aufrufe in einer Backoff-Schleife aus, die vorrangig die retryAfter-Angabe des Servers verwendet und andernfalls auf exponentiell steigende Wartezeiten zurückgreift:

backoff.ts
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 }));

Bei NotFoundError, ValidationError, AuthenticationError und PermissionDeniedError werden Anfragen bewusst nicht wiederholt: Eine Wiederholung dieser Anfragen kann ohne Änderung der Eingabe oder des Schlüssels nicht erfolgreich sein.

Netzwerkfehler und Zeitüberschreitungen

Alles, was den Eingang einer Antwort verhindert (DNS-Fehler, abgelehnte Verbindung oder Überschreitung des Zeitlimits für die jeweilige Anfrage), löst APIConnectionError aus. Das SDK gibt die nativen Fehler TypeError oder AbortError von fetch niemals weiter.

APIConnectionError bedeutet nicht, dass die Anfrage den Server nie erreicht hat. Bei einem DNS-Fehler oder einer abgelehnten Verbindung wurde die Anfrage zwar vor Erreichen der API gestoppt, aber eine Zeitüberschreitung gehört zur selben Fehlerklasse, und die Anfrage kann eingegangen und abgeschlossen worden sein, nachdem der Client bereits aufgegeben hat. Behandeln Sie diesen Fall als Ergebnis unbekannt: Einen Lesevorgang können Sie ohne Weiteres wiederholen; lesen Sie bei einem Schreibvorgang vor der Wiederholung den Datensatz erneut aus, da die API wiederholte Schreibvorgänge nicht erkennt.

Zeitlimits werden intern bei jedem Versuch mit AbortSignal.timeout durchgesetzt, standardmäßig mit 240000 ms, konfigurierbar über die Konstruktoroption timeout. Ein Versuch mit Zeitüberschreitung wird wie jeder andere Verbindungsfehler wiederholt; nur der letzte Fehler wird ausgelöst:

TypeScript
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);}}