用 Gadget 实战开发 Shopify App:从商品同步到自动打标签

文章目录

如果你第一次开发 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 设置为必填字段。

这样,商家不能保存一条没有关键词的记录。

模型可以理解为:

字段作用
idGadget 自动生成的记录 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

后台任务执行时:

  1. 检查参数是否完整
  2. 根据 shopId 获取当前商店连接
  3. 使用 Gadget 提供的 Shopify 认证客户端
  4. 将新标签写回 Shopify
  5. 记录成功或失败日志

后台任务适合处理:

  • 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/create Webhook 是否注册
  • 当前查看的是 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 从连接商店到完成生产部署的完整实践路径。