用 Gadget 实现 Shopify App Proxy:从店铺前台安全请求后端

文章目录

Shopify App Proxy 可以让店铺前台通过一个 Shopify 路径,请求应用后端的数据或功能。

这类功能适合:

  • 在主题中显示动态内容
  • 加载商品推荐
  • 展示会员信息
  • 创建前台表单
  • 获取应用配置
  • 根据商店数据渲染前台区块
  • 将店铺前台和 Gadget 后端连接起来

本文会使用 Gadget 创建一个简单的 App Proxy 示例:

Shopify 店铺前台
        ↓
Shopify App Proxy
        ↓
Gadget 后端 Action
        ↓
返回动态内容
        ↓
主题 App Block 显示结果

为了突出 App Proxy 的工作方式,示例后端只返回一段固定 Banner 文案。

实际项目中,Banner 更适合使用主题设置或 Metafield 管理,不需要每次都请求后端。本文的重点是理解 App Proxy 的配置、权限、请求路径和安全边界。

App Proxy 解决什么问题?

普通主题 JavaScript 如果直接请求 Gadget 后端,需要处理:

  • 请求地址
  • 商店身份
  • 请求签名
  • 跨域问题
  • 多商店数据隔离
  • 前台访问权限

使用 App Proxy 后,店铺前台可以请求 Shopify 提供的店铺路径,Shopify 再将请求转发到应用后端。

访问路径通常类似:

https://your-store.myshopify.com/apps/gadget-banner-tutorial

数据流如下:

顾客浏览店铺
        ↓
主题 JavaScript 请求 /apps/gadget-banner-tutorial
        ↓
Shopify 转发到 Gadget
        ↓
Gadget 验证请求上下文
        ↓
调用后端 Action
        ↓
返回 JSON 或文本
        ↓
主题页面显示结果

Shopify 官方也将 App Proxy 用于动态商品展示、愿望清单和自定义表单等店铺前台功能。商家可以在应用设置中调整默认的 App Proxy URL;如果 URL 发生变化,使用旧路径的菜单和链接也需要同步更新。citation:Change the app’s proxy URL

开始前的准备

需要准备:

  • Shopify Partners 或 Shopify 开发者账号
  • Shopify Development Store
  • Gadget 账号
  • Shopify CLI
  • Node.js
  • VS Code 或其他代码编辑器
  • 一个用于测试的 Online Store 2.0 主题

本项目使用:

Backend: Gadget Serverless Node.js
Database: Gadget PostgreSQL
Frontend: Shopify Theme App Extension
Proxy: Shopify App Proxy

如果主题使用 Online Store 2.0,可以通过 Theme Editor 添加 App Block。较旧的主题可能需要手动编辑 Liquid 文件。

创建 Gadget Shopify App

进入 Gadget,创建一个新的 Shopify App。

应用名称可以设置为:

App Proxy Example

如果只是用于一个指定商店的开发和测试,可以选择 Custom App 方向。

Gadget 会准备:

  • PostgreSQL 数据库
  • Node.js 后端
  • React 前端
  • Shopify Connection
  • Shopify API 客户端
  • Development 环境
  • Production 环境

本文不会开发复杂的 Embedded App 页面,重点放在:

Gadget 后端
+
Shopify App Proxy
+
主题前台请求

创建或连接 Shopify App

在 Gadget 中选择:

Connect to Shopify

如果 Shopify App 还不存在,可以在 Shopify Dev Dashboard 中创建。

应用名称可以保持一致:

App Proxy Example

创建完成后获取:

Client ID
Client Secret

将它们填入 Gadget 的 Shopify Connection。

Client Secret 只能保存在:

  • Gadget 私密配置
  • 服务端环境变量
  • 密钥管理服务

不能放在:

  • 主题 JavaScript
  • Liquid 文件
  • 浏览器 Local Storage
  • 公共 Git 仓库
  • 店铺前台请求参数

配置 Shopify API 权限

本示例只返回固定文字,因此不需要读取商品或订单数据。

如果应用需要读取主题、商品或其他资源,再根据实际功能申请对应权限。

例如:

read_products
read_themes

但不要因为要使用 App Proxy,就默认申请所有权限。

App Proxy 负责转发前台请求,是否需要访问 Shopify API,取决于后端实际业务。

安装到 Development Store

在 Shopify App 的安装页面选择 Development Store。

安装流程如下:

选择 Development Store
        ↓
查看应用权限
        ↓
点击安装
        ↓
完成 OAuth
        ↓
进入 Shopify Admin
        ↓
回到 Gadget 查看连接状态

安装成功后,Gadget 中应该出现当前商店的 Shop 记录。

同时确认:

  • 应用能在 Shopify Admin 中打开
  • Gadget Shopify Connection 显示已连接
  • App URL 和 OAuth 回调地址正确
  • 当前使用的是 Development 环境

