Shopify Admin API 深度应用
Shopify Admin API 是 Shopify 后端能力对外暴露的核心入口。所有应用、ERP / WMS 同步、自动化集成最终都要走这套 API。本文聚焦在实战开发中真正影响项目成败的几个关键点:选型决策、限流机制、批量操作、版本管理。
不涉及基础调用入门——基础部分见 Shopify 官方 API 文档 。
一、GraphQL vs REST 选型
Shopify 2018 年起把 GraphQL 作为推荐方向,REST API 自 2024 年 10 月起进入维护模式(仅安全修复,不再新增功能)。新项目应优先使用 GraphQL。
关键差异
| 维度 | REST | GraphQL |
|---|---|---|
| 数据获取 | 多次请求拼装 | 单次请求获取嵌套数据 |
| 接口稳定性 | 字段固定 | 客户端按需查询 |
| 限流计算 | 每秒请求数(leaky bucket) | 查询成本(cost-based) |
| 文档与示例 | 历史积累丰富 | 较新但完整 |
| 复杂查询 | 需要多次往返 | 一次往返 |
| 工具支持 | curl / Postman 友好 | GraphQL IDE 支持 |
| Shopify 官方推荐 | 不再 | 推荐 |
何时仍可用 REST
- 维护老代码且未在迁移计划中
- 简单的单一资源 CRUD
- 第三方库仅支持 REST
新功能 / 新接口(如 Cart API 部分)只在 GraphQL 提供。2025 年起的所有新项目都应优先 GraphQL。
选型实例
获取 50 个产品 + 每个产品的图片 + 变体 + 库存:
- REST:3-4 次请求(products + 每个产品的 images + variants + inventory levels)
- GraphQL:1 次请求
query GetProductsWithDetails($first: Int!) {
products(first: $first) {
edges {
node {
id
title
handle
descriptionHtml
images(first: 10) {
edges {
node {
url(transform: { maxWidth: 800 })
altText
width
height
}
}
}
variants(first: 100) {
edges {
node {
id
sku
price
inventoryQuantity
selectedOptions { name value }
}
}
}
}
}
pageInfo {
hasNextPage
endCursor
}
}
}二、限流机制详解
Shopify API 限流是初学者最常踩的坑。两套 API 用不同的限流模型:
REST:Leaky Bucket
- 每个店铺有”桶容量”40 请求(Plus 商家更高)
- 桶以每秒 2 个请求的速度漏水
- 请求填桶,桶满后 429 拒绝
应对:
- 监控 HTTP 响应 header
X-Shopify-Shop-Api-Call-Limit(如35/40) - 接近 40 时延迟下次请求
- 收到 429 后按响应 header
Retry-After等待
async function callShopifyRest(endpoint, options) {
const response = await fetch(endpoint, options);
const limit = response.headers.get('x-shopify-shop-api-call-limit');
const [current, max] = limit.split('/').map(Number);
if (current > max * 0.8) {
await sleep(500);
}
if (response.status === 429) {
const retryAfter = parseInt(response.headers.get('retry-after') || '2');
await sleep(retryAfter * 1000);
return callShopifyRest(endpoint, options);
}
return response.json();
}GraphQL:Cost-Based
- 每个店铺有”成本桶” 1000 点(Plus 更高)
- 桶以每秒 50 点的速度恢复
- 每个查询消耗一定点数(基础 1 点,连接字段按返回数量)
成本计算:
query {
products(first: 100) { # 成本 = 100(连接字段按 first 计算)
edges {
node {
id # 0
variants(first: 10) { # 100 × 10 = 1000(嵌套连接乘法)
edges {
node { id price }
}
}
}
}
}
}
# 总成本 ≈ 1100 点,单次查询即可超出桶容量应对:
- 在响应
extensions.cost中查看实际消耗 - 减少 first 参数或拆多次查询
- 用 fragment 但不要无限嵌套
const result = await graphqlClient.query({ query: PRODUCTS_QUERY });
console.log('Cost:', result.extensions.cost);
// {
// requestedQueryCost: 1100,
// actualQueryCost: 250, // 实际成本基于返回数量
// throttleStatus: {
// maximumAvailable: 1000,
// currentlyAvailable: 750,
// restoreRate: 50
// }
// }Bulk Operations(批量操作)
需要导出全店数据时不要用普通分页查询——会消耗大量配额。使用 GraphQL Bulk Operations:
mutation {
bulkOperationRunQuery(
query: """
{
products {
edges {
node {
id
title
variants {
edges {
node { id sku }
}
}
}
}
}
}
"""
) {
bulkOperation { id status }
userErrors { field message }
}
}特点:
- 异步执行(提交后立即返回)
- 完成后下载 JSONL 文件
- 不消耗主限流配额
- 适合全量导出(10万+ 产品的店铺)
每个店铺同时只能跑 1 个 Bulk Operation。
三、Webhook 集成
实时事件推送,避免轮询消耗 API 配额。
常用事件
| 事件 | 用途 |
|---|---|
| orders/create | 新订单 → 同步到 ERP |
| orders/updated | 订单变更 → 状态同步 |
| orders/paid | 支付完成 → 触发履约 |
| orders/fulfilled | 发货 → 通知客户 |
| products/create / update | 产品同步 |
| inventory_levels/update | 库存同步 |
| customers/create | 新客户 → CRM 同步 |
| customers/data_request | GDPR 请求 |
| customers/redact | GDPR 删除 |
| shop/redact | 店铺卸载 App 后清理数据 |
注册 Webhook
mutation {
webhookSubscriptionCreate(
topic: ORDERS_CREATE
webhookSubscription: {
callbackUrl: "https://yourapp.com/webhooks/orders-create"
format: JSON
}
) {
webhookSubscription { id }
userErrors { field message }
}
}验证 Webhook 签名
Shopify 在 HTTP header X-Shopify-Hmac-Sha256 提供签名。所有 Webhook 必须验签,否则接口可能被恶意伪造调用:
const crypto = require('crypto');
function verifyWebhook(rawBody, hmacHeader, secret) {
const calculated = crypto
.createHmac('sha256', secret)
.update(rawBody, 'utf8')
.digest('base64');
return crypto.timingSafeEqual(
Buffer.from(calculated),
Buffer.from(hmacHeader)
);
}
app.post('/webhooks/orders-create', (req, res) => {
const hmac = req.headers['x-shopify-hmac-sha256'];
const rawBody = req.rawBody; // 原始 body,未 JSON.parse
if (!verifyWebhook(rawBody, hmac, SHOPIFY_WEBHOOK_SECRET)) {
return res.status(401).send('Unauthorized');
}
// 处理事件
res.status(200).send('OK');
});Webhook 处理原则
- 5 秒内响应 200:Shopify 等待时间上限 5 秒
- 重试机制:超时或返回 5xx 时 Shopify 自动重试(最多 19 次,48 小时内)
- 幂等性:同一事件可能被多次推送,处理函数必须幂等
- 异步处理:复杂业务逻辑放队列异步处理,先 200 返回
app.post('/webhooks/orders-create', async (req, res) => {
// 立即验签并响应
if (!verifyWebhook(...)) return res.status(401).send('Unauthorized');
// 入队,不阻塞响应
await queue.add('process-order', { body: req.body });
res.status(200).send('OK');
});四、Access Token 管理
Token 类型
- Public App Token:通过 OAuth 流程获取,给已安装店铺
- Custom App Token:店主在后台创建,用于私有集成
- Storefront API Token:用于面向公众的 Headless 站点
安全要点
- 永远不要硬编码 token:用环境变量或密钥管理服务
- 最小权限:只申请实际需要的 scope
- 定期轮换:每 6-12 个月更换一次
- 泄露应急:在 Shopify Partner 后台撤销并重新生成
Scopes 选择原则
Shopify 把权限拆得很细。常用:
| Scope | 用途 |
|---|---|
| read_products / write_products | 产品读写 |
| read_orders / write_orders | 订单读写 |
| read_customers / write_customers | 客户读写 |
| read_inventory / write_inventory | 库存读写 |
| read_fulfillments / write_fulfillments | 履约 |
| read_locations | 多仓 |
| read_price_rules / write_price_rules | 折扣规则 |
只申请必要的 scope——app review 阶段超出实际需要的 scope 会被 Shopify 退回审核。
五、API 版本管理
版本周期
Shopify 每季度发布新 API 版本,命名 YYYY-MM:
- 2024-01(1 月发布)
- 2024-04(4 月发布)
- 2024-07(7 月发布)
- 2024-10(10 月发布)
每个版本支持 12 个月,过期后强制升级。未升级的应用会被自动迁移到下一个稳定版,可能触发 breaking change。
升级策略
- 监控 Shopify Changelog
- 测试环境验证后再升级生产
- 升级前查阅 Migration Guide
调用版本指定
每次 API 调用都应该显式指定版本:
GET /admin/api/2024-10/products.jsonGraphQL 同理:
POST /admin/api/2024-10/graphql.json不要使用 unstable——它是开发预览,每周可能变更。
六、常见集成场景
场景 1:ERP 同步
ERP(NetSuite / 用友等) ↔ Shopify
订单方向:
Shopify orders/create webhook
→ 服务器解析
→ 推送到 ERP
→ 创建 ERP 订单
库存方向:
ERP 库存变化
→ 调 Shopify GraphQL mutation inventorySetOnHandQuantities
→ 同步到 Shopify注意:
- 双向同步必须有”事件来源”标记,避免无限循环
- 网络异常时本地队列重试
- 月度对账机制
场景 2:第三方仓库(3PL)集成
Shopify orders/paid → 推送到 3PL → 3PL 发货 → fulfillment 回写 Shopify关键点:
- 用 fulfillmentCreate mutation 完成发货
- 物流追踪号回传:
trackingInfo字段 - 部分发货支持(拆单)
场景 3:自定义 Checkout 后处理
orders/paid webhook
→ 异步处理:发优惠券、加会员积分、推送 CRM不要在 Checkout 同步流程里做这些——会拖慢结账。
场景 4:客户数据导出(GDPR)
强制实现两个 Webhook:
customers/data_request:客户请求数据 → 30 天内提供customers/redact:客户请求删除 → 30 天内删除
详见 GDPR 合规完整指南。
七、性能与监控
客户端缓存
- 不变数据(产品分类、店铺信息)本地缓存
- 缓存失效靠 Webhook 触发(如 products/update)
- 不要缓存订单或库存等高频变化数据
重试策略
- 网络错误:指数退避(1s, 2s, 4s, 8s)
- 429 限流:按 Retry-After 等待
- 5xx 错误:最多 3 次重试
监控指标
- API 调用次数 / 错误率 / 延迟(按 endpoint)
- 限流命中次数
- Webhook 接收延迟 / 失败率
- Bulk Operation 完成时间
八、避免的常见错误
错误 1:在循环中调 API
// 反例:N+1 问题
for (const productId of productIds) {
const product = await fetchProduct(productId); // 多次单独调用
}
// 正确:批量查询
const products = await graphqlQuery(`
query { nodes(ids: ${JSON.stringify(productIds)}) { ... } }
`);错误 2:硬编码 API 版本
// 反例:永远用 2022-01,无法升级
const url = 'https://shop.myshopify.com/admin/api/2022-01/products.json';
// 正确:版本变量化
const API_VERSION = process.env.SHOPIFY_API_VERSION || '2024-10';
const url = `https://shop.myshopify.com/admin/api/${API_VERSION}/products.json`;错误 3:忽略 Webhook 重试
如果 Webhook 处理超过 5 秒,Shopify 会重试。如果处理函数不幂等,会重复处理订单。
错误 4:写权限申请过大
申请了 write_products 但只读产品。审核会被 Shopify 退回,且增加安全风险。
错误 5:不验签直接处理 Webhook
恶意第三方可以伪造 Webhook 攻击。所有 Webhook 必须验签。
错误 6:缺少错误处理
// 反例
const data = await fetch(url).then(r => r.json());
// 正确
const response = await fetch(url);
if (!response.ok) {
const text = await response.text();
throw new Error(`Shopify API ${response.status}: ${text}`);
}
const data = await response.json();
if (data.errors) {
throw new Error(`GraphQL errors: ${JSON.stringify(data.errors)}`);
}九、开发工具
必备工具
- Shopify CLI:脚手架与本地开发
- GraphiQL App:Shopify 自家的 GraphQL IDE
- Postman 与 Shopify Postman Collection:REST 测试
- Shopify Dev Console:API 调用日志查看
推荐工具
- GraphQL Codegen:从 GraphQL Schema 自动生成 TypeScript 类型
- @shopify/shopify-api:官方 Node.js SDK
- @shopify/cli-hydrogen:Headless 项目脚手架