关于 commercetools-storefront
面向 commercetools 店铺开发的框架无关技能,提供共享 BFF 基础、B2C 与 B2B 功能的实现模式,并附带 Next.js 和 Nuxt 适配参考。核心原则是由服务端代理 API 并管理会话与机密信息。
主要功能
- 01框架无关的店铺实现模式
- 02服务端 BFF 与会话设计
- 03服务端目录加载与客户端状态
- 04B2C 与 B2B 功能参考
- 05Next.js 与 Nuxt 适配指南
适用场景
- 01搭建 commercetools 店铺基础架构
- 02实现商品目录、购物车与结账
- 03为 B2C 或 B2B 店铺添加领域功能
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ commercetools/ commercetools-ai-plugins --skill commercetools-storefront安装后在 Claude Code 中输入/commercetools-storefront确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/b2b/add-api.mdapproval-workflows.mdcart.mdcheckout.mdcustomer-auth.mddashboard.mddata-loading.mdoverview.mdpermissions.mdproduct-detail.mdproduct-listing.mdquote-actions.mdquotes.mdsession-and-bu.mdshopping-lists.mdvariant-config.mdreferences/b2b/optional/recurring-orders.mdrecurring-prices.mdsuperuser.mdreferences/b2c/checkout.mdcustomer-auth.mdnavigation.mdoverview.mdproduct-detail.mdreferences/b2c/optional/bopis.mdbundles.mdpromotions.mdrecurring-orders.mdrecurring-prices.mdsuperuser.md详细介绍
commercetools Storefront(与框架无关)
适用于 commercetools 店面的生产模式——涵盖从共享 BFF 基础到 B2C 和 B2B 专属界面功能的各个方面。此处的模式与框架无关:每种模式都以一项决策和 commercetools 精确代码的形式呈现。你的前端框架负责提供实现原语(文件布局、渲染和路由原语、Cookie 读写绑定)。
架构假设——存在服务端层。 这些模式假设你的店面运行在包含服务端层的技术栈上(SSR / 服务端组件 / 独立 BFF 服务),该层能够保管密钥并代理 commercetools。以下 BFF 和密钥规则不可妥协,并且依赖这一假设。这些模式以 SSR 为先。对于特定框架,请使用
references/stack/下的技术栈适配器,例如 Next.js 技术栈,它会将此处的每个概念映射到相应框架的原语,并负责所有文件布局和渲染原语的细节。
参考实现。 代码示例中出现文件路径或渲染原语时,使用的是 Next.js App Router 作为参考实现。与框架无关的规则始终位于周围的说明文字中;适配器会指定确切的原语。
路径和状态约定。
每种参考实现都遵循以下路径和状态管理约定。这些约定并非技术上必需,但广泛用于生产店面,有助于浏览代码库并让新开发者快速上手。请在技术栈适配器中查找 concept-mapping.md,了解所用的确切原语。
约定 含义 <server>/服务端代码根目录;每种技术栈都会指定实际目录(例如 Next.js: lib/)<api>/面向客户端、供浏览器调用的 API 接口和路由 <server>/ct/*commercetools 辅助模块 <server>/ct/clientapiRoot单例<server>/types应用类型映射根目录(边界类型) <server>/mappers/commercetools→应用的映射器 客户端状态 框架的客户端状态管理 / 缓存层(具体库由技术栈决定)——用于存储每位用户可变的数据
工作流
调用此技能时,始终遵循以下步骤:
-
搜索文档(必需,首先运行) —— 在一个会话中首次使用此技能时,必须先运行此步骤再作答。它会收集最新且经过验证的文档,作为主要依据,并筛选出此技能涵盖的产品。使用此脚本搜索与此技能相关的文档;其他内容由 Knowledge MCP 覆盖。务必根据检索到的文档确认细节,而不能只依据技能文本:
node "<this skill's directory>/scripts/docs-search.mjs" \ --query "<extract key terms from user's question>" \ --app-name "<host app: claude-code, claude-chat, cursor, codex, copilot — or the host's own name>" \ --model "<current-model>" \ --commercetools-project-key "<if known>" \ --commercetools-region "<if known>" \ --limit 10仅当上下文中已有 commercetools 项目密钥和区域(格式如
api.{region}.commercetools.com)时才传入;否则省略两者。切勿搜索文件或向用户索要这些信息。 -
结合技能参考资料 —— 将分析输出与
./references/中的本地参考资料交叉核对,确保上下文完整。 -
提供实施指导 —— 根据用户目标集成模式,综合整理文档内容。
可选脚本
获取 GraphQL 架构 — 当你需要了解 commercetools GraphQL 查询或变更的上下文时运行此脚本,例如在编写查询前检查资源的字段、类型和可用操作,或根据实际架构验证刚生成的 GraphQL 查询/变更。此脚本会获取单个 commercetools 资源的部分 GraphQL SDL:
node scripts/graphql-schemata.mjs \
--resource-name "<commercetools resource, e.g. Cart, Product, Order>" \
--app-name "<host app: claude-code, claude-chat, cursor, codex, copilot — or the host's own name>" \
--model "<current-model>"
输出是该资源的 GraphQL SDL。如果无法识别资源名称,脚本会打印有效资源名称列表——选择正确的名称并重新运行。**注意:**SDL 可能包含存根类型——以存根形式呈现的引用资源,其实际类型名称会写在注释中。需要获取任何此类资源时,可使用该类型名称作为 --resource-name 重新运行此脚本。
获取 OpenAPI(REST)架构 — 当你需要了解 commercetools REST 端点、请求/响应负载或更新操作的上下文时运行此脚本,例如在构造请求前检查资源的 REST 操作,或根据实际规范验证刚生成的 REST 请求/负载。此脚本会获取单个 commercetools 资源的部分 OpenAPI 规范:
node scripts/openApi-schemata.mjs \
--resource-name "<commercetools resource, e.g. api-Cart-write, api-Customer-read, checkout-Application>" \
--app-name "<host app: claude-code, claude-chat, cursor, codex, copilot — or the host's own name>" \
--model "<current-model>"
输出是该资源的 OpenAPI 规范(YAML)。REST 资源使用读写拆分的命名形式(例如 api-Cart-read、api-Cart-write)。如果无法识别资源名称,脚本会打印有效资源名称列表——选择正确的名称并重新运行。**注意:**该规范不包含引用展开架构——需要单独获取被引用资源的架构时,可使用该资源名称作为 --resource-name 重新运行此脚本。
关键要点
**BFF 模式不可妥协。**所有 commercetools API 调用都通过服务器端点(BFF)进行。浏览器绝不直接调用 commercetools。密钥绝不会暴露在客户端代码包中(不使用公共环境变量前缀)。(这需要服务器层——请参阅上文的架构假设。)
**会话由服务器管理。**BFF 拥有会话状态,并且是唯一读取或写入会话状态的层。会话的存储方式由技术栈决定——可以在仅保存状态的 BFF 中使用 HTTP-only Cookie 保存签名令牌,也可以在有状态 BFF 中使用以不透明 Cookie 为键的服务器端会话存储。无论哪种方式,客户端都不会持有会话密钥或原始 commercetools 凭据,只会持有不透明引用。任何会话签名密钥都必须足够强,且绝不能硬编码或暴露给客户端。
**目录数据通过服务器渲染加载;可变用户状态使用客户端状态。**目录/不可变数据(类别页面、PDP)在服务器上加载,直接调用 <server>/ct/*。购物车、账户和用户专属数据则加载到客户端状态管理中 → 服务器端点 → commercetools SDK。
**<server>/ct/* 仅供服务器使用。**绝不要在客户端组件中导入它。请从应用类型映射根目录(<server>/types)导入类型。
决策步骤
按以下顺序构建店面——每个步骤都对应下方参考索引中的一个部分:
- **选择技术栈(如有)。**为你的前端选择
references/stack/<name>/下的适配器。它负责文件布局、渲染/路由原语、会话机制、客户端状态库和部署。 - **实现共享基础。**配置 BFF 边界、会话、commercetools 客户端、类型边界、购物车和数据加载——
references/core/。 - **添加可选核心功能。**按需加入跨领域扩展(例如周期性价格/订单)——
references/core/optional/。 - **选择 B2C 或 B2B。**加载对应的功能与规则——
references/b2c/或references/b2b/。 - **添加可选界面功能。**按需加入特定界面的扩展。
参考索引
技术栈
为你的前端技术栈选择适配器。references/stack/<name>/ 下的每个技术栈文件夹,都会将此技能中与框架无关的模式映射到该技术栈的基础构件,并负责其文件布局、渲染基础构件和部署。
| 技术栈 | 参考文档 | 命令 |
|---|---|---|
| Next.js 16(App Router)+ next-intl v4 + Tailwind v4 | stack/nextjs/overview.md | /nextjs-setup-project、/nextjs-deploy-vercel、/nextjs-deploy-netlify |
| Nuxt 4(Vue、SSR)+ Nitro + @nuxtjs/i18n v10 + nuxt-auth-utils + Pinia + Tailwind v4 | stack/nuxtjs/overview.md | /nuxtjs-setup-project |
共享基础(references/core/)
| 任务 | 参考文档 |
|---|---|
| 搭建新项目(依赖项、样式、区域设置路由、文件夹结构) | 框架专属——请参阅适配器的 overview.md |
| commercetools SDK 单例、服务端管理的会话、BFF 边界 | core/ct-client.md |
| 共享身份验证模式:commercetools 登录端点、服务端端点结构、客户端状态钩子、登出 | core/customer-auth.md |
添加新的国家/地区、货币/区域设置——COUNTRY_CONFIG 扁平结构 | core/add-country.md |
| 并行获取、服务端 TTL 缓存、客户端缓存水合、避免 N+1 | core/performance.md |
| 产品图片 URL 转换(CDN、Imgix、Cloudinary) | core/image-config.md |
| 购物车 CRUD、购物车状态/上下文、客户端状态钩子、迷你购物车抽屉 | core/cart.md |
| 全文搜索、分面配置、URL 状态、渲染器 | core/search-facets.md |
| 添加新的 BFF 端点和客户端状态钩子(3 层模式) | core/add-api.md |
| 添加新的独立页面或由 CMS 驱动的页面 | core/add-page.md |
| 服务端渲染与客户端获取的选择、映射器、BFF 结构、409 重试 | core/data-loading.md |
| 结账页面流程、步骤路由、下单 | core/checkout-page.md |
| PDP 路由、变体选择器、共享产品详情模式 | core/product-detail.md |
| 购物清单(愿望清单、已保存商品) | core/shopping-lists.md |
核心可选功能(references/core/optional/)
| 任务 | 参考文档 |
|---|---|
| 周期性价格——映射器、PDP 开关、选择器组件、使用 recurrenceInfo 添加到购物车 | core/recurring-prices.md |
| 周期性订单——范围限定、状态转换、结账后创建、周期性策略 | core/recurring-orders.md |
| 部署到 Vercel | 运行 /deploy-vercel——检查 commercetools 凭据,然后交由 Vercel 官方 Agent 技能处理 |
| 部署到 Netlify | 运行 /deploy-netlify——检查 commercetools 凭据,然后交由 Netlify 官方 Agent 技能处理 |
B2C 商店前台(references/b2c/)
| 任务 | 参考文档 |
|---|---|
| B2C 概览、要点总结、完整参考索引 | b2c/overview.md |
| 分类页面、产品映射器、commercetools Search API、ProductCard/Grid | b2c/product-listing.md |
| B2C PDP 路由、图片库、变体选择器、AddToCartButton | b2c/product-detail.md |
| 注册、登录、匿名购物车合并、受保护的账户布局 | b2c/customer-auth.md |
| 多步骤结账、配送方式、下单 | b2c/checkout.md |
| 导航模式、页眉、移动端菜单 | b2c/navigation.md |
| 共享 UI 组件库 | b2c/ui-components.md |
| PDP 变体选择器配置(屏蔽列表、色板、排序) | b2c/variant-config.md |
| 心愿单功能 | b2c/wishlist.md |
B2C 可选功能(references/b2c/optional/)
| 任务 | 参考文档 |
|---|---|
| CSR 身份模拟、双会话、行项目价格覆盖 | b2c/optional/superuser.md |
| 在线购买、门店自提 — 渠道 API、各门店库存 | b2c/optional/bopis.md |
| 产品捆绑 — 父/子购物车项目、级联更新 | b2c/optional/bundles.md |
| 产品折扣、购物车折扣、折扣码、促销横幅 | b2c/optional/promotions.md |
| 定期价格 — recurrencePrices[] 数组、门控 | b2c/recurring-prices.md |
| 定期订单 — 客户范围限定、originOrder 扩展、结账后创建、skip/setSchedule | b2c/recurring-orders.md |
B2B 店面(references/b2b/)
| 任务 | 参考文档 |
|---|---|
| B2B 概览、要点、完整参考索引 | b2b/overview.md |
| 会话字段、BU/店铺选择、渠道数据、BusinessUnitContext | b2b/session-and-bu.md |
| ProductApi 会话范围限定——店铺、渠道、价格注入、可用性 | b2b/product-listing.md |
| B2B PDP 路由、变体选择器、会话范围限定的定价 | b2b/product-detail.md |
| as-associate 购物车 CRUD、CartContext、通过 BU+店铺自动创建 | b2b/cart.md |
| 购物车结账和“申请报价”提交、BU 地址、下单 | b2b/checkout.md |
| 登录端点、BU 自动选择、登录时写入的会话字段 | b2b/customer-auth.md |
| RBAC——所有权限字符串、usePermissions、UI 门控模式 | b2b/permissions.md |
| 报价仪表板——CT 数据模型、每个 BU 的统一会话线程列表、状态标签、客户端状态钩子 | b2b/quotes.md |
| 报价买家操作——接受并下单、拒绝、重新协商、状态检查 | b2b/quote-actions.md |
| 审批规则、审批流程、谓词构建器、层级模型 | b2b/approval-workflows.md |
| 仪表板框架、统计小组件、页面、侧边栏导航项 | b2b/dashboard.md |
| 采购清单(通过 as-associate 使用 commercetools ShoppingList,按 BU 限定范围) | b2b/purchase-lists.md |
| 添加新的 B2B BFF 端点和客户端状态钩子 | b2b/add-api.md |
| B2B 数据加载——服务端渲染与客户端获取、映射器、BFF 结构 | b2b/data-loading.md |
| B2B 变体选择器配置 | b2b/variant-config.md |
B2B 可选功能(references/b2b/optional/)
| 任务 | 参考文档 |
|---|---|
| 超级用户角色——查看所有店铺购物车、切换购物车 | b2b/superuser.md |
| 定期价格——recurrencePrices[] 数组、as-associate 添加到购物车、PDP 门控 | b2b/recurring-prices.md |
| 定期订单——BU 范围限定、扩展购物车、从购物车创建、复制、仪表板 | b2b/recurring-orders.md |
优先级
严重
- BFF 架构——
<server>/ct/*仅限服务端使用。任何客户端组件中都不得导入 commercetools SDK。(需要服务端层——参见架构假设。) - 会话和客户端密钥——commercetools 客户端密钥及任何会话签名密钥都只能是服务端环境变量,不得硬编码,也不得暴露给客户端包(不使用公共环境变量前缀)。
- commercetools 登录端点——使用
apiRoot.login().post(),绝不使用apiRoot.customers().login()。 - B2B:as-associate 调用链——所有 B2B 写入操作(购物车、订单、报价、审批、BU)都必须通过
apiRoot.asAssociate().*。面向用户的 B2B 修改绝不使用项目级apiRoot.*。 - B2B:会话 B2B 字段——
businessUnitKey+storeKey+distributionChannelId+supplyChannelId+productSelectionId始终必须一起通过getStoreChannelData(storeKey)写入。 - B2B:三个字段的语言环境原子性——
locale、currency、country必须一起更新。语言环境或货币变更时重置cartId。
框架版本限制:查找你所用技术栈的
overview.md。
高优先级
- 并行获取 — 在服务端渲染的加载过程中,使用
Promise.all并行获取互不依赖的数据。避免请求瀑布。 - 类型安全 — 前端组件从应用类型映射根目录(
<server>/types)导入类型,绝不从<server>/ct/*导入。 - commercetools 类型边界 — 在
<server>/ct/之外使用类型前,先在<server>/mappers/中将 commercetools SDK 响应映射为应用类型。 - 客户端状态失效 — 登录、退出和下单后,使相关客户端状态失效或刷新。
- B2B:客户端状态管理器/缓存键中的 BU 键 — 所有仪表板状态都以
businessUnitKey为键(例如[KEY, businessUnitKey]元组),以便在切换 BU 时刷新。
中优先级
- Product Search API — 使用
apiRoot.products().search(),绝不使用已弃用的productProjections().search()。请参阅commercetools-platform技能 → product-search.md。 - 服务端 TTL 缓存 — 对很少变化的 commercetools 数据使用框架的服务端 TTL 缓存。绝不要将其用于用户或会话专属数据。(Next.js:
unstable_cache— 请参阅适配器。)
反模式速查
| 反模式 | 正确做法 |
|---|---|
在客户端组件中导入 <server>/ct/client(apiRoot) | 使用客户端状态钩子 → 服务端端点 → <server>/ct/ |
在组件中直接调用端点(fetch('/<api>/*')) | 将其封装在客户端数据/状态模块中 |
在页面或服务端端点中调用 new ClientBuilder() | 在 <server>/ct/client 中使用单例 apiRoot |
使用原始 fetch() 请求 commercetools REST 端点 | 始终使用 apiRoot — SDK 会管理 OAuth 令牌及其刷新 |
| 将 commercetools 密钥暴露在客户端打包内容中(使用公开环境变量前缀) | 使用仅限服务端的环境变量,不加公开前缀 |
product.name['en-US'](硬编码区域设置键) | getLocalizedString(product.name, locale) |
(centAmount / 100).toFixed(2) | formatMoney(centAmount, currencyCode, locale) |
对互不依赖的数据依次执行 await | Promise.all([fetchA(), fetchB()]) |
apiRoot.customers().login() | apiRoot.login().post() |
| 在组件中使用 commercetools SDK 类型 | 使用来自 <server>/types 的类型;在 <server>/mappers/ 中映射 |
B2B:为已登录用户调用 apiRoot.carts().post(...) | asAssociate().withAssociateIdValue(...).inBusinessUnitKey(...).carts().post(...) |
| B2B:BU 范围内的客户端状态未按 BU 设为键 | 使用 businessUnitKey 作为状态条目的键(例如 [KEY_ORDERS, businessUnitKey]) |
B2B:使用 StagedQuote.sellerComment 显示每轮内容 | 使用 Quote.sellerComment — 即创建报价时的快照 |
B2B:使用 apiRoot.shoppingLists() 访问采购清单 | 使用 asAssociate().*.shoppingLists() — 受 BU 范围限制并强制执行权限 |