// (g) Defensive handler: retries within the limits, then a clean fallback. // Secrets: API_KEY, a key for your API. // A run has 2000 ms and 3 fetches by default, and each fetch may take up to // 1500 ms before it rejects with 'fetch timed out'. So: retry only what can // succeed on a second try (a network error, 429 or 5xx), only while a whole // fetch still fits in the time left, and never more than the fetch limit. // A 4xx is your API saying no: fall back at once. Check what comes back // before you return it. There are no timers: you cannot wait between tries. const RUN_MS = 2000 // the run's time limit (adorably://claim-handler shows the current value) const FETCH_MS = 1500 // the longest one fetch can take const MAX_TRIES = 3 // the fetch limit per run const MARGIN_MS = 100 // time kept for the work after the last fetch /** @param {HandlerInput} input @returns {Promise} */ async function handle(input) { const started = Date.now() let last = '' for (let attempt = 1; attempt <= MAX_TRIES; attempt++) { if (attempt > 1 && Date.now() - started + FETCH_MS > RUN_MS - MARGIN_MS) break // no time for another full fetch let res try { res = await fetch('https://api.example.com/v1/perks', { method: 'POST', headers: { 'content-type': 'application/json', authorization: 'Bearer ' + SECRETS.API_KEY, 'idempotency-key': input.claim_id }, body: JSON.stringify({ claim_id: input.claim_id, email: input.email, offer_id: input.offer.id }), }) } catch (e) { last = e instanceof Error ? e.message : String(e) console.warn('attempt', attempt, last) if (last === 'fetch timed out') break // the next one would not finish in time continue } if (res.status === 429 || res.status >= 500) { last = 'perks API answered ' + res.status console.warn('attempt', attempt, last) continue } if (!res.ok) adorably.fallback('perks API refused the claim: ' + res.status) let body try { body = await res.json() } catch (e) { adorably.fallback('perks API answered with a body that is not JSON') } const url = body && body.url // adorably.allowedUrl: https, on an approved host, short enough. if (typeof url !== 'string' || !adorably.allowedUrl(url)) { adorably.fallback('perks API answered without a url this handler may return') } return { url } } adorably.fallback('perks API failed: ' + last) }