Common Shopify Admin GraphQL API patterns for product queries, metafield operations, webhooks, and bulk operations. Auto-invoked when working with Shopify API integration.
Provides reusable patterns for common Shopify Admin GraphQL API operations including product queries, metafield management, webhook handling, and bulk operations.
query getProducts($first: Int!, $after: String) {
products(first: $first, after: $after) {
edges {
node {
id
title
vendor
handle
productType
tags
variants(first: 10) {
edges {
node {
id
title
price
sku
}
}
}
}
cursor
}
pageInfo {
hasNextPage
endCursor
}
}
}
query getProductMetafields($productId: ID!) {
product(id: $productId) {
id
title
metafields(first: 20, namespace: "custom") {
edges {
node {
id
namespace
key
value
type
}
}
}
}
}
mutation updateMetafields($metafields: [MetafieldsSetInput!]!) {
metafieldsSet(metafields: $metafields) {
metafields {
id
namespace
key
value
type
}
userErrors {
field
message
}
}
}
Usage Example:
const response = await admin.graphql(UPDATE_METAFIELDS, {
variables: {
metafields: [
{
ownerId: "gid://shopify/Product/123",
namespace: "custom",
key: "color",
value: "Red",
type: "single_line_text_field",
},
],
},
});
mutation createMetafieldDefinition($definition: MetafieldDefinitionInput!) {
metafieldDefinitionCreate(definition: $definition) {
createdDefinition {
id
name
namespace
key
type
ownerType
}
userErrors {
field
message
}
}
}
Usage:
await admin.graphql(CREATE_METAFIELD_DEFINITION, {
variables: {
definition: {
name: "Product Color",
namespace: "custom",
key: "color",
type: "single_line_text_field",
ownerType: "PRODUCT",
},
},
});
mutation registerWebhook($topic: WebhookSubscriptionTopic!, $webhookSubscription: WebhookSubscriptionInput!) {
webhookSubscriptionCreate(topic: $topic, webhookSubscription: $webhookSubscription) {
webhookSubscription {
id
topic
endpoint {
__typename
... on WebhookHttpEndpoint {
callbackUrl
}
}
}
userErrors {
field
message
}
}
}
Common Topics:
PRODUCTS_CREATEPRODUCTS_UPDATEPRODUCTS_DELETEORDERS_CREATECUSTOMERS_CREATEasync function fetchAllProducts(admin) {
let hasNextPage = true;
let cursor = null;
const allProducts = [];
while (hasNextPage) {
const response = await admin.graphql(GET_PRODUCTS, {
variables: { first: 250, after: cursor },
});
const data = await response.json();
if (data.errors) {
throw new Error(`GraphQL error: ${data.errors[0].message}`);
}
const products = data.data.products.edges.map(edge => edge.node);
allProducts.push(...products);
hasNextPage = data.data.products.pageInfo.hasNextPage;
cursor = data.data.products.pageInfo.endCursor;
// Rate limiting check
const rateLimitCost = response.headers.get("X-Shopify-Shop-Api-Call-Limit");
if (rateLimitCost) {
const [used, total] = rateLimitCost.split("/").map(Number);
if (used > total * 0.8) {
await new Promise(resolve => setTimeout(resolve, 1000));
}
}
}
return allProducts;
}
mutation bulkOperationRunQuery {
bulkOperationRunQuery(
query: """
{
products {
edges {
node {
id
title
metafields {
edges {
node {
namespace
key
value
}
}
}
}
}
}
}
"""
) {
bulkOperation {
id
status
}
userErrors {
field
message
}
}
}
Check Status:
query {
currentBulkOperation {
id
status
errorCode
createdAt
completedAt
objectCount
fileSize
url
}
}
Download and Process Results:
async function processBulkOperationResults(url: string) {
const response = await fetch(url);
const jsonl = await response.text();
const lines = jsonl.trim().split("\n");
const results = lines.map(line => JSON.parse(line));
return results;
}
async function graphqlWithRetry(admin, query, variables, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
const response = await admin.graphql(query, { variables });
// Check rate limit
const rateLimitCost = response.headers.get("X-Shopify-Shop-Api-Call-Limit");
if (rateLimitCost) {
const [used, total] = rateLimitCost.split("/").map(Number);
console.log(`API calls: ${used}/${total}`);
if (used > total * 0.9) {
console.warn("Approaching rate limit, slowing down...");
await new Promise(resolve => setTimeout(resolve, 2000));
}
}
const data = await response.json();
if (data.errors) {
throw new Error(`GraphQL error: ${data.errors[0].message}`);
}
return data;
} catch (error) {
if (error.message.includes("Throttled") && i < maxRetries - 1) {
const delay = Math.pow(2, i) * 1000; // Exponential backoff
console.log(`Rate limited, retrying in ${delay}ms...`);
await new Promise(resolve => setTimeout(resolve, delay));
continue;
}
throw error;
}
}
}
errors and userErrors in responsessingle_line_text_field - Short textmulti_line_text_field - Long textnumber_integer - Whole numbersnumber_decimal - Decimal numbersjson - Structured datacolor - Color valuesurl - URLsboolean - True/falsedate - Date valueslist.single_line_text_field - Array of stringsquery getProductByHandle($handle: String!) {
productByHandle(handle: $handle) {
id
title
vendor
}
}
query getProductVariants($productId: ID!) {
product(id: $productId) {
variants(first: 100) {
edges {
node {
id
title
price
sku
inventoryQuantity
}
}
}
}
}
mutation productUpdate($input: ProductInput!) {
productUpdate(input: $input) {
product {
id
title
}
userErrors {
field
message
}
}
}
Remember: Always check the Shopify Admin API documentation for the latest schema and deprecations.
npx skills add sarojpunde/shopify-api-patterns下载完整 Skill 目录,包含 SKILL.md 及所有相关文件
Search for places (restaurants, cafes, etc.) via Google Places API proxy on localhost.
Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries.
Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Start voice calls via the OpenClaw voice-call plugin.
Notion API for creating and managing pages, databases, and blocks.
Gemini CLI for one-shot Q&A, summaries, and generation.
Category:developer