When you first start building a custom Shopify app or a headless Next.js storefront, the ecosystem feels like magic.
Then you deploy to production, real customers start checking out, and your webhooks start silently failing at 2:00 AM.
Unlike standard web apps, e-commerce development has zero room for unhandled exceptions—a broken API call literally means lost revenue. Before you push your next Shopify integration, make sure you aren't falling into these 4 developer traps:
1. Ignoring Webhook Idempotency (Duplicate Orders)
Shopify’s webhook delivery system guarantees at-least-once delivery. This means during high-traffic sales, Shopify will frequently fire the exact same ORDERS_CREATE webhook two or three times for a single purchase.
If your backend unconditionally inserts records or triggers fulfillment emails, you will duplicate orders.
The Fix: Always grab the X-Shopify-Webhook-Id header and store it in Redis or your database with a 24-hour TTL before processing the payload:
JavaScript
const webhookId = req.headers['x-shopify-webhook-id'];
// Check if we already processed this exact webhook eventwebhook:${webhookId}`);
const alreadyProcessed = await redis.get(
if (alreadyProcessed) {
return res.status(200).send('Duplicate webhook ignored');
}
await redis.set(webhook:${webhookId}, 'true', 'EX', 86400);
// Now safe to process order...`
2. Getting Throttled by GraphQL "Cost-Based" Limits
Unlike standard REST APIs that limit you to X requests per minute, Shopify’s GraphQL Admin API uses a Calculated Query Cost model (a leaky bucket holding 1,000 points).
If you write a deeply nested query fetching 50 products, their variants, and their inventory quantities all at once, a single query can cost 800 points and instantly trigger a THROTTLED error.
The Fix: Request only the scalar fields you strictly need, and use pagination cursors (first: 10, after: $cursor) instead of requesting massive arrays.
3. Exposing Admin API Keys in Client Bundles
When building headless storefronts (Next.js, Remix, Nuxt), developers frequently confuse Shopify's two primary APIs:
Storefront API: Public, unauthenticated (Safe for browser/client requests).
Admin API: Highly privileged (Strictly server-side only).
If you accidentally prefix your Admin API access token with NEXT_PUBLIC_ in your .env file, any user inspecting your site's JavaScript bundle can steal it and modify store products or customer data.
4. Assuming All Currencies Have 2 Decimal Places
If you are writing custom frontend pricing logic and format prices by doing Math.round(amount * 100) / 100, your code will break the moment the store expands internationally.
Currencies like the Japanese Yen (JPY) or South Korean Won (KRW) are zero-decimal currencies.
The Fix: Never manually parse decimal placement. Delegate formatting to native JavaScript Intl.NumberFormat:
JavaScript
const formattedPrice = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: product.currencyCode, // e.g., 'USD' or 'JPY'
}).format(product.amount);
🛡️ Quick Takeaway
When developing for Shopify, always program defensively against network duplicates, API cost throttling, and international edge cases.
What was the most frustrating bug you ran into while building a Shopify app or theme? Drop your debugging war story in the comments below! 👇 (Hit 🦄 or 💖 if this saved your webhook logic).
Top comments (0)