Use this skill when the user asks about "Shopify GraphQL", "Admin API", "metafields", "webhooks", "rate limiting", "pagination", "App Bridge", or any Shopify API integration work...
Always verify API version before implementing!
Shopify deprecates API versions regularly. Check:
shopify.app.toml or app config// Check what version your app uses
// shopify.app.toml
[api]
api_version = "2024-10" // Verify this matches your implementation
| Need | Solution |
|---|---|
| Customize checkout UI | Checkout UI Extension |
| Apply discounts | Discount Function |
| Validate cart | Cart Validation Function |
| React to events | Webhooks |
| Read/write data | GraphQL Admin API |
| Sync large data | Bulk Operations |
| Store custom data | Metafields/Metaobjects |
const query = `
query getProduct($id: ID!) {
product(id: $id) {
id
title
handle
variants(first: 10) {
nodes {
id
price
}
}
}
}
`;
const response = await shopify.graphql(query, { id: productId });
async function getAllProducts(shopify) {
const products = [];
let hasNextPage = true;
let cursor = null;
while (hasNextPage) {
const query = `
query getProducts($cursor: String) {
products(first: 50, after: $cursor) {
pageInfo { hasNextPage }
edges {
cursor
node { id title }
}
}
}
`;
const response = await shopify.graphql(query, { cursor });
const { edges, pageInfo } = response.products;
products.push(...edges.map(e => e.node));
hasNextPage = pageInfo.hasNextPage;
cursor = edges[edges.length - 1]?.cursor;
}
return products;
}
Before implementing any Shopify data sync, ask: "Can this hit API limits?"
Rate Limits Context:
| Volume | Strategy |
|---|---|
| < 50 items | Regular GraphQL |
| 50-500 items | Batch with Cloud Tasks + rate limiting |
| 500+ items | Bulk Operations API |
For detailed bulk mutation patterns, see: shopify-bulk-operations skill
// BAD: In-function sleep wastes CPU time
await sleep(60000); // 60s sleep = 60s CPU billed
// GOOD: Schedule retry with Cloud Tasks
async function scheduleRetry(payload, delaySeconds) {
await client.createTask({
parent: client.queuePath(project, location, 'shopify-retry'),
task: {
httpRequest: {
url: `${baseUrl}/api/retry-shopify`,
body: Buffer.from(JSON.stringify(payload)).toString('base64'),
headers: { 'Content-Type': 'application/json' }
},
scheduleTime: {
seconds: Math.floor(Date.now() / 1000) + delaySeconds
}
}
});
}
const mutation = `
mutation metafieldsSet($metafields: [MetafieldsSetInput!]!) {
metafieldsSet(metafields: $metafields) {
metafields { id key value }
userErrors { field message }
}
}
`;
await shopify.graphql(mutation, {
metafields: [
{
ownerId: customerId,
namespace: 'loyalty',
key: 'points',
type: 'number_integer',
value: '500'
},
{
ownerId: customerId,
namespace: 'loyalty',
key: 'tier',
type: 'single_line_text_field',
value: 'Gold'
}
]
});
Must respond within 5 seconds!
// BAD: Heavy processing (may timeout)
app.post('/webhooks/orders/create', async (req, res) => {
await calculatePoints(req.body);
await updateCustomer(req.body);
await syncToShopify(req.body);
res.status(200).send('OK');
});
// GOOD: Queue and respond fast
app.post('/webhooks/orders/create', async (req, res) => {
// Quick validation
if (!verifyHmac(req)) {
return res.status(401).send('Unauthorized');
}
// Queue for background processing
await webhookQueueRef.add({
type: 'orders/create',
payload: req.body
});
// Respond immediately
res.status(200).send('OK');
});
import crypto from 'crypto';
function verifyHmac(req) {
const hmac = req.get('X-Shopify-Hmac-Sha256');
const body = req.rawBody;
const secret = process.env.SHOPIFY_WEBHOOK_SECRET;
const hash = crypto
.createHmac('sha256', secret)
.update(body, 'utf8')
.digest('base64');
return crypto.timingSafeEqual(
Buffer.from(hmac),
Buffer.from(hash)
);
}
| Scenario | Use App Bridge | Use Firebase API |
|---|---|---|
| Simple Shopify CRUD | Yes | No |
| Need Firestore data | No | Yes |
| Complex business logic | No | Yes |
| Background processing | No | Yes |
import { authenticatedFetch } from '@shopify/app-bridge/utilities';
async function fetchProducts(app) {
const response = await authenticatedFetch(app)(
'/admin/api/2024-04/graphql.json',
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
query: `{ products(first: 10) { nodes { id title } } }`
})
}
);
return response.json();
}
Benefits: