diff --git a/README.md b/README.md index a4459b0..c57d0c5 100644 --- a/README.md +++ b/README.md @@ -193,6 +193,65 @@ Siren ships official SDKs in three languages, all built against the same API: Contributions are welcome. See [CONTRIBUTING.md](./CONTRIBUTING.md) for how to clone, build, test, and open a pull request. Please also review our [Code of Conduct](./CODE_OF_CONDUCT.md). +## Error handling + +API and network failures throw a typed subclass of `SirenError`. Catch the base class to +handle any SDK error, or branch on a specific subclass. Every error has a `message`; `code` +(the API's machine-readable error code) and `statusCode` (the HTTP status) are set when the +failing response provides them — `ConnectionError` and `SignatureVerificationError`, for +example, carry no `statusCode`: + +| Error | When it's thrown | +|---|---| +| `AuthenticationError` | Missing or invalid API key (401) | +| `PermissionError` | Key lacks permission for the operation (403) | +| `NotFoundError` | The requested resource does not exist (404) | +| `ValidationError` | Request payload failed validation (422) | +| `BadRequestError` | Malformed request (400) | +| `ConflictError` | Conflicting state, e.g. a duplicate (409) | +| `RateLimitError` | Rate limit exceeded (429); honors `Retry-After` | +| `ApiError` | Unexpected server error (5xx) | +| `ConnectionError` | Network failure reaching Siren | +| `SignatureVerificationError` | A webhook signature did not verify | + +Two exceptions to the subclass rule: constructing a client without an `apiKey` throws the +base `SirenError` itself, and `webhooks.constructEvent` throws a native `SyntaxError` — not +a `SirenError` — when a correctly signed body is not valid JSON. + +```ts +import { + Siren, + SirenError, + RateLimitError, + ValidationError, + NotFoundError, +} from '@novatorius/siren'; + +const siren = new Siren({ apiKey: process.env.SIREN_API_KEY! }); + +try { + await siren.events.sale({ + source: 'stripe', + externalId: 'cs_test_a1b2c3', + total: 49.99, + trackingId: 4021, + }); +} catch (err) { + if (err instanceof RateLimitError) { + // already retried with backoff; back off further or queue for later + } else if (err instanceof ValidationError) { + console.error('Invalid payload:', err.message, err.code); + } else if (err instanceof NotFoundError) { + // nothing matched — e.g. a refund for a sale Siren never recorded + } else if (err instanceof SirenError) { + // statusCode/code are set when the API produced the failure + console.error(`Siren error ${err.statusCode ?? 'n/a'} (${err.name}):`, err.message); + } else { + throw err; // not a Siren error + } +} +``` + ## Resources - [Siren homepage](https://sirenaffiliates.com)