← 博客
Shopify AI Toolkit 实战指南:让 AI 成为你的开发加速器
Shopify AI Toolkit 实战指南,通过真实场景展示如何快速生成 GraphQL 查询、搭建 App 架构、自动化批量操作、处理 Webhook 与开发 UI 扩展,全面提升开发效率。
前言
从查阅文档到编写代码,从调试错误到优化性能,Shopify 开发者的日常工作中充斥着大量重复性劳动。
Shopify AI Toolkit 的推出,正在改变这一切。它不是一个简单的代码生成器,而是一个深度集成 Shopify 平台知识的智能开发助手。
本文不讲空泛的概念,直接通过实战案例,展示如何用 Shopify AI Toolkit 提升开发效率。
第一部分:快速开始
安装(一行命令搞定)
# Claude Code 用户
/plugin marketplace add Shopify/shopify-ai-toolkit
/plugin install shopify-plugin@shopify-ai-toolkit
# MCP 方式(推荐)
npm install -g @shopify/dev-mcp
# 配置到 Claude Desktop
# 在 ~/Library/Application Support/Claude/claude_desktop_config.json 添加:
{
"mcpServers": {
"shopify-dev-mcp": {
"command": "npx",
"args": ["-y", "@shopify/dev-mcp@latest"]
}
}
}前置要求: Node.js 18+,已安装 Claude Code / Cursor / VS Code
就这么简单,接下来直接看如何使用。
第二部分:实战场景
场景一:快速生成 GraphQL 查询
传统做法
打开 Shopify GraphQL 文档 → 查找需要的字段 → 手动编写查询 → 测试调试
AI Toolkit 做法
Prompt:
我需要获取最近 7 天内的所有订单,包括客户信息、订单金额和物流状态。请生成对应的 GraphQL 查询。AI 输出:
query GetRecentOrders($first: Int!) {
orders(first: $first, query: "created_at:>=7days ago") {
edges {
node {
id
orderNumber
createdAt
currentTotalPrice {
amount
currencyCode
}
customer {
id
email
firstName
lastName
}
fulfillments(first: 10) {
edges {
node {
id
status
trackingInfo {
company
trackingNumber
}
}
}
}
}
}
pageInfo {
hasNextPage
endCursor
}
}
}关键优势:
- • 字段准确,符合最新 Shopify GraphQL 规范
- • 包含分页逻辑
- • 自动处理时间筛选
- • 无需翻阅文档
进阶:自动生成 TypeScript 类型
Prompt:
基于上面的查询,生成 TypeScript 接口定义AI 输出:
interface Money {
amount: string;
currencyCode: string;
}
interface Customer {
id: string;
email: string;
firstName: string;
lastName: string;
}
interface TrackingInfo {
company: string;
trackingNumber: string;
}
interface Fulfillment {
id: string;
status: string;
trackingInfo: TrackingInfo;
}
interface Order {
id: string;
orderNumber: number;
createdAt: string;
currentTotalPrice: Money;
customer: Customer;
fulfillments: {
edges: Array<{
node: Fulfillment;
}>;
};
}
interface OrdersResponse {
orders: {
edges: Array<{
node: Order;
}>;
pageInfo: {
hasNextPage: boolean;
endCursor: string | null;
};
};
}场景二:创建 Shopify App 基础架构
Prompt:
创建一个 Shopify Remix app,包含以下功能:
1. 产品批量价格更新
2. 使用 Shopify Admin API
3. 包含权限请求配置
4. 使用 Polaris 组件库AI 会生成:
shopify.app.toml
name = "Batch Price Updater"
id = "your-app-id"
[access_scopes]
scopes = "write_products,read_products"
[app_settings]
embedded = true
[auth]
redirect_urls = [
"https://your-app.com/auth/callback",
"https://your-app.com/auth/shopify/callback"
]
[pos]
embedded = falseapp/routes/app.products.tsx
import { json, type LoaderFunctionArgs, type ActionFunctionArgs } from "@remix-run/node";
import { useLoaderData, Form } from "@remix-run/react";
import {
Page,
LegacyCard,
TextField,
Button,
Layout
} from "@shopify/polaris";
import { authenticate } from "../shopify.server";
export const loader = async ({ request }: LoaderFunctionArgs) => {
const { admin, session } = await authenticate.admin(request);
const response = await admin.graphql(
`#graphql
query GetProducts($first: Int!) {
products(first: $first) {
edges {
node {
id
title
handle
variants(first: 1) {
edges {
node {
id
price
unitPrice {
amount
currencyCode
}
}
}
}
}
}
}
}`,
{
variables: { first: 50 }
}
);
const data = await response.json();
return json(data.data.products);
};
export const action = async ({ request }: ActionFunctionArgs) => {
const { admin } = await authenticate.admin(request);
const formData = await request.formData();
const updates: Array<{ id: string; price: string }> = [];
for (const [key, value] of formData.entries()) {
if (key.startsWith("price-")) {
const variantId = key.replace("price-", "");
updates.push({
id: variantId,
price: value.toString(),
});
}
}
const UPDATE_MUTATION = `
mutation productVariantUpdate($input: ProductVariantInput!) {
productVariantUpdate(input: $input) {
productVariant {
id
price
}
userErrors {
field
message
}
}
}
`;
try {
for (const update of updates) {
await admin.graphql(UPDATE_MUTATION, {
variables: {
input: {
id: update.id,
price: update.price,
},
},
});
}
return json({ success: true, updatedCount: updates.length });
} catch (error) {
return json(
{ success: false, error: error.message },
{ status: 500 }
);
}
};
export default function Products() {
const products = useLoaderData();
return (
<Page title="批量价格更新">
<Layout>
<Layout.Section>
<LegacyCard sectioned>
<Form method="post">
{products?.edges?.map(({ node }: any) => (
<LegacyCard.Section
key={node.id}
title={node.title}
>
<TextField
label="价格"
name={`price-${node.variants.edges[0]?.node.id}`}
defaultValue={node.variants.edges[0]?.node.price}
type="number"
prefix="$"
/>
</LegacyCard.Section>
))}
<Button submit variant="primary">
更新所有价格
</Button>
</Form>
</LegacyCard>
</Layout.Section>
</Layout>
</Page>
);
}关键点说明:
- • 自动处理认证:
authenticate.admin()自动验证 Shopify 会话 - • GraphQL 集成:直接使用 admin GraphQL 客户端
- • Polaris 组件:使用 Shopify 设计系统,保证一致性
- • 类型安全:自动推断 GraphQL 响应类型
- • 完整的 CRUD:loader 读取数据,action 更新数据
场景三:批量操作自动化
需求: 将所有产品的描述翻译成中文
Prompt:
使用 Shopify Bulk Operations 编写一个批量操作,将所有英文产品描述翻译成中文。要求:
1. 使用 GraphQL bulkOperationRunQuery
2. 分批处理,每批 500 个产品
3. 包含错误处理和重试机制
4. 使用 Remix action 实现
5. 添加 API 速率限制处理AI 生成代码:
import { json, type ActionFunctionArgs } from "@remix-run/node";
import { authenticate } from "../shopify.server";
import pLimit from 'p-limit';
const PRODUCT_UPDATE_MUTATION = `
mutation productUpdate($input: ProductInput!) {
productUpdate(input: $input) {
product {
id
descriptionHtml
}
userErrors {
field
message
}
}
}
`;
const BULK_QUERY = `
mutation {
bulkOperationRunQuery(
query: """
{
products {
edges {
node {
__typename
id
title
descriptionHtml
}
}
}
}
"""
) {
bulkOperation {
id
status
}
userErrors {
field
message
}
}
}
`;
const limit = pLimit(2);
export const action = async ({ request }: ActionFunctionArgs) => {
const { admin } = await authenticate.admin(request);
try {
const bulkResponse = await admin.graphql(BULK_QUERY);
const bulkData = await bulkResponse.json();
if (bulkData.data?.bulkOperationRunQuery?.userErrors?.length > 0) {
throw new Error(bulkData.data.bulkOperationRunQuery.userErrors[0].message);
}
let isComplete = false;
let attempts = 0;
const maxAttempts = 60;
while (!isComplete && attempts < maxAttempts) {
await new Promise(resolve => setTimeout(resolve, 10000));
const statusResponse = await admin.graphql(`
{
currentBulkOperation {
id
status
errorCode
createdAt
completedAt
objectCount
fileSize
url
}
}
`);
const statusData = await statusResponse.json();
const status = statusData.data.currentBulkOperation;
if (status.status === 'COMPLETED') {
isComplete = true;
const results = await fetchBulkResults(status.url);
const batches = chunkArray(results, 500);
for (const batch of batches) {
await updateProductsBatch(admin, batch);
}
return json({
success: true,
updatedCount: results.length
});
} else if (status.status === 'FAILED') {
throw new Error(`Bulk operation failed: ${status.errorCode}`);
}
attempts++;
}
if (!isComplete) {
throw new Error('Bulk operation timed out');
}
} catch (error) {
return json(
{ success: false, error: error.message },
{ status: 500 }
);
}
};
async function fetchBulkResults(url: string): Promise<any[]> {
const response = await fetch(url);
const text = await response.text();
const products: any[] = [];
const lines = text.split('\n').filter(line => line.trim());
for (const line of lines) {
try {
const data = JSON.parse(line);
if (data.__typename === 'Product') {
products.push(data);
}
} catch (e) {
console.error('Failed to parse line:', line);
}
}
return products;
}
function chunkArray<T>(array: T[], size: number): T[][] {
const chunks: T[][] = [];
for (let i = 0; i < array.length; i += size) {
chunks.push(array.slice(i, i + size));
}
return chunks;
}
async function updateProductsBatch(
admin: any,
products: any[]
): Promise<void> {
const updatePromises = products.map((product) =>
limit(async () => {
const translatedDescription = await translateDescription(
product.descriptionHtml
);
return admin.graphql(PRODUCT_UPDATE_MUTATION, {
variables: {
input: {
id: product.id,
descriptionHtml: translatedDescription,
},
},
});
})
);
await Promise.all(updatePromises);
}
async function translateDescription(text: string): Promise<string> {
const apiKey = process.env.GOOGLE_TRANSLATE_API_KEY;
if (!apiKey) {
return `[中文翻译] ${text}`;
}
const response = await fetch(
`https://translation.googleapis.com/language/translate/v2?key=${apiKey}`,
{
method: "POST",
body: JSON.stringify({
q: text,
target: "zh-CN",
source: "en",
}),
}
);
const data = await response.json();
return data.data.translations[0].translatedText;
}代码亮点:
- • 完整的错误处理流程
- • 批量操作状态轮询机制
- • 分批处理避免超时
- • API 速率限制处理(使用 p-limit)
- • 集成真实的翻译 API
场景四:Webhook 处理
Prompt:
实现一个 Shopify App webhook 处理器,监听 orders/create 事件:
1. 验证 webhook 真实性
2. 解析订单数据
3. 调用第三方 API(如 ERP 系统)
4. 异步处理,立即响应
5. 使用 Remix 实现AI 生成:
import { type ActionFunctionArgs } from "@remix-run/node";
import crypto from "crypto";
export const action = async ({ request }: ActionFunctionArgs) => {
const rawBody = await request.text();
const hmac = request.headers.get("X-Shopify-Hmac-Sha256");
if (!hmac) {
return new Response("Missing HMAC header", { status: 401 });
}
const computedHmac = crypto
.createHmac("sha256", process.env.SHOPIFY_API_SECRET!)
.update(rawBody, "utf8")
.digest("base64");
if (hmac !== computedHmac) {
return new Response("Invalid HMAC", { status: 401 });
}
const topic = request.headers.get("X-Shopify-Topic");
const shop = request.headers.get("X-Shopify-Shop-Domain");
if (topic !== "orders/create") {
return new Response("Invalid topic", { status: 400 });
}
try {
const order = JSON.parse(rawBody);
processOrderInERP(order).catch((error) => {
console.error("ERP processing failed:", error);
sendErrorNotification(error, order.id);
});
return new Response(JSON.stringify({ success: true }), {
status: 200,
headers: { "Content-Type": "application/json" }
});
} catch (error) {
console.error("Webhook error:", error);
return new Response(JSON.stringify({ error: "Processing failed" }), {
status: 500,
headers: { "Content-Type": "application/json" }
});
}
};
async function processOrderInERP(order: any): Promise<void> {
const erpResponse = await fetch(process.env.ERP_API_URL!, {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${process.env.ERP_API_KEY}`,
},
body: JSON.stringify({
external_id: order.id,
customer_email: order.email,
total_price: order.total_price,
line_items: order.line_items.map((item: any) => ({
product_id: item.product_id,
quantity: item.quantity,
price: item.price,
})),
created_at: order.created_at,
}),
});
if (!erpResponse.ok) {
throw new Error(`ERP API error: ${erpResponse.statusText}`);
}
}
async function sendErrorNotification(error: any, orderId: string): Promise<void> {
console.error(`Order ${orderId} failed to process in ERP:`, error);
if (process.env.SLACK_WEBHOOK_URL) {
await fetch(process.env.SLACK_WEBHOOK_URL, {
method: "POST",
body: JSON.stringify({
text: `Order ${orderId} failed to process in ERP`,
error: error.message,
}),
});
}
}shopify.app.toml 配置:
[[webhooks.subscriptions]]
topic = "orders/create"
uri = "/webhooks"
[webhooks]
api_version = "2024-01"关键点说明:
- • HMAC 验证确保 webhook 安全性
- • 异步处理避免超时
- • Shopify 内置重试机制(返回非 2xx 状态码会自动重试)
- • 添加错误通知功能
场景五:UI 扩展开发
Prompt:
创建一个 Shopify Checkout UI 扩展,在结账页面显示:
1. 自定义横幅,显示促销信息
2. 条件:订单金额超过 $100 才显示
3. 使用 React 组件
4. 遵循 Shopify 设计规范AI 生成:
extensions/checkout-banner/src/index.jsx
import {
extend,
Banner,
BlockStack,
Text,
useCartCost,
} from "@shopify/ui-extensions-react/checkout";
export default extend("Checkout::RenderBlock::RenderBlock", (root, api) => {
const { cartCost } = api;
const totalAmount = parseFloat(cartCost.totalAmount.current.amount);
if (totalAmount < 100) {
return;
}
const banner = root.createComponent(
Banner,
{ status: "success" },
[
root.createComponent(
BlockStack,
{ spacing: "base" },
[
root.createComponent(
Text,
{ variant: "headingMd", as: "h3" },
"🎉 免运费优惠"
),
root.createComponent(
Text,
{ variant: "bodyMd" },
`订单金额 $${totalAmount.toFixed(2)} 已超过 $100,自动免运费!`
),
totalAmount < 150 ? root.createComponent(
Text,
{ variant: "bodySm", emphasis: "subtle" },
`再购买 $${(150 - totalAmount).toFixed(2)} 即可获得额外礼品`
) : null,
]
),
]
);
root.appendChild(banner);
});extensions/checkout-banner/shopify.ui.extension.toml
name = "促销横幅"
type = "checkout_ui_extension"
[[extensions.targeting]]
target = "purchase.checkout.block.render"
module = "./index.jsx"
[extensions.capabilities]
api_access = true
block_progress = true或者使用 React 组件风格
import {
extend,
Banner,
BlockStack,
Text,
useCartCost,
useTranslate,
} from "@shopify/ui-extensions-react/checkout";
export default extend("Checkout::RenderBlock::RenderBlock", () => {
const cartCost = useCartCost();
const translate = useTranslate();
const totalAmount = parseFloat(cartCost.totalAmount.current.amount);
if (totalAmount < 100) {
return null;
}
return (
<Banner status="success">
<BlockStack spacing="base">
<Text variant="headingMd" as="h3">
🎉 免运费优惠
</Text>
<Text variant="bodyMd" as="p">
订单金额 ${totalAmount.toFixed(2)} 已超过 $100,自动免运费!
</Text>
{totalAmount < 150 && (
<Text variant="bodySm" emphasis="subtle">
再购买 ${(150 - totalAmount).toFixed(2)} 即可获得额外礼品
</Text>
)}
</BlockStack>
</Banner>
);
});关键点说明:
- • 使用
@shopify/ui-extensions-react/checkout - • 条件渲染避免不必要的组件
- • 支持国际化(i18n)
- • 两种实现方式:传统 API 和 React 组件
第三部分:高级技巧
1. 多步骤开发工作流
不要一次性生成所有代码,分步骤进行更高效:
第一步:先创建数据库模型
创建一个 Prisma schema,包含产品、订单和客户的关联关系
第二步:基于模型生成 API 路由
使用 Remix 和刚才的 Prisma schema,创建 CRUD API
第三步:添加前端界面
使用 Polaris 组件创建产品管理界面
第四步:添加错误处理和测试
为所有 API 添加错误处理和单元测试2. 迭代优化
生成初始代码后,继续优化:
优化上面的产品管理界面:
1. 添加搜索和筛选功能
2. 实现分页加载
3. 添加加载状态和错误提示
4. 优化移动端显示3. 代码审查模式
让 AI 帮你审查代码:
请审查以下代码,检查:
1. 安全漏洞
2. 性能问题
3. Shopify 最佳实践
4. TypeScript 类型安全
[粘贴你的代码]第四部分:常见问题处理
Q1:GraphQL 查询太复杂,如何拆分?
Prompt:
我有一个复杂的查询需要获取产品详情、库存和订单数据。帮我拆分成多个小查询,每个查询负责一个领域,避免 N+1 问题AI 会生成:
- • 独立的 products 查询
- • 独立的 inventoryLevels 查询
- • 独立的 orders 查询
- • 包含 DataLoader 模式的批处理代码
Q2:如何处理 API 速率限制?
Prompt:
实现一个 Shopify API 请求队列,具备以下功能:
1. 自动检测速率限制(429 响应)
2. 指数退避重试
3. 请求优先级
4. 并发控制(使用 p-limit)Q3:如何调试 GraphQL 错误?
Prompt:
创建一个 GraphQL 错误处理中间件:
1. 解析 userErrors 字段
2. 格式化错误信息
3. 提供修复建议
4. 记录错误日志第五部分:实战项目示例
项目:库存同步工具
需求描述:
创建一个 Shopify App,实现以下功能:
1. 每 5 分钟同步一次库存到 ERP 系统
2. 使用 Shopify InventoryLevelUpdated webhook 实时更新
3. 批量处理 API 调用
4. 包含完整的错误处理和监控
5. 使用 Admin API 和 WebhooksAI 会生成完整的项目结构:
shopify-app/
├── shopify.app.toml
├── app/
│ ├── routes/
│ │ ├── app.sync.tsx # 同步管理界面
│ │ ├── app.webhooks.tsx # webhook 配置
│ │ └── webhooks.ts # webhook 处理
│ ├── services/
│ │ ├── sync.service.ts # 同步逻辑
│ │ ├── queue.service.ts # 任务队列
│ │ └── monitor.service.ts # 监控服务
│ └── utils/
│ ├── api-client.ts # API 客户端
│ └── error-handler.ts # 错误处理
└── prisma/
└── schema.prisma # 数据库模型每个文件都有完整的实现,包括:
- • 定时任务
- • Webhook 处理
- • 批量操作
- • 错误重试
- • 监控仪表盘
结语
Shopify AI Toolkit 的真正价值,不在于"自动生成代码",而在于:
- 1. 减少上下文切换:无需频繁查阅文档
- 2. 提高代码质量:自动遵循最佳实践
- 3. 加速学习曲线:新手上手更快
- 4. 聚焦核心逻辑:让 AI 处理样板代码
关键建议:
- • 从小任务开始,逐步扩大使用范围
- • 始终审查 AI 生成的代码
- • 保持对 Shopify 平台的理解
- • 建立自己的 Prompt 模板库
- • 注意 API 版本兼容性
- • 处理 API 速率限制
- • 使用异步处理避免超时
Shopify AI Toolkit 是工具,你是使用者。善用工具,但保持思考。
参考资料:
- • Shopify AI Toolkit 官方文档
- • Shopify GraphQL API 参考
- • Shopify App 开发最佳实践
- • Shopify CLI 文档
- • Polaris 组件库
本文首发于 乐豆说