← back to Tk 11331 Exec

lib.mjs

31 lines

// TK-11331 shared lib — Shopify Admin GraphQL, with retry on 429/5xx.
import fs from 'fs';
export const SHOP='designer-laboratory-sandbox.myshopify.com', VER='2024-10';
export const TOK=fs.readFileSync(process.env.HOME+'/Projects/secrets-manager/.env','utf8')
  .match(/^SHOPIFY_ADMIN_TOKEN=(.+)$/m)[1].trim().replace(/^["']|["']$/g,'');
const EP=`https://${SHOP}/admin/api/${VER}/graphql.json`;
export const sleep=ms=>new Promise(r=>setTimeout(r,ms));

export async function gql(query, variables={}, tries=6){
  let lastErr;
  for(let i=0;i<tries;i++){
    try{
      const r=await fetch(EP,{method:'POST',headers:{'X-Shopify-Access-Token':TOK,'Content-Type':'application/json'},
        body:JSON.stringify({query,variables})});
      if(r.status===429||r.status>=500){ lastErr=`HTTP ${r.status}`; await sleep(2000*(i+1)); continue; }
      const j=await r.json();
      if(j.errors){
        const throttled=JSON.stringify(j.errors).includes('THROTTLED');
        if(throttled){ lastErr='THROTTLED'; await sleep(3000*(i+1)); continue; }
        throw new Error('GQL errors: '+JSON.stringify(j.errors));
      }
      // throttle courtesy from cost extensions
      const c=j.extensions?.cost?.throttleStatus;
      if(c && c.currentlyAvailable < 200) await sleep(600);
      return j.data;
    }catch(e){ lastErr=e.message; await sleep(1500*(i+1)); }
  }
  throw new Error('gql failed after retries: '+lastErr);
}
export function j(o){return JSON.stringify(o);}