Gestione degli errori
Intercetta le classi di errore di listingsapi-js, leggi i codici di errore della piattaforma e implementa un backoff che tenga conto di retryAfter.
Ogni errore sollevato dall'SDK deriva da ListingsAPIError. Gli errori dell'API sollevano APIError o una delle sue sottoclassi, sia quando la piattaforma li segnala con uno stato HTTP diverso da 2xx, sia quando li include nel corpo di una risposta HTTP 200. Gli errori di rete (errori DNS, connessioni rifiutate, timeout) sollevano APIConnectionError. Non devi mai esaminare i flag success o i payload grezzi: una promise risolta indica che la chiamata è riuscita.
Gerarchia degli errori
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 unknownTutte le classi sono esportate dalla radice del pacchetto:
import {ListingsAPIError,APIError,AuthenticationError,PermissionDeniedError,NotFoundError,ValidationError,RateLimitError,InternalServerError,APIConnectionError,} from 'listingsapi-js';Proprietà di 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);}}Intercettare gli errori in base al tipo
Errori di autenticazione
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.');}}Risorse mancanti
import { NotFoundError } from 'listingsapi-js'; try {await client.fetchPremiumListings(99999999);} catch (error) {if (error instanceof NotFoundError) { console.error('Location not found.');}}Limiti di frequenza
RateLimitError.retryAfter contiene il valore Retry-After del server in secondi, oppure null se l'header era assente:
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');}}Errori di validazione
Gli errori di validazione lato server contengono le voci di errore della piattaforma:
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); }}}Errori nelle risposte HTTP 200
La piattaforma segnala alcuni errori con HTTP 200 e un payload di errore nel corpo. L'SDK esamina ogni risposta e solleva un'eccezione anche in questi casi, quindi uno stato 200 non significa mai "controlla tu il corpo":
Per le scritture (funzioni ausiliarie POST e DELETE), l'SDK controlla ogni oggetto risultato nella risposta; se uno di questi segnala success: false o contiene errors[], solleva ValidationError con statusCode impostato su 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);}}Validazione lato client nelle funzioni ausiliarie per i post
Le funzioni ausiliarie per i post (createAnnouncement, createEvent, createOffer e bulkPublish) validano il payload localmente e sollevano ValidationError prima di qualsiasi chiamata di rete:
namenon deve essere vuoto né contenere solo spazi elocationIdsnon deve essere vuoto.sitesdeve essere un sottoinsieme non vuoto diGOOGLEeFACEBOOK.messageè obbligatorio; quando è una mappa per sito, ogni sito di destinazione deve avere una voce che non sia vuota né contenga solo spazi.ctaTypeectaUrldevono essere forniti insieme ectaTypedeve essere uno dei seguenti valori:BOOK,ORDER,SHOP,LEARN_MORE,SIGN_UP,GET_OFFER.createEventecreateOfferrichiedono anchetitle.postType(subulkPublish) deve essere uno dei seguenti valori:ANNOUNCEMENT,EVENT,OFFER,COVID19,PRODUCT.
Questi errori locali hanno statusCode: null, che ti permette di distinguerli da un rifiuto lato server. Il metodo di basso livello createPost(body) invia il corpo grezzo senza questi controlli.
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}}Tentativi automatici integrati
Il client riprova automaticamente in caso di risposte 429 e 5xx (rispettando l'header Retry-After) e di errori di rete, per impostazione predefinita 2 volte. Gli errori raggiungono il tuo codice solo dopo l'esaurimento di questi tentativi. Consulta Configurazione per le opzioni maxRetries e timeout.
Un esempio di backoff che tiene conto di retryAfter
Per le attività di lunga durata che devono proseguire oltre i tentativi automatici integrati nel client, racchiudi le chiamate in un ciclo di backoff che dia la precedenza all'indicazione retryAfter del server e, in sua assenza, usi ritardi esponenziali:
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 }));In caso di NotFoundError, ValidationError, AuthenticationError e PermissionDeniedError, non vengono deliberatamente effettuati nuovi tentativi: ripetere queste richieste non può riuscire senza modificare l'input o la chiave.
Errori di rete e timeout
Qualsiasi problema che impedisca l'arrivo di una risposta (errore DNS, connessione rifiutata o scadenza del timeout della singola richiesta) solleva APIConnectionError. L'SDK non propaga mai le eccezioni native TypeError o AbortError di fetch.
APIConnectionError non significa che la richiesta non abbia mai raggiunto il server. Un errore DNS o una connessione rifiutata hanno effettivamente interrotto la richiesta prima che raggiungesse l'API, ma un timeout appartiene alla stessa classe e la richiesta potrebbe essere arrivata ed essere stata completata dopo che il client ha smesso di attendere. Considera l'esito sconosciuto: puoi riprovare liberamente una lettura, mentre per una scrittura rileggi il record prima di ripeterla, poiché l'API non rileva le scritture ripetute.
I timeout vengono applicati internamente tramite AbortSignal.timeout a ogni tentativo, con una durata predefinita di 240000 ms, configurabile tramite l'opzione timeout del costruttore. Un tentativo scaduto viene ripetuto come per qualsiasi altro errore di connessione; viene sollevato solo l'errore finale:
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);}}Risorse correlate
- Configurazione, le opzioni
timeoutemaxRetries - Guida rapida, esempio minimo di gestione degli errori
- Casi d'uso, gestione degli errori in scenari reali con script completi