文章目录
如果你第一次开发 Shopify App,最容易卡住的地方通常不是业务逻辑,而是 OAuth、API 权限、商品同步、Webhook、后台任务和生产部署。
Gadget 可以把这些通用基础设施准备好,让开发者把时间放在真正有价值的功能上。
本文会开发一个完整的小项目:
Product Tagger:商家输入关键词,应用自动检查 Shopify 商品描述,并将匹配到的关键词写入商品标签。
最终效果如下:
商家输入关键词:cozy、warm、sweater
↓
关键词保存到 Gadget 数据库
↓
Shopify 商品同步到 Gadget
↓
读取商品描述
↓
匹配关键词
↓
将匹配结果写回 Shopify 商品标签
项目会用到什么技术?
项目的整体结构如下:
Shopify
├── 商品和商品描述
├── 商品标签
└── 商品 Webhook
↓
Gadget
├── Shopify Connection
├── PostgreSQL 数据库
├── Node.js 后端
├── Remix + React 前端
├── 历史数据同步
└── 后台任务队列
项目会使用:
- Shopify Development Store
- Shopify Dev Dashboard
- Gadget
- Shopify Product API
- Shopify Product Webhook
- PostgreSQL
- Remix
- React
- TypeScript
- Serverless Node.js
- 后台 Action
- Access Control
如果应用只服务一个指定商家,可以按照 Custom App 路线开发。如果未来要服务多个独立商家并提交 Shopify App Store,则应该按照 Public App 的要求规划应用分发、隐私、收费和审核。
开始前的准备
开始前准备:
- Shopify Partners 或 Shopify 开发者账号
- 一个 Shopify Development Store
- 一个 Gadget 账号
- Chrome 浏览器
- 一个用于测试的开发商店
开发商店专门用于应用测试,不会影响真实商店。创建开发商店时,建议使用带测试数据的商店,这样可以快速获得商品、客户、订单和库存数据。
Shopify 当前将开发商店、应用创建和应用管理集中在 Dev Dashboard 中。完整应用可以使用 Shopify CLI 创建;如果主要是数据同步和后台集成,也可以通过 Dev Dashboard 配置应用。citation:Building apps
创建 Gadget 应用
进入 Gadget 的新建应用页面,通常可以从以下地址开始:
https://gadget.new
选择:
Shopify App
如果应用只针对一个指定商家使用,可以选择 Custom App 方向。
应用名称可以设置为:
Product Tagger
前端技术选择:
Remix
TypeScript
创建完成后,Gadget 会准备应用基础设施,包括:
- PostgreSQL 数据库
- Node.js 后端
- React 前端
- Shopify 连接
- API 客户端
- Development 环境
- Production 环境
- 后台 Action
- 部署能力
完成创建后,你会进入 Gadget 编辑器。
连接 Shopify
在 Gadget 中找到 Shopify Connection。不同版本的 Gadget 可能显示为:
Shopify
Shopify Connection
Connections
Connect to Shopify
如果 Gadget 提供 AI 引导,可以描述应用需求:
Build a Shopify product tagger app that reads product descriptions,
stores merchant keywords, and writes matching keywords back as product tags.
如果使用手动配置,选择 Shopify 应用连接,然后选择:
Shopify Product
本项目需要读取商品描述,并将标签写回 Shopify,因此通常需要以下权限:
read_products
write_products
read_products 用于读取:
- 商品标题
- 商品描述
- 商品 ID
- 商品现有标签
write_products 用于将匹配到的关键词写回商品标签。
只申请当前功能真正需要的权限,不要一次申请所有权限。
配置开发和生产环境
开发环境和生产环境应该分开。
可以准备两个应用:
Product Tagger - Development
Product Tagger - Production
Development 环境用于:
- 测试 OAuth
- 测试 Webhook
- 测试商品同步
- 调试前端和后端
Production 环境用于:
- 正式商店安装
- 正式数据
- 正式应用访问
- 生产部署
Shopify 应用配置通常会涉及:
- Client ID
- Client Secret
- App URL
- OAuth 回调地址
- API Scopes
- Embedded App 设置
- Webhook API 版本
Client Secret 只能保存在服务端配置中,不能放进 React 前端或公开代码仓库。
选择 Shopify Product 数据模型
在 Gadget 的 Shopify Connection 中选择 Shopify Product 后,通常可以同步以下数据模型:
- Shopify Product
- Shopify Product Variant
- Shopify Product Image
- Shopify Shop
- Shopify Sync
- GDPR Request
本项目至少需要:
Shopify Product
Shop
Shopify Sync
选择 Product 模型后,Gadget 通常会帮助配置商品相关 Webhook:
products/create
products/update
products/delete
这些 Webhook 用来保持 Gadget 数据库和 Shopify 商品数据同步。
同时,Gadget 会为商品数据生成对应模型、字段和基础 Action。
理解 Gadget 的数据模型
Gadget 中的 Model,可以理解成数据库中的表。
例如:
Shopify Product
相当于保存 Shopify 商品的数据库表。
其中的字段就像数据库中的列,例如:
id
title
body
handle
status
tags
createdAt
updatedAt
本项目主要依赖商品的:
id
body
tags
shopId
实际字段名称以 Gadget 当前生成的 Shopify Product 模型为准。
Gadget 自动生成的数据模型并不是固定模板,开发者仍然可以:
- 增加字段
- 删除不需要的字段
- 添加自定义模型
- 设置字段类型
- 设置必填规则
- 创建模型关系
- 添加数据验证
创建 Allowed Tag 模型
现在创建一个自定义模型:
Allowed Tag
这个模型用来保存商家输入的关键词。
添加字段:
keyword
字段类型:
String
将 keyword 设置为必填字段。
这样,商家不能保存一条没有关键词的记录。
模型可以理解为:
| 字段 | 作用 |
|---|---|
| id | Gadget 自动生成的记录 ID |
| keyword | 商家输入的关键词 |
| createdAt | 创建时间 |
| updatedAt | 更新时间 |
创建模型后,Gadget 通常会自动生成对应的 CRUD Action:
Create
Read
Update
Delete
同时也会生成应用 API 客户端和接口文档。
使用 API Playground 测试关键词
在 Gadget 的 API Playground 中创建一条 Allowed Tag 记录:
keyword: cozy
创建成功后进入 Data 页面查看。
接着再创建几条测试关键词:
warm
sweater
cotton
如果这些记录可以正常创建,说明以下部分基本正常:
- Allowed Tag 模型
- 数据库
- Create Action
- API 客户端
- 基础权限
配置前端访问权限
Gadget 自动生成 API,并不代表前端用户自动拥有访问权限。
如果 React 页面出现:
Permission denied
需要检查 Access Control。
为 Shopify App 用户角色开放 Allowed Tag 的权限:
Read
Create
Delete
如果需要修改关键词,再开放:
Update
权限关系可以理解为:
模型存在
≠
前端用户可以访问模型
应用必须同时满足:
- 模型已经创建
- API 已经生成
- 用户角色拥有权限
- 前端调用了正确的 Action
测试商品 Webhook
在 Shopify Development Store 中创建一个测试商品:
Title: Cozy Sweater
Description: A warm and cozy sweater for winter.
保存后,回到 Gadget 的 Shopify Product 数据页面刷新。
如果商品记录出现,说明商品创建 Webhook 正常:
Shopify 创建商品
↓
products/create
↓
Gadget 接收 Webhook
↓
Shopify Product 记录写入 Gadget
接着修改商品描述,例如增加:
Soft cotton material.
保存后再次查看 Gadget。
如果商品记录更新,说明:
products/update
正在工作。
删除商品时,Gadget 中的处理方式可能取决于当前连接和模型配置:
- 删除对应记录
- 标记为已删除
- 保留同步记录
- 更新商品状态
生产应用中应该明确选择一种删除策略。
编写商品标签匹配逻辑
应用的核心逻辑如下:
商品创建或更新
↓
读取商品描述
↓
读取 Allowed Tag 中的关键词
↓
将商品描述拆分为可比较的词
↓
找出匹配关键词
↓
过滤商品已有标签
↓
得到新的标签
↓
将新标签写回 Shopify
可以用下面的伪代码理解:
if 商品描述没有变化:
不执行标签处理
keywords = 读取所有 Allowed Tag 关键词
descriptionWords = 拆分商品描述
existingTags = 读取商品已有标签
matchedTags = 找出同时出现在 keywords 和 descriptionWords 中的词
newTags = 过滤掉 existingTags 中已经存在的标签
if newTags 不为空:
将 shopId、productId 和 newTags 放入后台任务
这里有一个非常重要的设计点:
只有商品描述发生变化时,才重新执行匹配。
否则可能形成循环:
商品更新
↓
Webhook 触发
↓
应用写回商品标签
↓
商品再次更新
↓
Webhook 再次触发
Gadget 提供记录变化检测能力,可以根据商品描述是否变化,判断是否需要重新执行标签逻辑。
使用后台 Action 写回 Shopify
不要在 Webhook 请求中执行大量耗时操作。
更稳妥的流程是:
Webhook Action
↓
判断是否需要处理
↓
创建后台任务
↓
后台任务调用 Shopify API
↓
更新商品标签
后台任务可以接收:
shopId
productId
tags
后台任务执行时:
- 检查参数是否完整
- 根据
shopId获取当前商店连接 - 使用 Gadget 提供的 Shopify 认证客户端
- 将新标签写回 Shopify
- 记录成功或失败日志
后台任务适合处理:
- Shopify API 速率限制
- 网络重试
- 大量商品处理
- 外部 API 调用
- 长时间运行的业务逻辑
还要设置合理的并发数,避免短时间内向 Shopify 发出过多请求。
Gadget 的后台任务通常可以提供自动重试和失败记录。生产环境中仍然需要查看任务队列、日志和最终失败任务。
正确使用 Action 的执行阶段
Gadget Action 中常见的执行阶段包括:
run
onSuccess
它们适合的场景不同。
run
适合事务性逻辑,例如:
- 创建 Gadget 数据库记录
- 更新 Gadget 数据库字段
- 删除 Gadget 数据库记录
如果事务中发生错误,相关数据库变化可以回滚。
onSuccess
适合外部副作用,例如:
- 调用 Shopify API
- 调用 ERP
- 发送通知
- 调用第三方服务
- 创建后台任务
外部系统的写入通常无法和本地数据库一起回滚,因此调用 Shopify 的逻辑更适合放在成功后的处理阶段或后台任务中。
处理关键词数量和分页
入门项目可以先读取有限数量的关键词,但生产应用不能默认数据量很小。
需要考虑:
- Allowed Tag 记录超过一页
- 商品数量超过一页
- 商品变体数量较多
- 商店数据规模较大
- 同步过程中发生网络错误
- API 请求受到速率限制
生产环境需要:
- 对 Allowed Tag 分页
- 对 Shopify 商品同步分页
- 记录同步进度
- 处理 API 速率限制
- 失败后支持重试
- 避免一次性加载全部数据
如果使用 Gadget 的历史同步功能,应检查:
- 同步是否完成
- 是否存在失败记录
- 是否支持重新执行
- 是否记录同步时间
- 数据数量是否符合预期
制作 React 管理页面
现在为商家制作一个简单的管理界面。
页面可以包含:
Product Tagger
并提供:
- 添加关键词
- 显示关键词列表
- 删除关键词
- 显示最近同步时间
- 显示商品数量
- 显示最近处理日志
可以使用 Gadget 提供的:
- AutoForm
- AutoTable
- React Hooks
- Action API
- Model API
页面逻辑如下:
提交关键词表单
↓
调用 Allowed Tag Create Action
↓
保存关键词
↓
刷新关键词列表
删除逻辑如下:
点击删除
↓
调用 Allowed Tag Delete Action
↓
刷新关键词列表
前端不要直接携带 Shopify Access Token。
前端只调用 Gadget API,Shopify 的访问凭证和商品写入逻辑放在后端处理。
执行历史数据同步
Webhook 只能处理安装应用之后发生的数据变化。
如果开发商店里已经有商品,需要执行一次历史同步。
在 Gadget 中找到当前商店的安装记录或同步功能,启动:
Historical Sync
Data Sync
Shopify Sync
同步完成后检查:
- Shopify Product 是否有记录
- 商品描述是否完整
- 商品标签是否同步
- 商品数量是否合理
- 商品变体是否关联正确
- 关键词匹配是否执行
- 商品标签是否成功写回 Shopify
如果应用需要在商家安装后自动同步,可以在安装成功 Action 中调用同步能力。
推荐流程:
商家安装应用
↓
创建 Shop 记录
↓
启动首次历史同步
↓
保存同步状态
↓
同步结束
↓
开始接收后续 Webhook
完整测试案例
测试关键词创建
在应用中添加:
cozy
确认 Gadget 的 Allowed Tag 中出现:
cozy
测试商品创建
创建商品:
Title: Cozy Sweater
Description: A warm and cozy sweater for winter.
预期结果:
Shopify 商品进入 Gadget
商品描述包含 cozy
Shopify 商品标签出现 cozy
测试商品描述更新
将描述改为:
A soft cotton sweater for winter.
预期结果:
- 触发商品更新 Webhook
- Gadget 商品记录更新
- 重新执行关键词匹配
- 如果
cotton是关键词,则写入商品标签
测试关键词删除
删除:
cozy
确认:
- Gadget 中的 Allowed Tag 记录被删除
- 后续商品处理不再使用
cozy - 已经写入 Shopify 的旧标签是否保留,符合应用的业务规则
测试重复 Webhook
让同一个商品更新事件重复到达,确认:
- 不会重复创建商品记录
- 不会重复添加相同标签
- 不会形成 Webhook 循环
- 日志中可以看到处理结果
部署到 Production
Development 环境测试完成后,再部署 Production。
部署前确认:
OAuth 测试通过
API 权限正确
Webhook 正常
历史同步正常
Access Control 正常
后台任务正常
前端页面正常
生产环境应使用独立的:
- Shopify Production App
- Client ID
- Client Secret
- App URL
- OAuth 回调地址
- 数据库环境
- 环境变量
在 Gadget 中执行类似:
Deploy to Production
部署过程通常包括:
- 执行数据库迁移
- 构建前端
- 优化静态资源
- 更新后端 Action
- 更新数据模型
- 重启应用服务
- 发布生产版本
生产环境通常是只读的。需要修改代码和模型时,应回到 Development 环境完成修改,再重新部署。
安装生产版本
生产应用部署完成后,在 Shopify Dev Dashboard 中生成安装方式。
对于 Custom App:
- 选择目标商店或组织
- 生成安装链接
- 使用目标商店管理员账号打开
- 检查应用权限
- 完成安装
- 查看 Gadget Production 中的 Shop 记录
自定义安装链接可能会过期。Shopify 官方说明中,Custom App 安装链接通常具有有效期,过期后需要重新生成。citation:Installing and setting up apps
生产安装完成后,需要重新执行:
生产环境首次历史同步
然后测试:
生产商品创建
生产商品修改
生产 Webhook
生产标签写回
生产日志
Custom App 适合特定商家使用。如果应用要面向多个独立商家,应按照 Public App 和 Shopify App Store 的要求规划应用分发和审核。
生产环境安全检查
上线前检查:
- Client Secret 没有出现在前端
- Access Token 没有写入浏览器
- Gadget 日志没有打印完整 Token
- 生产和开发凭据没有混用
- 生产数据库与开发数据库分离
- Webhook 只处理验证通过的请求
- Webhook 具备幂等性
- 商品更新不会产生无限循环
- 任务失败后可以查看和重试
- API 权限符合最小权限原则
Shopify Webhook 使用 HTTPS 地址,并需要设置正确的 Webhook API 版本。生产环境不能依赖 localhost 作为 Webhook 接收地址。citation:Creating webhooks
常见问题排查
应用安装后无限跳转
检查:
- App URL 是否正确
- OAuth 回调地址是否完整
- Development 和 Production 凭据是否混用
- Embedded App 设置是否正确
- 当前商店是否已经完成安装
- 浏览器 Cookie 是否异常
商品没有进入 Gadget
检查:
- Shopify Product 模型是否启用
read_products是否授权products/createWebhook 是否注册- 当前查看的是 Development 还是 Production
- Shopify Shop 记录是否存在
- Gadget 日志是否出现错误
商品标签没有写回
检查:
- 是否申请
write_products - 商品描述是否真的包含关键词
- Allowed Tag 是否有关键词
- 关键词是否已经存在于商品标签
- 后台任务是否执行
- 队列中是否有失败任务
- Shopify API 请求是否返回权限错误
前端显示 Permission denied
检查:
- Shopify App 用户角色是否有 Allowed Tag 的 Read 权限
- 是否有 Create、Delete 权限
- 前端调用的模型名称是否正确
- 当前用户是否属于正确的应用角色
商品一直重复更新
通常是因为:
收到商品更新
→ 应用无条件写回标签
→ Shopify 再次触发更新 Webhook
→ 应用再次写回标签
解决方法:
- 只在商品描述变化时执行
- 写入前排除已有标签
- 对商品 ID 做幂等处理
- 不要无条件写回 Shopify
项目验收标准
当以下条件全部满足时,项目才算真正完成:
- 应用可以安装到开发商店
- Shopify OAuth 可以正常完成
- Gadget 中可以看到 Shop 记录
- 商品历史同步可以完成
- 商品创建 Webhook 正常
- 商品更新 Webhook 正常
- 商品删除行为符合设计
- Allowed Tag 可以创建
- Allowed Tag 可以删除
- Shopify App 用户拥有正确权限
- 商品描述能匹配关键词
- 商品标签能写回 Shopify
- 后台任务可以重试
- 重复 Webhook 不会生成重复数据
- 商品更新不会造成循环
- Development 可以部署到 Production
- 生产应用可以重新安装和同步
结语
这个商品自动打标签应用虽然功能不复杂,但它完整展示了 Shopify App 的核心开发链路:
创建应用
↓
连接 Shopify
↓
配置 API 权限
↓
选择数据模型
↓
订阅 Webhook
↓
执行历史同步
↓
编写业务逻辑
↓
开发 React 页面
↓
设置访问权限
↓
使用后台任务
↓
部署到 Production
Gadget 负责处理大量通用基础设施,开发者主要负责:
- 应用解决什么问题
- 数据模型如何设计
- 商品如何匹配关键词
- 什么情况下写回 Shopify
- 如何避免重复处理
- 商家在前端看到什么
当 Product Tagger 项目跑通后,还可以继续扩展:
- 根据库存自动打标签
- 根据供应商自动分类
- 自动生成商品描述
- 将商品同步到 ERP
- 添加批量处理功能
- 增加订单或客户数据
- 使用 AI 生成商品标签
- 添加定时后台任务
- 接入多个 Shopify 商店
这就是一个 Shopify App 从连接商店到完成生产部署的完整实践路径。




