Tratamento de erros
Capture as classes de erro de listingsapi-js, leia os códigos de erro da plataforma e implemente backoff que considere retryAfter.
Todo erro lançado pelo SDK deriva de ListingsAPIError. Falhas da API lançam APIError ou uma de suas subclasses, seja quando a plataforma as informa por meio de um status HTTP fora da faixa 2xx ou no corpo de uma resposta HTTP 200. Falhas de rede (erros de DNS, conexões recusadas, tempos limite excedidos) lançam APIConnectionError. Você nunca precisa inspecionar indicadores success ou payloads brutos: uma promise resolvida significa que a chamada foi bem-sucedida.
Hierarquia de erros
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 unknownTodas as classes são exportadas da raiz do pacote:
import {ListingsAPIError,APIError,AuthenticationError,PermissionDeniedError,NotFoundError,ValidationError,RateLimitError,InternalServerError,APIConnectionError,} from 'listingsapi-js';Propriedades 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);}}Captura de erros por tipo
Falhas de autenticação
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.');}}Recursos ausentes
import { NotFoundError } from 'listingsapi-js'; try {await client.fetchPremiumListings(99999999);} catch (error) {if (error instanceof NotFoundError) { console.error('Location not found.');}}Limites de requisições
RateLimitError.retryAfter contém o valor de Retry-After do servidor em segundos, ou null quando o cabeçalho estava ausente:
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');}}Falhas de validação
Falhas de validação no servidor contêm as entradas de erro da plataforma:
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); }}}Erros em respostas HTTP 200
A plataforma informa algumas falhas com HTTP 200 e um payload de erro no corpo. O SDK inspeciona todas as respostas e também lança erros nesses casos, portanto um status 200 nunca significa "verifique o corpo por conta própria":
Para operações de escrita (funções auxiliares POST e DELETE), o SDK verifica cada objeto de resultado na resposta; se algum deles informar success: false ou contiver errors[], ele lança ValidationError com statusCode definido como 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);}}Validação no cliente nas funções auxiliares de post
As funções auxiliares de post (createAnnouncement, createEvent, createOffer e bulkPublish) validam seu payload localmente e lançam ValidationError antes de qualquer chamada de rede:
namenão pode estar em branco elocationIdsnão pode estar vazio.sitesdeve ser um subconjunto não vazio deGOOGLEeFACEBOOK.messageé obrigatório; quando for um mapa por site, cada site de destino precisa de uma entrada que não esteja em branco.ctaTypeectaUrldevem ser fornecidos juntos, ectaTypedeve ser um dos seguintes valores:BOOK,ORDER,SHOP,LEARN_MORE,SIGN_UP,GET_OFFER.createEventecreateOffertambém exigemtitle.postType(embulkPublish) deve ser um dos seguintes valores:ANNOUNCEMENT,EVENT,OFFER,COVID19,PRODUCT.
Esses erros locais têm statusCode: null, o que permite que você os diferencie de uma rejeição no servidor. O método de baixo nível createPost(body) envia seu corpo bruto sem essas verificações.
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}}Novas tentativas integradas
O próprio cliente repete as tentativas em caso de respostas 429 e 5xx (respeitando o cabeçalho Retry-After) e de falhas de rede, duas vezes por padrão. Os erros chegam ao seu código somente depois que essas tentativas se esgotam. Consulte Configuração para ver as opções maxRetries e timeout.
Exemplo de espera progressiva que considera retryAfter
Para tarefas de longa duração que devem continuar além das novas tentativas integradas do cliente, coloque as chamadas em um laço de repetição com espera progressiva que priorize a indicação retryAfter do servidor e, na ausência dela, use intervalos exponenciais:
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 }));Por decisão de projeto, não há novas tentativas para NotFoundError, ValidationError, AuthenticationError e PermissionDeniedError: repetir essas requisições não pode ter sucesso sem alterar os dados de entrada ou a chave.
Falhas de rede e tempos limite
Qualquer situação que impeça a chegada de uma resposta (falha de DNS, conexão recusada ou esgotamento do tempo limite por requisição) lança APIConnectionError. O SDK nunca propaga os erros nativos TypeError ou AbortError de fetch.
APIConnectionError não significa que a requisição nunca chegou ao servidor. Uma falha de DNS ou uma conexão recusada de fato interrompeu a requisição antes de ela chegar à API, mas um tempo limite excedido usa a mesma classe, e a requisição pode ter chegado e sido concluída depois que o cliente parou de esperar. Trate esse caso como resultado desconhecido: repita uma leitura livremente e, para uma escrita, leia o registro novamente antes de repeti-la, pois a API não detecta escritas repetidas.
Os tempos limite são aplicados internamente com AbortSignal.timeout em cada tentativa, com valor padrão de 240000 ms, configurável pela opção timeout do construtor. Uma tentativa que excede o tempo limite é repetida como qualquer outra falha de conexão; somente a falha final é lançada:
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);}}Recursos relacionados
- Configuração, as opções
timeoutemaxRetries - Guia de início rápido, exemplo mínimo de tratamento de erros
- Casos de uso, tratamento de erros em situações reais com scripts completos