From 401b43cc04192fa3fadfcd6fa187c97cf0af4bfb Mon Sep 17 00:00:00 2001 From: Mathieu Date: Fri, 26 Jun 2026 11:59:37 +0200 Subject: [PATCH] fix: add rate limiting with retry on 429 in sync-stock Co-Authored-By: Claude Sonnet 4.6 --- src/app/api/creation/sync-stock/route.ts | 32 ++++++++++++++++-------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/src/app/api/creation/sync-stock/route.ts b/src/app/api/creation/sync-stock/route.ts index cc38976..1875bde 100644 --- a/src/app/api/creation/sync-stock/route.ts +++ b/src/app/api/creation/sync-stock/route.ts @@ -80,19 +80,28 @@ export async function POST(req: NextRequest) { let updated = 0 let errors = 0 + const sleep = (ms: number) => new Promise(r => setTimeout(r, ms)) + + const shopifyFetch = async (url: string, opts: RequestInit, retries = 3): Promise => { + const res = await fetch(url, opts) + if (res.status === 429 && retries > 0) { + const retryAfter = parseInt(res.headers.get('Retry-After') ?? '2', 10) + await sleep(retryAfter * 1000) + return shopifyFetch(url, opts, retries - 1) + } + return res + } + for (let i = 0; i < entries.length; i++) { const { shopifyId, stock, title } = entries[i] send({ type: 'progress', current: i + 1, total: entries.length, title }) try { - // Récupérer le variant pour obtenir inventory_item_id - const pRes = await fetch(`${baseUrl}/products/${shopifyId}.json?fields=variants`, { headers }) - if (pRes.status === 404) { - // Produit supprimé de Shopify, on ignore silencieusement - continue - } + await sleep(300) // éviter le rate limiting (2 req/s max) + + const pRes = await shopifyFetch(`${baseUrl}/products/${shopifyId}.json?fields=variants`, { headers }) + if (pRes.status === 404) continue if (!pRes.ok) { - const msg = `Produit ${shopifyId} erreur (${pRes.status})` - send({ type: 'error', title, message: msg }) + send({ type: 'error', title, message: `Produit ${shopifyId} erreur (${pRes.status})` }) errors++ continue } @@ -100,13 +109,14 @@ export async function POST(req: NextRequest) { const inventoryItemId = pData.product.variants[0]?.inventory_item_id if (!inventoryItemId) { errors++; continue } - // Connect (idempotent) - await fetch(`${baseUrl}/inventory_levels/connect.json`, { + await sleep(300) + await shopifyFetch(`${baseUrl}/inventory_levels/connect.json`, { method: 'POST', headers, body: JSON.stringify({ location_id: locationId, inventory_item_id: inventoryItemId }), }) - const setRes = await fetch(`${baseUrl}/inventory_levels/set.json`, { + await sleep(300) + const setRes = await shopifyFetch(`${baseUrl}/inventory_levels/set.json`, { method: 'POST', headers, body: JSON.stringify({ location_id: locationId, inventory_item_id: inventoryItemId, available: stock }), })