| Shopify API fully down |
Monitor status page |
Nothing to f
'Install and configure Shopify app authentication with OAuth, session.
ReadWriteEditBash(npm:*)Bash(pnpm:*)Bash(npx:*)Grep
Shopify Install & Auth
Overview
Set up Shopify app authentication using the official @shopify/shopify-api library. Covers OAuth flow, session token exchange, custom app tokens, and Storefront API access.
Prerequisites
- Node.js 18+ (the
@shopify/shopify-api v9+ requires it)
- A Shopify Partner account at https://partners.shopify.com
- An app created in the Partner Dashboard with API credentials
- A development store for testing
Instructions
Step 1: Install the Shopify API Library
# Core library + Node.js runtime adapter
npm install @shopify/shopify-api @shopify/shopify-app-remix
# Or for standalone Node apps:
npm install @shopify/shopify-api @shopify/shopify-app-express
# For Remix (recommended by Shopify):
npm install @shopify/shopify-app-remix @shopify/app-bridge-react
Step 2: Configure Environment Variables
Create a .env file (add to .gitignore immediately):
# .env — NEVER commit this file
SHOPIFY_API_KEY=your_app_api_key
SHOPIFY_API_SECRET=your_app_api_secret
SHOPIFY_SCOPES=read_products,write_products,read_orders,write_orders
SHOPIFY_APP_URL=https://your-app.example.com
SHOPIFY_HOST_NAME=your-app.example.com
# For custom/private apps only:
SHOPIFY_ACCESS_TOKEN=shpat_xxxxxxxxxxxxxxxxxxxxx
# API version — use a stable quarterly release
# Update quarterly — see shopify.dev/docs/api/usage/versioning
SHOPIFY_API_VERSION=2025-04
# .gitignore — add these immediately
.env
.env.local
.env.*.local
Step 3: Initialize the Shopify API Library
// src/shopify.ts
import "@shopify/shopify-api/adapters/node";
import { shopifyApi, LATEST_API_VERSION, Session } from "@shopify/shopify-api";
const shopify = shopifyApi({
apiKey: process.env.SHOPIFY_API_KEY!,
apiSecretKey: process.env.SHOPIFY_API_SECRET!,
scopes: process.env.SHOPIFY_SCOPES!.split(","),
hostName: process.env.SHOPIFY_HOST_NAME!,
apiVersion: LATEST_API_VERSION,
isEmbeddedApp: true,
});
export default shopify;
Step 4: Implement OAuth Flow (Public Apps)
Express-based OAuth flow that redirects to Shopify and handles the callback token exchange.
See OAuth Flow for the complete Express route implementation.
Step 5: Token Exchange (Embedded Apps)
For embedded apps, use session token exchange instead of traditional OAuth:
// Token exchange — converts session token (JWT) to API access token
import shopify from "../shopify";
async function exchangeToken(
shop: string,
sessionToken: string
): Promise<Session> {
const { session } = await shopify.auth.tokenExchange({
sessionToken,
shop,
'Identify and avoid Shopify API anti-patterns: ignoring userErrors, wrong.
ReadGrep
Shopify Known Pitfalls
Overview
The 10 most common mistakes when building Shopify apps, with real API examples showing the wrong way and the right way.
Prerequisites
- Existing Shopify app codebase to review or audit
- Familiarity with GraphQL Admin API query patterns and response shapes
- Access scopes configured for the APIs your app uses
@shopify/shopify-api v9+ installed (for code examples)
Instructions
Each pitfall includes a wrong-way and right-way code example. See Pitfall Examples for all 10 complete code comparisons.
Pitfall #1: Not Checking userErrors (The #1 Mistake)
Shopify GraphQL mutations return HTTP 200 even when they fail. The errors are in userErrors. Always check userErrors.length > 0 before accessing the result.
Pitfall #2: Using REST When GraphQL Is Required
REST Admin API is legacy as of October 2024. New public apps after April 2025 must use GraphQL. GraphQL also lets you request only the fields you need.
Pitfall #3: Ignoring API Version Deprecation
Shopify deprecates API versions ~12 months after release. Use LATESTAPIVERSION from @shopify/shopify-api and monitor x-shopify-api-deprecated-reason response headers.
Pitfall #4: Missing Mandatory GDPR Webhooks
Your app will be rejected from the App Store without customers/data_request, customers/redact, and shop/redact webhook handlers.
Pitfall #5: Webhook Handler Takes Too Long
Shopify expects a 200 response within 5 seconds. Respond immediately and queue work asynchronously, otherwise Shopify retries and creates duplicates.
Pitfall #6: Using ProductInput on API 2024-10+
The ProductInput type was split into ProductCreateInput and ProductUpdateInput in 2024-10. Use the specific type for each operation.
Pitfall #7: Not Using Cursor Pagination
Shopify uses Relay-style cursor pagination, not page numbers. Use after / endCursor with pageInfo.
Pitfall #8: Requesting 250 Items Per Page
first: 250 with nested connections creates enormous query costs that THROTTLE immediately. Use first: 50 or smaller with nested resources.
Pitfall #9: Exposing Admin Token in Client-Side Code
Admin API tokens have full access. Never send them to the browser — proxy through your server.
Pitfall #10: Not Handling APP_UNINSTALLED Webhook
When a merchant uninstalls your app, clean up sessions immediately. Stale sessions cause auth redirect loops on reinstall.
Output
- Anti-patterns identified in codebase
- Fixes prioritized (security
'Load test Shopify integrations respecting API rate limits, plan capacity.
ReadWriteEditBash(k6:*)Bash(curl:*)
Shopify Load & Scale
Overview
Load test Shopify app integrations while respecting API rate limits. Plan capacity for high-traffic events like Black Friday / Cyber Monday (BFCM).
Prerequisites
- k6 load testing tool installed (
brew install k6)
- Test store with API access (never load test production)
- Understanding of Shopify rate limits per plan
Instructions
Step 1: Understand Capacity Constraints
Your app's throughput is bounded by Shopify's rate limits, not your infrastructure:
| Plan |
GraphQL Points |
Restore Rate |
Max Sustained QPS |
Burst Capacity |
| Standard |
1,000 |
50/sec |
~10 queries/sec |
1,000 points burst |
| Shopify Plus |
2,000 |
100/sec |
~20 queries/sec |
2,000 points burst |
A typical product query costs 10-50 points. At 50 points/query, Standard supports ~1 query/second sustained.
Step 2: k6 Load Test Script
k6 script with Shopify-specific custom metrics (throttle tracking, query cost trends, error rates) and automatic request pacing.
See k6 Load Test Script for the complete test script.
Step 3: Run Load Test
# Against a test store — NEVER production
k6 run \
--env SHOPIFY_STORE=dev-store.myshopify.com \
--env SHOPIFY_ACCESS_TOKEN=shpat_test_token \
shopify-load-test.js
# Output results to InfluxDB for Grafana dashboards
k6 run --out influxdb=http://localhost:8086/k6 shopify-load-test.js
Step 4: BFCM / Flash Sale Preparation
Pre-BFCM preparation including cache pre-warming, Storefront API offloading, bulk inventory sync, and Kubernetes HPA configuration for webhook processing.
See BFCM Preparation for application-level and infrastructure scaling patterns.
Output
- Load test script calibrated to Shopify rate limits
- Performance baseline documented
- BFCM preparation checklist completed
- Infrastructure scaling configured for webhook volume
Error Handling
| Issue |
Cause |
Solution |
| k6 shows high error rate |
Hitting rate limits |
Reduce VUs, increase sleep between requests |
| All requests THROTTLED |
Exceeding 50 points/sec |
Space queries further apart |
| Webhooks backing up |
Slow processing |
Respond 200 immediately, queue processing |
| Cache stampede on sale start |
All caches expire at once |
Stagger cache TTLs, pre-warm |
Examples
Quick Capacity Estimate
'Configure Shopify local development with Shopify CLI, hot reload, and.
ReadWriteEditBash(npm:*)Bash(pnpm:*)Bash(npx:*)Bash(shopify:*)
Shopify Local Dev Loop
Overview
Set up a fast, reproducible local development workflow using Shopify CLI, ngrok tunneling for webhooks, and Vitest for testing against the Shopify API.
Prerequisites
- Completed
shopify-install-auth setup
- Node.js 18+ with npm/pnpm
- Shopify CLI 3.x (
npm install -g @shopify/cli)
- A Shopify Partner account and development store
Instructions
Step 1: Scaffold with Shopify CLI
# Create a new Remix-based Shopify app (recommended)
shopify app init
# Or scaffold manually
mkdir my-shopify-app && cd my-shopify-app
npm init -y
npm install @shopify/shopify-api @shopify/shopify-app-remix \
@shopify/app-bridge-react @remix-run/node @remix-run/react
Step 2: Project Structure
my-shopify-app/
├── app/
│ ├── routes/
│ │ ├── app._index.tsx # Main app page
│ │ ├── app.products.tsx # Products management
│ │ ├── auth.$.tsx # OAuth callback
│ │ └── webhooks.tsx # Webhook handler
│ ├── shopify.server.ts # Shopify API client setup
│ └── root.tsx
├── extensions/ # Theme app extensions
├── shopify.app.toml # App configuration
├── .env # Local secrets (git-ignored)
├── .env.example # Template for team
└── package.json
Step 3: Configure shopify.app.toml
Central app configuration with scopes, auth redirects, and mandatory GDPR webhook subscriptions.
See App TOML Config for the complete configuration file.
Step 4: Start Dev Server with Tunnel
# Shopify CLI handles ngrok tunnel + OAuth automatically
shopify app dev
# This will:
# 1. Start your app on localhost:3000
# 2. Create an ngrok tunnel
# 3. Update your app URLs in Partner Dashboard
# 4. Open your app in the dev store admin
# 5. Hot reload on file changes
Step 5: Set Up Testing
Vitest setup with mocked Shopify API client and recommended package.json scripts for the dev workflow.
See Vitest Shopify Mock for the complete test setup.
Step 6: GraphQL Explorer for Development
# Open the Shopify GraphiQL explorer for your store
# Navigate to: https://your-store.myshopify.com/admin/api/2025-04/graphql.json
# Use the Shopify Admin GraphiQL app (install from admin)
# Or use curl to test queries directly:
curl -X POST \
"https://your-store.myshopify.com/admin/api/${SHOPIFY_API_VERSION:-2025-04}/graphql.json" \
-H "Content-Type: application/json" \
-H "X-Shopify-Access-Token: shpat_xxx" \
-d '{"query": "{ shop { name } }"}'
Output
- Shopify CLI dev server ru
'Model custom data with Shopify metafields and metaobjects via the GraphQL.
ReadWriteEditBash(npm:*)Grep
Shopify Metafields & Metaobjects
Overview
Metafields attach key-value custom data to existing resources (products, variants, orders, customers). Metaobjects define entirely new content types with custom schemas. Together they replace the need for external databases for most custom data needs.
Prerequisites
- Completed
shopify-install-auth setup
- Access scopes:
readmetaobjects, writemetaobjects, readmetaobjectdefinitions, writemetaobjectdefinitions
- Metafield scopes per resource:
readproducts/writeproducts for product metafields, etc.
Instructions
Step 1: Define a Metafield on Products
Use metafieldDefinitionCreate with namespace, key, type, and ownerType:
await client.request(METAFIELD_DEFINITION_CREATE, {
variables: {
definition: {
namespace: "custom",
key: "care_instructions",
name: "Care Instructions",
type: "multi_line_text_field", // See references/metafield-types.md
ownerType: "PRODUCT",
pin: true, // Show in admin UI
},
},
});
// Always check userErrors — Shopify returns 200 even on validation failures
Step 2: Set Values in Batch with metafieldsSet
await client.request(METAFIELDS_SET, {
variables: {
metafields: [
{
ownerId: "gid://shopify/Product/123456",
namespace: "custom",
key: "care_instructions",
value: "Machine wash cold.\nTumble dry low.",
type: "multi_line_text_field",
},
// Up to 25 metafields per call
],
},
});
Step 3: Create a Metaobject Definition
Define a custom content type (e.g., "Designer" with typed fields):
await client.request(METAOBJECT_DEFINITION_CREATE, {
variables: {
definition: {
type: "$app:designer",
displayNameKey: "name",
fieldDefinitions: [
{ key: "name", name: "Name", type: "single_line_text_field" },
{ key: "bio", name: "Bio", type: "multi_line_text_field" },
{ key: "photo", name: "Photo", type: "file_reference" },
],
access: { storefront: "PUBLIC_READ" },
},
},
});
Step 4: Create Metaobject Instances
await client.request(METAOBJECT_CREATE, {
variables: {
metaobject: {
type: "$app:designer",
handle: "jane-doe",
fields: [
{ key: "name", value: "Jane Doe&qu
'Migrate e-commerce data to Shopify using bulk operations, product imports,.
ReadWriteEditBash(npm:*)Bash(node:*)Bash(curl:*)
Shopify Migration Deep Dive
Overview
Migrate product catalogs, customers, and orders to Shopify using the GraphQL Admin API bulk mutations, CSV imports, and incremental migration patterns.
Prerequisites
- Source platform data exported (CSV, JSON, or API access)
- Shopify store with appropriate access scopes
- Scopes needed:
writeproducts, writecustomers, writeorders, writeinventory
Instructions
Step 1: Assess Migration Scope
| Data Type |
Shopify Import Method |
Complexity |
| Products + variants |
productSet mutation (upsert) |
Low |
| Product images |
productCreateMedia mutation |
Low |
| Customers |
Customer CSV import or customerCreate |
Medium |
| Historical orders |
draftOrderCreate + draftOrderComplete |
High |
| Inventory levels |
inventorySetQuantities mutation |
Medium |
| Collections |
collectionCreate mutation |
Low |
| Redirects (URLs) |
urlRedirectCreate mutation |
Low |
| Metafields |
Included in product/customer mutations |
Medium |
Step 2: Bulk Product Import with productSet
productSet is idempotent — it creates or updates based on handle, making it perfect for migrations. Handles variants, metafields, and all product attributes in a single mutation.
See Product Set Migration for the complete migration function.
Step 3: Bulk Operations for Large Imports
For importing thousands of products, use Shopify's staged uploads combined with bulk mutation to avoid rate limit issues.
See Bulk Operations Import for the staged upload and bulk mutation workflow.
Step 4: Set Inventory Levels & URL Redirects
After products are created, set inventory quantities at each location and create URL redirects to preserve SEO from the old platform.
See Inventory and Redirects for both mutation implementations.
Step 5: Post-Migration Validation
Automated validation that compares expected source counts against actual Shopify counts for products, customers, and other data types.
See Post-Migration Validation for the validation script.
Output
- Products migrated with variants, images, and metafields
- Inventory levels set at correct locations
- UR
'Configure Shopify apps across development, staging, and production environments.
ReadWriteEditBash(shopify:*)
Shopify Multi-Environment Setup
Overview
Configure Shopify apps with isolated development, staging, and production environments. Each environment uses a separate Shopify app instance, development store, and credentials.
Prerequisites
- Shopify Partner account
- Multiple development stores (free to create in Partner Dashboard)
- Secret management solution for production (Vault, AWS Secrets Manager, etc.)
Instructions
Step 1: Create Separate App Instances
Create one app per environment in your Partner Dashboard:
| Environment |
App Name |
Store |
Purpose |
| Development |
My App (Dev) |
dev-store.myshopify.com |
Local development |
| Staging |
My App (Staging) |
staging-store.myshopify.com |
Pre-prod testing |
| Production |
My App |
live-store.myshopify.com |
Live traffic |
Each app gets its own APIKEY, APISECRET, and ACCESS_TOKEN.
Step 2: Environment Configuration Files
Create .env.* files for each environment and corresponding Shopify CLI TOML configs. Production secrets should never be stored on disk -- use a secret manager.
See Environment Config Files for the complete .env and .toml configuration templates.
Step 3: Environment-Aware Configuration
TypeScript config module that loads environment-specific settings (debug mode, session storage type) with LATESTAPIVERSION from @shopify/shopify-api.
See Environment-Aware Config for the complete implementation.
Step 4: Environment Guards
Safety functions that prevent dangerous operations from running in the wrong environment (e.g., blocking test data seeding in production, requiring production for billing activation).
See Environment Guards for the complete implementation.
Output
- Isolated environments with separate app instances
- Environment-specific configuration loading
- Shopify CLI configured for multi-env workflow
- Safety guards preventing cross-environment mistakes
Error Handling
| Issue |
Cause |
Solution |
| Wrong store in dev |
Using prod .env |
Verify SHOPIFY_STORE in your .env file |
| OAuth fails on staging |
Wrong redirect URL |
Update redirect_urls in staging app config |
| Webhooks not received |
URL mismatch |
Each environment needs its own webhook U
'Set up observability for Shopify app integrations with query cost tracking,.
ReadWriteEdit
Shopify Observability
Overview
Instrument your Shopify app to track GraphQL query cost, rate limit consumption, webhook delivery success, and API latency. Shopify-specific metrics that generic monitoring misses.
Prerequisites
- Prometheus or compatible metrics backend
- pino or similar structured logger
- Shopify API client with response interception
Instructions
Step 1: Shopify-Specific Metrics
Define Prometheus counters, histograms, and gauges for query cost, rate limit headroom, REST bucket state, API duration, webhook processing, and error classification.
See Shopify Metrics Definitions for the complete metric registrations.
Step 2: Instrumented GraphQL Client
Wrap the Shopify GraphQL client to automatically record query cost from extensions.cost, update rate limit gauges, and classify errors (throttled, auth, API error).
See Instrumented GraphQL Client for the complete implementation.
Step 3: REST API Header Tracking
Parse X-Shopify-Shop-Api-Call-Limit headers (e.g., "32/40") from REST responses to track leaky bucket fill level. Warn when bucket exceeds 80% capacity.
function trackRestHeaders(shop: string, headers: Record<string, string>): void {
const callLimit = headers["x-shopify-shop-api-call-limit"];
if (callLimit) {
const [used, max] = callLimit.split("/").map(Number);
restBucketGauge.set({ shop }, used);
if (used > max * 0.8) {
console.warn(`[shopify] REST bucket at ${used}/${max} for ${shop}`);
}
}
}
Step 4: Webhook Observability
Track HMAC validation results, processing success/failure, and duration for all incoming webhooks.
See Webhook Observability for the complete Express middleware.
Step 5: Structured Logging
Pino-based logger with automatic PII redaction and Shopify-specific context fields (query cost, available points, operation name).
See Structured Logging for the complete implementation.
Step 6: Alert Rules
Prometheus alert rules for low rate limits, high query cost (P95 > 500), webhook failures (> 10%), and API latency (P95 > 3s).
See Alert Rules for the complete Prometheus configuration.
Output
- GraphQL query cost tracking with per-operation metrics
- Rate limit monitoring for both REST and GraphQL
- Webhook delivery and processing metrics
- Structured logs with automatic PII redaction
- Alert rules for critical Shopify-specific conditions
Error Handling
'Optimize Shopify API performance with GraphQL query cost reduction,.
ReadWriteEdit
Shopify Performance Tuning
Overview
Optimize Shopify API performance through GraphQL query cost reduction, bulk operations for large data exports, response caching, and Storefront API for high-traffic public-facing queries.
Prerequisites
- Understanding of Shopify's calculated query cost system
- Access to the
Shopify-GraphQL-Cost-Debug: 1 header for cost analysis
- Redis or in-memory cache available (optional)
Instructions
Step 1: Analyze and Reduce Query Cost
Use the debug header to inspect requestedQueryCost vs actualQueryCost. Reduce cost by selecting only needed fields and lowering first: page sizes (250 to 50 can cut cost by 5x).
See Query Cost Optimization for debug commands and before/after examples.
Step 2: Use Bulk Operations for Large Exports
Bulk operations bypass rate limits and are designed for exporting large datasets. Start a mutation, poll for completion, then download JSONL results.
See Bulk Operations for the complete mutation/poll/download flow and performance comparison table.
Step 3: Cache Frequently Accessed Data
LRU cache layer with webhook-driven invalidation. Cache product data for 5 minutes, then clear on products/update webhook events.
See Response Caching for the complete implementation.
Step 4: Use Storefront API for Public Queries
The Storefront API has separate rate limits and is designed for high-traffic public storefronts. Uses LATESTAPIVERSION from @shopify/shopify-api.
See Storefront API Usage for the complete implementation.
Output
- Query costs reduced through field selection and page size optimization
- Bulk operations configured for large data exports
- Response caching with webhook-driven invalidation
- Storefront API used for public-facing high-traffic queries
Error Handling
| Issue |
Cause |
Solution |
THROTTLED on every query |
requestedQueryCost too high |
Reduce first: and remove unused fields |
| Bulk operation FAILED |
Query syntax error |
Test query in GraphiQL first |
| Stale cache data |
Cache not invalidated |
Add webhook handlers to clear cache |
| Storefront API 403 |
Wrong token type |
Use Storefront API access token, not Admin |
Examples
Reducing Query Cost on a Product Sync
A product sync job hits THROTTLED errors. Analyze the cost b
'Implement Shopify app policy enforcement with ESLint rules for API key.
ReadWriteEditBash(npx:*)
Shopify Policy & Guardrails
Overview
Automated policy enforcement for Shopify apps: secret detection, query cost budgets, App Store compliance checks, and CI policy validation.
Prerequisites
- ESLint configured in project
- Pre-commit hooks infrastructure
- CI/CD pipeline with GitHub Actions
- Shopify app with
shopify.app.toml
Instructions
Step 1: Secret Detection Rules
Custom ESLint rule that catches hardcoded Shopify tokens (shpat, shpss) and API secrets in string literals and template literals.
See Secret Detection ESLint for the complete rule implementation.
Step 2: Query Cost Budget Enforcement
Static analysis of GraphQL queries enforcing budgets: max 100 items per first: param, max 3 levels of nesting, and max 500 estimated cost. Runs at build/test time.
See Query Cost Budget for the complete implementation.
Step 3: Pre-Commit Hooks
Git hooks that scan staged changes for Shopify tokens and block .env files from being committed.
# .pre-commit-config.yaml
repos:
- repo: local
hooks:
- id: shopify-token-scan
name: Scan for Shopify tokens
language: system
entry: bash -c '
if git diff --cached --diff-filter=d | grep -E "shpat_[a-f0-9]{32}|shpss_[a-f0-9]{32}" ; then
echo "ERROR: Shopify access token detected in staged changes"
exit 1
fi'
pass_filenames: false
- id: shopify-env-check
name: Check .env not staged
language: system
entry: bash -c '
if git diff --cached --name-only | grep -E "^\.env$|^\.env\.local$|^\.env\.production$" ; then
echo "ERROR: .env file staged for commit"
exit 1
fi'
pass_filenames: false
Step 4: App Store Compliance Checker
Pre-submission script that validates all three GDPR webhooks, token hygiene, CSP headers, and API version stability.
See Compliance Checker for the complete implementation.
Step 5: CI Policy Pipeline
GitHub Actions workflow enforcing token scanning, GDPR webhook configuration, and API version stability on every push and PR.
See CI Policy Pipeline for the complete workflow.
Output
- ESLint rules catching hardcoded tokens
- Query cost budgets enforced
- Pre-commit hooks blocking secret leaks
- App Store compliance checker
- CI policy pipeline preventing violations
Error Handling
| Issue |
Cause<
'Execute Shopify app production deployment checklist covering App Store.
ReadBash(curl:*)Grep
Shopify Production Checklist
Overview
Complete pre-launch checklist for deploying Shopify apps to production and submitting to the Shopify App Store.
Prerequisites
- Staging environment tested and verified
- Shopify Partner account with app configured
- All development and staging tests passing
Instructions
Step 1: API and Authentication
- [ ] Using a recent stable API version (e.g., 2025-04), not
unstable
- [ ] Access token stored in secure environment variables (never in code)
- [ ] API secret stored securely for webhook HMAC verification
- [ ] OAuth flow tested with a fresh install on a clean dev store
- [ ] Session persistence implemented (database or Redis, not in-memory)
- [ ] Token refresh/re-auth handled for expired sessions
- [ ]
APP_UNINSTALLED webhook handler cleans up sessions
Step 2: Mandatory GDPR Compliance
- [ ]
customers/data_request webhook handler implemented
- [ ]
customers/redact webhook handler implemented
- [ ]
shop/redact webhook handler implemented (fires 48h after uninstall)
- [ ] All three configured in
shopify.app.toml
- [ ] Handlers respond with HTTP 200 within 5 seconds
- [ ] Customer data deletion actually works (test it!)
Step 3: Webhook Security
- [ ] All webhooks verify
X-Shopify-Hmac-Sha256 using HMAC-SHA256
- [ ] Using
crypto.timingSafeEqual() for signature comparison
- [ ] Webhook endpoints use raw body parsing (not JSON middleware)
- [ ] Idempotency: duplicate webhook deliveries handled gracefully
Step 4: Rate Limit Resilience
- [ ] GraphQL queries optimized (check
requestedQueryCost with debug header)
- [ ] Retry logic with exponential backoff for 429 / THROTTLED responses
- [ ] Bulk operations used for large data exports instead of paginated queries
- [ ] No unbounded loops that could exhaust rate limits
Step 5: Error Handling
- [ ] All GraphQL mutations check
userErrors array (200 with errors!)
- [ ] HTTP 4xx/5xx errors caught and logged with
X-Request-Id
- [ ] Graceful degradation when Shopify is unavailable
- [ ] No PII logged (customer emails, addresses, phone numbers)
Step 6: App Store Submission Requirements
- [ ] App listing has clear name, description, and screenshots
- [ ] Privacy policy URL provided
- [ ] App has proper onboarding flow for new merchants
- [ ] Embedded app uses App Bridge for navigation (no full-page redirects)
- [ ] CSP headers set:
frame-ancestors https://*.myshopify.com https://admin.shopify.com
- [ ] App works on both desktop and m
'Handle Shopify API rate limits for both REST (leaky bucket) and GraphQL.
ReadWriteEdit
Shopify Rate Limits
Overview
Shopify uses two distinct rate limiting systems: leaky bucket for REST and calculated query cost for GraphQL. This skill covers both with real header values and response shapes.
Prerequisites
- Understanding of Shopify's REST and GraphQL Admin APIs
- Familiarity with the
@shopify/shopify-api library
Instructions
Step 1: Understand the Two Rate Limit Systems
REST Admin API -- Leaky Bucket:
| Plan |
Bucket Size |
Leak Rate |
| Standard |
40 requests |
2/second |
| Shopify Plus |
80 requests |
4/second |
The X-Shopify-Shop-Api-Call-Limit header shows your bucket state (e.g., 32/40 means 32 of 40 slots used). When full, you get HTTP 429 with Retry-After header.
GraphQL Admin API -- Calculated Query Cost:
| Plan |
Max Available |
Restore Rate |
| Standard |
1,000 points |
50 points/second |
| Shopify Plus |
2,000 points |
100 points/second |
Every GraphQL response includes cost info in extensions.cost with requestedQueryCost (worst-case estimate), actualQueryCost (real cost, often much lower), and throttleStatus (available points and restore rate). When currentlyAvailable drops to 0, you get THROTTLED.
Step 2: Implement GraphQL Cost-Aware Throttling
Client-side rate limiter that tracks the query cost bucket and pre-emptively waits before sending requests that would be throttled. Updates available points from each response's throttleStatus.
See Cost-Aware Rate Limiter for the complete ShopifyRateLimiter class.
Step 3: Implement Retry with Backoff for 429s
Generic retry wrapper handling both REST 429 responses and GraphQL THROTTLED errors. Uses Retry-After header when available, otherwise exponential backoff with jitter (max 30s).
See Retry with Backoff for the complete implementation.
Step 4: Reduce Query Cost
Prune unused fields and lower first: page sizes to reduce requestedQueryCost. A query dropping from first: 250 to first: 50 with fewer nested fields can go from ~5,500 to ~112 cost.
See Query Cost Reduction for before/after examples and the debug curl command.
Output
- Rate limit-aware client that prevents 429 errors
- Retry logic with proper backoff for both RES
'Implement Shopify app reference architecture with Remix, Prisma session.
ReadGrep
Shopify Reference Architecture
Overview
Production-ready architecture based on Shopify's official Remix app template. Covers project structure, session storage with Prisma, extension architecture, and the recommended app patterns.
Prerequisites
- Understanding of Remix framework basics
- Shopify CLI 3.x installed
- Familiarity with Prisma ORM
Instructions
Step 1: Official Project Structure (Remix Template)
my-shopify-app/
├── app/
│ ├── routes/
│ │ ├── app._index.tsx # Main app dashboard
│ │ ├── app.products.tsx # Product management page
│ │ ├── app.settings.tsx # App settings
│ │ ├── auth.$.tsx # OAuth catch-all route
│ │ ├── auth.login/
│ │ │ └── route.tsx # Login page
│ │ └── webhooks.tsx # Webhook handler
│ ├── shopify.server.ts # Shopify API config (singleton)
│ ├── db.server.ts # Database connection
│ └── root.tsx
├── extensions/
│ ├── theme-app-extension/ # Theme blocks for Online Store
│ ├── checkout-ui/ # Checkout UI extension
│ └── product-discount/ # Shopify Function
├── prisma/
│ ├── schema.prisma # Database schema
│ └── migrations/
├── shopify.app.toml # App configuration
├── shopify.web.toml # Web process config
├── remix.config.js
└── package.json
Step 2: Core App Configuration
The shopify.server.ts singleton configures the API client, session storage, webhooks, and auth hooks. It uses LATESTAPIVERSION from @shopify/shopify-api and exports all auth/session helpers.
See Core App Configuration for the complete implementation.
Step 3: Session Storage with Prisma
The Prisma schema defines the required Session model (matching Shopify's session fields) plus your app's custom models. Use SQLite for dev and PostgreSQL for production.
See Prisma Session Storage for the complete schema.
Step 4: Route Pattern — Authenticated Admin Page
Each app route calls authenticate.admin(request) to get a pre-authenticated GraphQL client, then renders with Polaris components. Data flows through Remix loaders.
See Authenticated Admin Route for the complete implementation.
Step 5: Theme App Extension
Theme app extensions add customizable blocks to the Online Store. Each block defines a Liquid template with a {% schema %} JSON block for merchant-facing settings.
See Theme App Extension for the complete implementation.
Output
- Remix app with Shopify authent
'Implement reliability patterns for Shopify apps including circuit breakers.
ReadWriteEdit
Shopify Reliability Patterns
Overview
Build fault-tolerant Shopify integrations that handle API outages, webhook retry storms, and rate limit exhaustion gracefully.
Prerequisites
- Understanding of circuit breaker pattern
- Queue infrastructure (BullMQ, SQS, etc.) for async processing
- Cache layer for fallback data
Instructions
Step 1: Circuit Breaker for Shopify API
Wrap all Shopify API calls in a circuit breaker (using opossum) that opens at 50% error rate, only counting 5xx and timeout errors. When open, serve cached data. The breaker auto-tests recovery after 30 seconds in half-open state.
See Circuit Breaker for the complete implementation.
Step 2: Webhook Idempotency
Shopify retries webhooks up to 19 times over 48 hours if your endpoint doesn't return 200. Your handler must be idempotent. Use Redis to track X-Shopify-Webhook-Id with a 7-day TTL. Always respond 200 within 5 seconds, then process asynchronously.
See Webhook Idempotency for the complete implementation.
Step 3: Graceful Degradation with Cached Fallback
Implement a three-tier fallback: try the live API first (caching the result), fall back to cached data, then fall back to an alternative data source (e.g., local DB). Track the data source so you can log degraded responses.
See Cached Fallback for the complete implementation.
Step 4: Webhook Processing Queue
Don't process webhooks inline — queue them with BullMQ for resilience. Verify HMAC first, respond 200 immediately, then enqueue with topic/shop/payload metadata. The worker processes jobs with exponential backoff (5 retries) and idempotency checks.
See Webhook Processing Queue for the complete implementation.
Step 5: Rate Limit-Aware Retry
Retry logic that respects Shopify's Retry-After header (REST 429) and GraphQL throttle status restore rate. Calculates optimal wait time from available points rather than blindly backing off.
See Rate Limit-Aware Retry for the complete implementation.
Output
- Circuit breaker preventing cascade failures during Shopify outages
- Idempotent webhook processing preventing duplicate operations
- Graceful degradation with cached fallback data
- Queue-based webhook processing for resilience
- Rate limit-aware retry logic
Error Handling
| Issue |
Cause |
Solution |
| Circuit stays open |
Shopify extended outage |
Serve cached data, monitor status page |
Duplicate orders processed<
'Apply production-ready patterns for @shopify/shopify-api including typed.
ReadWriteEdit
Shopify SDK Patterns
Overview
Production-ready patterns for the @shopify/shopify-api library: singleton clients, typed GraphQL operations, session management, cursor-based pagination, codegen-typed operations, bulk operations, and webhook registry patterns.
Prerequisites
@shopify/shopify-api v9+ installed
- Familiarity with Shopify's GraphQL Admin API
- Understanding of async/await and TypeScript generics
Instructions
Step 1: Typed GraphQL Client Wrapper
Initialize a singleton shopifyApi instance with LATESTAPIVERSION, cache sessions per shop, and expose a typed shopifyQuery() helper that wraps client.request().
See Typed GraphQL Client for the complete implementation.
Step 2: Error Handling with Shopify Error Types
Custom ShopifyServiceError class that distinguishes retryable errors (429, 5xx) from permanent ones. Includes handleShopifyError() for error translation and safeShopifyCall() that returns {data, error} tuples instead of throwing.
See Error Handling for the complete implementation.
Step 3: Cursor-Based Pagination
Async generator paginateShopify() for Relay-style cursor pagination. Yields batches of nodes, automatically following pageInfo.endCursor until hasNextPage is false. Memory-efficient for large datasets.
See Cursor Pagination for the complete implementation.
Step 4: Multi-Tenant Client Factory
ShopifyClientFactory class for apps installed on multiple stores. Creates isolated GraphqlClient instances per merchant with session caching. Includes removeClient() for eviction on app uninstall.
See Multi-Tenant Factory for the complete implementation.
Step 5: Codegen-Typed Operations
Use @shopify/api-codegen-preset to generate TypeScript types from your GraphQL operations. This eliminates manual type definitions and catches schema changes at build time.
// codegen.ts — project root config
import { shopifyApiProject, ApiType } from "@shopify/api-codegen-preset";
export default {
schema: "https://shopify.dev/admin-graphql-direct-proxy",
documents: ["src/**/*.{ts,tsx}"],
projects: {
default: shopifyApiProject({
apiType: ApiType.Admin,
apiVersion: "2025-04", // Update quarterly
outputDir: "./src/types",
}),
},
};
// src/operations/products.ts — typed query with codegen output
im
'Apply Shopify security best practices for API credentials, webhook HMAC.
ReadWriteGrep
Shopify Security Basics
Overview
Security essentials for Shopify apps: credential management, webhook HMAC validation, request verification, and least-privilege access scopes.
Prerequisites
- Shopify Partner account with app credentials
- Understanding of HMAC-SHA256 signatures
- Access to Shopify app configuration
Instructions
Step 1: Secure Credential Storage
# .env — NEVER commit
SHOPIFY_API_KEY=your_api_key
SHOPIFY_API_SECRET=your_api_secret_key
SHOPIFY_ACCESS_TOKEN=shpat_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# .gitignore — add immediately
.env
.env.local
.env.*.local
*.pem
Token format reference:
| Token Type |
Prefix |
Length |
Used For |
| Admin API access token |
shpat_ |
38 chars |
Server-side Admin API |
| Storefront API token |
varies |
varies |
Client-safe storefront queries |
| API secret key |
none |
32+ hex |
Webhook HMAC, OAuth |
Step 2: Webhook HMAC Verification
Shopify signs every webhook with your app's API secret using HMAC-SHA256. The signature is in the X-Shopify-Hmac-Sha256 header. Use crypto.timingSafeEqual for comparison to prevent timing attacks. The middleware must use raw body parser (not JSON parser).
See Webhook HMAC Verification for the complete implementation.
Step 3: OAuth Request Verification
Verify that incoming OAuth requests from Shopify are authentic by checking the HMAC query parameter. The library handles this automatically, but the manual approach sorts params alphabetically, creates a query string, and compares HMAC hex digests.
See OAuth Request Verification for the complete implementation.
Step 4: Minimal Access Scopes
Only request the scopes your app actually needs:
| Use Case |
Required Scopes |
| Read-only product catalog |
read_products |
| Product management |
readproducts, writeproducts |
| Order dashboard |
read_orders |
| Fulfillment automation |
readorders, writefulfillments, read_fulfillments |
| Customer loyalty app |
readcustomers, writecustomers |
| Full admin app |
Request scopes incrementally, not all at once |
# shopify.app.toml — start minimal, add as needed
[access_scopes]
'Build headless storefronts with Shopify''s Storefront API and Cart API.
ReadWriteEditBash(npm:*)Grep
Shopify Storefront & Headless Commerce
Overview
The Storefront API is Shopify's public-facing GraphQL API for customer experiences. Unlike the Admin API (server-side, privileged), it uses a public access token safe for client-side code. Paired with the Cart API, it powers headless storefronts, mobile apps, and custom buying experiences.
Prerequisites
- Shopify store with a Storefront API access token (Headless channel or custom app)
- For Hydrogen: Node.js 18+ and Shopify CLI 3.x+
- Storefront API scopes:
unauthenticatedreadproducts, unauthenticatedreadcollections
Instructions
Step 1: Storefront API Client Setup
import { createStorefrontApiClient } from "@shopify/storefront-api-client";
const client = createStorefrontApiClient({
storeDomain: "my-store.myshopify.com",
apiVersion: LATEST_API_VERSION,
publicAccessToken: "your-storefront-public-token", // Safe in browser
});
Step 2: Cart Operations
const { data } = await client.request(CART_CREATE, {
variables: {
input: {
lines: [{ merchandiseId: "gid://shopify/ProductVariant/123", quantity: 2 }],
buyerIdentity: { email: "customer@example.com", countryCode: "US" },
},
},
});
// Redirect to data.cartCreate.cart.checkoutUrl to complete purchase
Full cart mutations (cartLinesAdd, cartLinesUpdate, cartLinesRemove, cartDiscountCodesUpdate) in cart-api.md.
Step 3: Query Products (Storefront Schema)
// Storefront API schema differs from Admin API — field names are NOT the same
const { data } = await client.request(`
query { products(first: 10) { edges { node {
id title handle availableForSale
priceRange { minVariantPrice { amount currencyCode } }
variants(first: 5) { edges { node {
id title availableForSale
price { amount currencyCode } // MoneyV2 object, not a string
selectedOptions { name value }
}}}
}}}}
`);
Step 4: Storefront vs Admin API Decision Guide
| Concern |
Storefront API |
Admin API |
| Token type |
Public (safe in browser) |
Private (server-only) |
| Rate limiting |
Request-based |
Query cost-based (1000 pts/sec) |
| Cart/Checkout |
Full cart + checkout URL |
No cart operations |
| Product data |
Customer-facing fields only |
Full data + inventory |
| Mutations |
Cart, customer, checkout |
Full CRUD on all resources |
Detailed comparison in
'Optimize Shopify theme performance for Core Web Vitals (LCP, CLS, INP).
ReadWriteEditBash(npm:*)Grep
Shopify Theme Performance
Overview
59% of Shopify stores lazy-load their LCP image, adding 3-5 seconds to perceived load time. This skill covers the highest-impact theme optimizations: fixing LCP image loading, switching to the modern image_url filter, profiling Liquid render times, and optimizing font delivery.
Prerequisites
- Access to the Shopify theme editor or theme files via CLI (
shopify theme dev)
- Theme using Online Store 2.0 architecture (sections + JSON templates)
- Google Chrome DevTools or Lighthouse for measurement
Instructions
Step 1: Fix LCP Image Loading
The LCP element is usually the hero banner image. Find it in your theme's hero section:
{% comment %} BAD: lazy-loading the LCP image costs 3-5s {% endcomment %}
{{ section.settings.hero_image | image_url: width: 1200 | image_tag: loading: 'lazy' }}
{% comment %} GOOD: eager load + fetchpriority for LCP {% endcomment %}
{{ section.settings.hero_image | image_url: width: 1200 | image_tag:
loading: 'eager',
fetchpriority: 'high',
sizes: '100vw' }}
Add a preload hint in theme.liquid inside :
{%- if template.name == 'index' -%}
<link rel="preload"
href="{{ section.settings.hero_image | image_url: width: 1200 }}"
as="image"
fetchpriority="high">
{%- endif -%}
Step 2: Use the image_url Filter
The modern imageurl filter replaces the deprecated imgurl. It supports responsive images via srcset:
{% assign image = product.featured_image %}
<img src="{{ image | image_url: width: 800 }}"
srcset="{{ image | image_url: width: 400 }} 400w,
{{ image | image_url: width: 600 }} 600w,
{{ image | image_url: width: 800 }} 800w,
{{ image | image_url: width: 1200 }} 1200w"
sizes="(max-width: 749px) 100vw, 50vw"
width="{{ image.width }}"
height="{{ image.height }}"
loading="lazy"
alt="{{ image.alt | escape }}">
Always set explicit width and height attributes to prevent CLS (Cumulative Layout Shift).
Step 3: Liquid Profiler
Append ?profile=true to any storefront URL to activate the Liquid profiler. It renders a table at the bottom of the page showing render times per snippet.
See references/liquid-profiling.md for how to read the output and common slow patterns.
Step 4: Font Loading
Preload critical fonts and use font-display: swap to prevent invisible te
'Upgrade Shopify API versions and migrate from REST to GraphQL with breaking.
ReadWriteEditBash(npm:*)Bash(curl:*)
Shopify Upgrade & Migration
Overview
Guide for upgrading Shopify API versions (quarterly releases) and migrating from the legacy REST Admin API to the GraphQL Admin API. REST was deprecated as a legacy API on October 1, 2024.
Prerequisites
- Current Shopify API version identified
- Git for version control
- Test suite available
- Access to Shopify release notes
Instructions
Step 1: Check Current Version and Available Versions
# Check what API version you're using in code
grep -r "apiVersion" src/ --include="*.ts" --include="*.js"
grep -r "api_version" . --include="*.toml"
# Check what versions the store supports
curl -s -H "X-Shopify-Access-Token: $TOKEN" \
"https://$STORE/admin/api/versions.json" \
| jq '.supported_versions[] | {handle, display_name, supported, latest}'
Shopify releases quarterly (e.g., 2025-01, 2025-04, 2025-07, 2025-10). Versions are supported for ~12 months after release.
Step 2: Review Breaking Changes
Key breaking changes by version:
| Version |
Breaking Change |
Migration |
| 2024-10 |
ProductInput split into ProductCreateInput + ProductUpdateInput |
Update mutations to use separate types |
| 2024-10 |
REST declared legacy |
Migrate to GraphQL Admin API |
| 2024-07 |
InventoryItem.unitCost removed |
Use InventoryItem.unitCost on InventoryLevel |
| 2024-04 |
Cart warnings replace inventory userErrors (Storefront) |
Update cart error handling |
| 2025-01 |
New public apps must use GraphQL only |
No REST for new public apps |
Step 3: Migrate REST to GraphQL
Side-by-side comparison of REST vs GraphQL patterns, plus a mapping table for common endpoints (products, orders, customers, webhooks).
See REST to GraphQL Migration for the complete examples and mapping table.
Step 4: Update API Version in Config
// src/shopify.ts — use LATEST_API_VERSION instead of hardcoded dates
import { LATEST_API_VERSION } from "@shopify/shopify-api";
const shopify = shopifyApi({
apiKey: process.env.SHOPIFY_API_KEY!,
apiSecretKey: process.env.SHOPIFY_API_SECRET!,
hostName: process.env.SHOPIFY_HOST_NAME!,
apiVersion: LATEST_API_VERSION,
// ...
});
# shopify.app.toml
[webhooks]
api_version = "2025-04" # Update quarterly
Step 5: Handle the ProductInput Sp
'Register and handle Shopify webhooks including mandatory GDPR compliance.
ReadWriteEditBash(curl:*)
Shopify Webhooks & Events
Overview
Register webhooks via GraphQL, handle events with HMAC verification, and implement the mandatory GDPR compliance webhooks required for Shopify App Store submission.
Prerequisites
- Shopify app with API credentials configured
- HTTPS endpoint accessible from the internet (use
shopify app dev tunnel for local)
- API secret for HMAC webhook verification
Instructions
Step 1: Register Webhooks via GraphQL
Use the webhookSubscriptionCreate mutation with WebhookSubscriptionTopic and WebhookSubscriptionInput to register subscriptions for all critical event topics (orders, products, customers, inventory, app lifecycle).
See Webhook Registration for the complete implementation.
Step 2: Configure Mandatory GDPR Webhooks
Required for App Store submission. These are configured in shopify.app.toml, not via API:
# shopify.app.toml
[webhooks]
api_version = "2025-04" # Update quarterly
# MANDATORY: customers/data_request
[[webhooks.subscriptions]]
topics = ["customers/data_request"]
uri = "/webhooks/gdpr/data-request"
# MANDATORY: customers/redact
[[webhooks.subscriptions]]
topics = ["customers/redact"]
uri = "/webhooks/gdpr/customers-redact"
# MANDATORY: shop/redact
[[webhooks.subscriptions]]
topics = ["shop/redact"]
uri = "/webhooks/gdpr/shop-redact"
Step 3: Implement GDPR Webhook Handlers
Three mandatory handlers: (1) customer data request -- collect and send all data for a customer, (2) customer redact -- delete customer personal data and specified orders, (3) shop redact -- delete ALL shop data 48 hours after uninstall.
See GDPR Webhook Handlers for the complete implementation.
Step 4: Event Handler Pattern
A typed webhook dispatcher maps topics to handler functions. Verifies HMAC first, responds 200 immediately, then processes asynchronously. Unknown topics are logged but not rejected.
See Event Handler Pattern for the complete implementation.
Step 5: List and Manage Existing Webhooks
// Query all webhook subscriptions
const LIST_WEBHOOKS = `{
webhookSubscriptions(first: 50) {
edges {
node {
id
topic
endpoint {
... on WebhookHttpEndpoint { callbackUrl }
}
format
createdAt
}
}
}
}`;
// Delete a webhook
const DELETE_WEBHOOK = `
mutation webhookSubscriptionDelete($id: ID!) {
webhookSubscriptionDelete(id: $id) {
deletedWebhookSubscriptionId
userErrors { field mes
How It Works
1. Install the Pack
/plugin install shopify-pack@claude-code-plugins-plus
2. Create a Development Store
Go to partners.shopify.com and create a free development store for testing.
3. Set Up Your First App
npm install -g @shopify/cli
shopify app init --template remix
cd my-shopify-app
shopify app dev
Or set up a standalone integration:
mkdir shopify-app && cd shopify-app
npm init -y
npm install @shopify/shopify-api dotenv
4. Make Your First API Call
import "@shopify/shopify-api/adapters/node";
import { shopifyApi, LATEST_API_VERSION } from "@shopify/shopify-api";
const shopify = shopifyApi({
apiKey: process.env.SHOPIFY_API_KEY!,
apiSecretKey: process.env.SHOPIFY_API_SECRET!,
hostName: "localhost",
apiVersion: LATEST_API_VERSION,
isCustomStoreApp: true,
adminApiAccessToken: process.env.SHOPIFY_ACCESS_TOKEN!,
});
const session = shopify.session.customAppSession("your-store.myshopify.com");
const client = new shopify.clients.Graphql({ session });
const response = await client.request(`{
shop { name }
products(first: 5) {
edges { node { id title status } }
}
}`);
console.log("Store:", response.data.shop.name);
5. Go to Production
Follow shopify-prod-checklist for the complete launch checklist including GDPR webhooks, security review, and App Store submission requirements.
Ready to use shopify-pack?
|
|
|
|