创建用于前台请求的 Global Action

在 Gadget 中创建一个全局 Action:

getBanner

这个 Action 的作用是返回一段内容:

This is my banner.

实际项目中,Action 可以返回:

  • 商品推荐
  • 会员状态
  • 店铺配置
  • 动态活动内容
  • 表单选项
  • 个性化文案

本示例还会记录以下信息:

  • 当前 Shop ID
  • App Proxy 请求信息
  • 当前访问角色
  • 请求处理日志

这样测试时可以确认:

请求来自哪家商店
请求是否经过 App Proxy
请求当前属于哪个用户角色

配置前台访问权限

店铺前台的顾客通常没有登录 Shopify Admin,因此通过 App Proxy 进入 Gadget 的请求,默认可能属于:

Unauthenticated

在 Gadget Access Control 中,需要允许未认证访问者调用:

getBanner

权限结构可以理解为:

Shopify App Users
        ↓
商家在 Shopify Admin 中使用应用

Unauthenticated
        ↓
店铺前台普通顾客访问

Shopify Customer
        ↓
启用了顾客登录并完成认证的顾客

如果只是普通店铺前台 Banner,通常需要给 Unauthenticated 角色读取权限。

如果应用启用了顾客认证,例如:

  • Customer Account Extension
  • 登录会员中心
  • 顾客专属推荐
  • 会员积分

那么登录顾客可能会使用 Shopify Customer 角色。此时必须根据顾客身份进行数据隔离,不能只依赖前端传来的参数。

配置 Shopify App Proxy

在 Shopify App 配置文件中添加 App Proxy 设置。

配置通常包括:

Proxy URL
Prefix
Subpath

例如:

Prefix: apps
Subpath: gadget-banner-tutorial

最终店铺前台访问路径可能是:

/apps/gadget-banner-tutorial

Gadget 后端实际接收的 URL 由应用配置决定。

需要确保以下三处完全一致:

Shopify App Proxy 配置
        ↓
主题 JavaScript 请求路径
        ↓
Gadget API Client 的代理端点

如果配置的是:

/apps/gadget-banner-tutorial

前端就不能请求:

/apps/banner

也不能请求另一个环境的路径。

Development 和 Production 要分开

开发环境可以使用:

gadget-banner-tutorial-dev

生产环境使用:

gadget-banner-tutorial

不要让 Development 和 Production 共用容易混淆的代理配置。

发布 App Proxy 配置

修改 App Proxy 配置后,需要将应用配置部署到 Shopify App。

当前 Shopify CLI 项目通常可以使用:

shopify app deploy

如果 Gadget 项目通过包管理脚本封装了 Shopify CLI,也可以使用项目提供的部署命令。

发布前检查:

  • 修改的是正确 Shopify App
  • 使用的是正确配置文件
  • App Proxy prefix 正确
  • App Proxy subpath 正确
  • Development 和 Production 没有混用
  • 发布的是当前应用版本

发布完成后,可以在 Shopify Dev Dashboard 中查看新的应用版本和 App Proxy 配置。

如果项目同时配置了合规 Webhook,发布应用配置时也可能一起更新相关 Webhook 订阅。

使用 Shopify CLI 生成 Theme App Extension

现在需要让店铺主题调用 App Proxy。

使用 Shopify CLI 生成 Theme App Extension:

shopify app generate extension

选择:

Theme App Extension

创建完成后,项目中通常会出现:

extensions/

Theme App Extension 可能包含:

blocks/banner.liquid
assets/banner.js
shopify.extension.toml

其中:

  • banner.liquid 负责输出主题区块
  • banner.js 负责请求 Gadget 后端
  • shopify.extension.toml 负责扩展配置

如果使用 Gadget CLI 管理本地代码,可以先将 Gadget 项目同步到本地,再在同一个项目中生成 Extension。

这样可以将:

Gadget 后端
Gadget 前端
Theme App Extension
Shopify App 配置

放在同一个代码仓库中管理。

让 Theme Extension 加载 Gadget API Client

Theme Extension 的 JavaScript 需要请求 Gadget 后端。

在主题区块中加载 Gadget 项目对应的公开 API Client。

这个 Client 可以放在浏览器中使用,但必须确保:

  • 不包含 Client Secret
  • 不包含 Access Token
  • 只调用允许公开访问的 Action
  • 返回的数据经过最小化处理
  • 后端仍然负责权限判断

在 JavaScript 中,需要初始化 Client,并指定 App Proxy endpoint。

配置的 endpoint 必须与 Shopify App Proxy 中的 prefix 和 subpath 完全一致。

例如:

Shopify App Proxy:
apps/gadget-banner-tutorial

