关于 shopify-admin-graphql
shopify-admin-graphql 是面向通过 GraphQL 调用 Shopify Admin API 的代码开发技能,适用于应用和后台任务。内容涵盖身份验证、常见查询、分页、限流和错误处理。
主要功能
- 01通过 Remix 身份验证访问 API
- 02使用在线和离线管理会话
- 03查询店铺、产品、客户和订单
- 04为大型数据集实现分页
- 05处理限流和 GraphQL 错误
- 06遵循 GraphQL 查询最佳实践
适用场景
- 01在 Remix loaders 中编写 Shopify 数据查询
- 02通过 GraphQL mutation 更新 Shopify 记录
- 03在无请求上下文时处理 webhook
- 04通过分页和重试获取大型列表
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ tamiror6/ shopify-app-skills --skill shopify-admin-graphql安装后在 Claude Code 中输入/shopify-admin-graphql确认已加载。
详细介绍
Shopify Admin GraphQL
在添加或修改通过 GraphQL 调用 Shopify Admin API 的代码时使用此技能。
使用场景
- 查询 Shopify 数据(商店、客户、订单、产品、库存)
- 修改 Shopify 数据(创建或更新客户、订单、产品)
- 为大型数据集实现分页
- 处理 API 节流和速率限制
- 使用 metafield 或其他 Shopify 资源
获取 GraphQL 客户端
在 Remix 加载器/操作中(推荐)
使用 @shopify/shopify-app-remix 中的 authenticate.admin():
import { authenticate } from "../shopify.server";
export const loader = async ({ request }: LoaderFunctionArgs) => {
const { admin } = await authenticate.admin(request);
const response = await admin.graphql(`
query GetShopDetails {
shop {
id
name
myshopifyDomain
plan {
displayName
}
}
}
`);
const { data } = await response.json();
return json({ shop: data.shop });
};
使用变量
export const action = async ({ request }: ActionFunctionArgs) => {
const { admin } = await authenticate.admin(request);
const formData = await request.formData();
const email = formData.get("email") as string;
// Validate email format to prevent query manipulation
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!email || !emailRegex.test(email)) {
return json({ error: "Invalid email format" }, { status: 400 });
}
// Escape quotes and wrap in quotes to treat as literal value
const sanitizedEmail = email.replace(/"/g, '\\"');
const response = await admin.graphql(`
query FindCustomerByEmail($query: String!) {
customers(first: 1, query: $query) {
edges {
node {
id
email
phone
firstName
lastName
}
}
}
}
`, {
variables: { query: `email:"${sanitizedEmail}"` }
});
const { data } = await response.json();
return json({ customer: data.customers.edges[0]?.node });
};
后台任务 / Webhook(离线访问)
没有请求上下文时,请使用离线会话令牌:
import { unauthenticated } from "../shopify.server";
export async function processWebhook(shop: string) {
const { admin } = await unauthenticated.admin(shop);
const response = await admin.graphql(`
query GetShop {
shop {
name
}
}
`);
const { data } = await response.json();
return data.shop;
}
常见查询模式
商店详情
query GetShopDetails {
shop {
id
name
email
myshopifyDomain
primaryDomain {
url
}
plan {
displayName
}
currencyCode
timezoneAbbreviation
}
}
带分页的产品查询
query GetProducts($first: Int!, $after: String) {
products(first: $first, after: $after) {
pageInfo {
hasNextPage
endCursor
}
edges {
node {
id
title
handle
status
variants(first: 10) {
edges {
node {
id
title
price
sku
}
}
}
}
}
}
}
客户查找
query FindCustomer($query: String!) {
customers(first: 1, query: $query) {
edges {
node {
id
email
phone
firstName
lastName
ordersCount
totalSpent
}
}
}
}
按 ID 查询订单
query GetOrder($id: ID!) {
order(id: $id) {
id
name
email
phone
totalPriceSet {
shopMoney {
amount
currencyCode
}
}
lineItems(first: 50) {
edges {
node {
title
quantity
variant {
id
sku
}
}
}
}
shippingAddress {
address1
city
country
}
}
}
处理节流
Shopify 使用漏桶算法限制速率。对于批量操作或后台任务,请实现重试逻辑:
interface RetryOptions {
maxRetries?: number;
initialDelay?: number;
maxDelay?: number;
}
async function executeWithRetry<T>(
admin: AdminApiContext,
query: string,
variables?: Record<string, unknown>,
options: RetryOptions = {}
): Promise<T> {
const { maxRetries = 3, initialDelay = 1000, maxDelay = 10000 } = options;
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
const response = await admin.graphql(query, { variables });
const result = await response.json();
if (result.errors?.some((e: any) => e.extensions?.code === "THROTTLED")) {
throw new Error("THROTTLED");
}
return result.data as T;
} catch (error) {
if (attempt === maxRetries) throw error;
const delay = Math.min(initialDelay * Math.pow(2, attempt), maxDelay);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
throw new Error("Max retries exceeded");
}
错误处理
const response = await admin.graphql(query, { variables });
const { data, errors } = await response.json();
if (errors) {
// Handle GraphQL errors
console.error("GraphQL errors:", errors);
// Check for specific error types
const throttled = errors.some((e: any) =>
e.extensions?.code === "THROTTLED"
);
const notFound = errors.some((e: any) =>
e.message?.includes("not found")
);
if (throttled) {
// Retry with backoff
}
}
最佳实践
- 使用操作名称,便于调试:
query GetShopDetails { ... } - 仅请求所需字段,以减小响应体并提升性能
- 对列表使用分页——不要请求无界数据
- 妥善处理错误——同时检查
errors数组和 HTTP 错误 - 为后台任务实现带指数退避的重试
- 对多个查询中重复的字段选择使用片段
- 对于包含关联关系的复杂查询,优先使用 GraphQL 而非 REST
参考资料
评论
支持 Markdown