Theme Extension:
apps/gadget-banner-tutorial

如果两边不一致,前端请求通常会返回:

404

或:

Proxy endpoint not found

在 Theme Block 中调用 Gadget Action

Theme Extension 的基本逻辑如下:

页面加载
        ↓
初始化 Gadget API Client
        ↓
配置 App Proxy endpoint
        ↓
调用 getBanner Action
        ↓
获取返回数据
        ↓
插入主题区块

本示例最终显示:

This is my banner.

虽然内容是固定字符串,但真实项目可以改成:

读取商店配置
        ↓
从数据库读取活动内容
        ↓
返回 Banner 标题和链接
        ↓
主题前台动态渲染

如果只是显示固定 Banner,更适合使用:

  • 主题设置
  • Metafield
  • App Block 设置项

不要为了输出一段固定文字而增加后端请求。

启动 Theme Extension 开发预览

使用 Shopify CLI 启动开发服务。

项目中可能使用:

shopify app dev

或者:

yarn shopify dev

启动时:

  1. 选择 Shopify Partners 组织
  2. 选择正确的 Shopify App
  3. 选择 Development Store
  4. 等待 Theme Extension 上传
  5. 打开主题编辑器
  6. 添加 Banner App Block
  7. 保存并预览主题

如果 Development Store 启用了店铺密码,需要在 Shopify Admin 中进入:

Online Store → Preferences

查看店铺密码,然后输入预览页面。

在主题编辑器中添加 App Block

进入:

Online Store → Themes → Customize

然后:

  1. 选择需要显示 Banner 的页面
  2. 点击 Add section 或 Add block
  3. 在 Apps 中选择你的 Theme App Extension
  4. 添加 Banner Block
  5. 点击 Save

Shopify 官方支持在兼容主题中添加、移动、预览和保存 App Block。citation:Extend your theme with apps

如果看不到 App Block,检查:

  • Theme App Extension 是否已经生成
  • Extension 是否正在运行
  • 当前主题是否支持 App Block
  • 当前应用是否安装到正确商店
  • 选择的 Shopify App 是否正确
  • 当前 Development Store 是否正确

检查 Gadget 日志

回到 Gadget,打开 Logs。

可以查看:

  • 当前 Shop ID
  • App Proxy 请求信息
  • 当前访问角色
  • Action 是否执行成功
  • 返回数据是否正确
  • 请求是否被权限拒绝

普通店铺顾客访问时,通常会看到:

Unauthenticated

如果启用了顾客认证,并且顾客已经登录,则可能会看到:

Shopify Customer

如果是商家从 Shopify Admin 使用应用,则通常会使用:

Shopify App User

这些角色不同,后端应该使用不同的数据访问规则。

App Proxy 的安全边界

App Proxy 让店铺前台可以请求应用后端,但它不等于“所有前台请求都安全”。

生产应用需要处理:

不要把秘密放到前台

不能暴露:

Client Secret
Access Token
数据库密码
第三方 API 私钥

验证请求上下文

后端应该确认:

  • 请求来自哪个商店
  • 请求属于哪个用户角色
  • 访问的资源是否属于当前商店
  • 当前 Action 是否允许公开调用

限制公开 Action

未认证访问者只应该能访问明确需要公开的 Action。

例如:

允许读取公开 Banner
允许提交有限的前台表单
不允许读取订单
不允许读取客户后台数据
不允许修改商品
不允许读取其他商店数据

处理输入校验

前台传入的数据不能直接信任,需要检查:

  • 字段格式
  • 长度
  • 类型
  • 是否为空
  • 是否属于当前商店
  • 是否重复提交

设置频率限制

公开前台接口可能被机器人调用。

生产环境需要考虑:

  • 请求频率限制
  • 重复请求处理
  • 垃圾请求过滤
  • 表单验证码
  • 邮箱格式校验
  • 日志监控

App Proxy 与顾客认证

普通店铺访客通常是未认证用户。

如果启用了 Shopify Customer Account 相关认证,登录顾客可能会映射到 Gadget 的 Shopify Customer 角色。

这时可以构建:

  • 会员专属内容
  • 登录用户的推荐
  • 顾客积分
  • 顾客订单状态
  • 个性化店铺组件

但必须确保:

当前顾客
        ↓
只能读取自己的数据

不能因为顾客已经登录,就允许其读取同一商店的全部顾客数据。

App Proxy 与主题开发的关系

App Proxy 只负责:

前台请求 → 应用后端

Theme App Extension 负责:

主题区块 → 发起请求 → 显示结果

两者分工如下:

部分作用
shopify.app.toml配置 App Proxy
Gadget Action处理后端业务
Access Control控制谁可以调用
Theme App Extension在前台显示功能
banner.liquid输出主题区块
banner.js调用 Gadget API
Shopify CLI运行、预览和部署扩展

部署到 Production

Development 测试完成后,再配置 Production。

生产环境需要单独确认:

  • Production Shopify App
  • Production Gadget 环境
  • Production Client ID
  • Production Client Secret
  • Production App Proxy URL
  • Production Theme App Extension
  • Production 数据库
  • Production 环境变量

部署顺序如下:

Development 测试完成
        ↓
发布 Production App Proxy 配置
        ↓
部署 Gadget Production
        ↓
部署 Theme App Extension
        ↓
安装到目标商店
        ↓
在主题编辑器中添加 App Block
        ↓
执行真实前台请求测试
        ↓
检查生产日志

不要让 Production 主题去请求 Development Gadget 地址。

常见问题排查

前台请求返回 404

检查:

  • App Proxy prefix 是否一致
  • App Proxy subpath 是否一致
  • Theme Extension 中的 endpoint 是否正确
  • Shopify App 配置是否已经发布
  • 当前主题使用的是 Development 还是 Production
  • 请求地址是否多了或少了路径

前台显示 Permission denied

检查:

  • Unauthenticated 角色是否有权调用 Action
  • Action 是否允许前台调用
  • 当前请求是否属于 Shopify Customer
  • 是否把 Shopify App Users 权限误当成前台顾客权限
  • Gadget 项目是否部署了最新 Access Control

Gadget 没有收到请求

检查:

  • Theme Extension JavaScript 是否加载
  • API Client 是否正确初始化
  • App Proxy 是否部署
  • 当前主题是否添加了 App Block
  • 浏览器 Console 是否有 JavaScript 错误
  • 当前主题是否仍然使用旧的 Extension 版本

Shopify App Proxy 配置没有生效

检查:

  • 是否执行了应用部署
  • 是否发布了新的 App Version
  • 是否发布到了正确 Shopify App
  • 是否使用了正确的 Development 配置文件
  • Dev Dashboard 中是否出现新版本

主题中看不到 App Block

检查:

  • Extension 是否成功生成
  • Extension 是否成功上传
  • 当前主题是否支持 App Block
  • 应用是否安装到当前商店
  • 当前页面是否支持该类型的主题区块
  • 是否选择了错误的 Shopify Partners 组织

请求能返回,但数据属于错误商店

这是严重的数据隔离问题。

检查:

  • 是否使用 Gadget 提供的当前 Shop 上下文
  • 是否直接相信前端传入的 shop ID
  • Action 是否根据当前请求验证商店
  • 数据查询是否包含商店条件
  • 是否混用了 Development 和 Production 数据库

项目验收清单

完成以下检查后,App Proxy 才算真正跑通:

  • Gadget Shopify App 创建成功
  • Shopify Connection 正常
  • Development Store 安装成功
  • OAuth 流程正常
  • Global Action 可以执行
  • Unauthenticated 角色权限配置正确
  • App Proxy prefix 配置正确
  • App Proxy subpath 配置正确
  • 应用版本已经发布
  • Theme App Extension 成功生成
  • Theme Extension 可以运行
  • 主题编辑器可以添加 App Block
  • 前台页面可以发起请求
  • Gadget Action 收到请求
  • Gadget 日志显示正确 Shop ID
  • Gadget 日志显示正确角色
  • 前台可以显示返回内容
  • 生产环境没有暴露敏感信息
  • Development 和 Production 地址没有混用
  • 公开接口完成输入校验和频率限制

结语

Shopify App Proxy 的核心价值,是让店铺前台可以通过 Shopify 的代理路径访问应用后端,而不需要将后端地址和敏感凭证直接暴露给顾客。

完整架构如下:

Shopify Theme App Extension
        ↓
Shopify App Proxy
        ↓
Gadget API Client
        ↓
Gadget Access Control
        ↓
Gadget Action
        ↓
数据库或外部服务

开发时最关键的是让下面几处保持一致:

Shopify App Proxy 配置
        =
Theme Extension 请求路径
        =
Gadget API Client endpoint

同时要记住:

  • App Proxy 不等于自动完成所有权限控制
  • 店铺前台请求通常是未认证请求
  • 公开 Action 需要单独配置权限
  • 前端不能保存 Client Secret 和 Access Token
  • Production 和 Development 必须分开
  • 固定内容不必强行调用后端
  • 动态数据才适合使用 App Proxy

当这个简单的 Banner 示例跑通后,可以继续扩展成:

  • 动态商品推荐
  • 店铺活动组件
  • 会员专属内容
  • 自定义前台表单
  • 愿望清单
  • 商品对比工具
  • 个性化推荐
  • 外部 ERP 数据展示

citation:Change the app’s proxy URL

citation:Extend your theme with apps

citation:Building apps