前端开发
为 headless VTEX 店铺设计 BFF 层并保护…
适用于任何与 VTEX API 通信的 headless storefront、移动应用或 kiosk,用于建立服务端 BFF、分类公私 API、安全管理 VtexIdclientAutCookie 和 API key。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 38/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 55/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 60/100 ×0.15
适用场景
为 headless VTEX 店铺设计特定
解决思路
- 为每个 headless VTEX storefront 建立服务端 BFF 层,无例外;客户端代码不得直接请求私有 VTEX API endpoints,所有私有 API 调用都经 BFF。
- 除 Intelligent Search 外,所有 VTEX API 调用都通过 BFF 路由。
- 用服务端存储的 VtexIdclientAutCookie 做 shopper-scoped API 调用;用 X-VTEX-API-AppKey 和 X-VTEX-API-AppToken 做机器到机器调用。
- 将客户端暴露 VTEX_APP_KEY、VTEX_APP_TOKEN、VtexIdclientAutCookie、checkout cookies、shopper/session tokens 视为安全违规,不是建议或权衡;使用 must not、never expose、server-side only 等明确措辞,避免 avoid、prefer、ideally 等软措辞。
- 按路径分类 API:/pub/ endpoints 是公共的,但多数仍需要 BFF 代理以管理 session;/pvt/ endpoints 是私有的,必须通过 BFF。
- 即使公共 Checkout endpoints (/api/checkout/pub/) 也必须通过 BFF 代理,因为它们处理敏感个人数据。
- 为不同 BFF 模块创建最小权限的独立 API key,而不是共享一个具有广泛访问权限的 key。
- 检测客户端代码(src/、public/、app/ 或任何浏览器执行 bundle)中对 vtexcommercestable.com.br/api/checkout、/api/oms、/api/profile 或任何 /pvt/ endpoint 的 fetch 或 axios 调用;一旦发现立即停止,把这些调用移到 BFF。
- 前端调用 BFF 路由而不是 VTEX;例如请求 /api/bff/orders/${orderId} 并带 credentials: 'include' 发送 session cookie 到 BFF。
- 将 VtexIdclientAutCookie 存入安全服务端 session(如加密 cookie、Redis session store),不得存入 localStorage、sessionStorage 或任何客户端可访问的 JavaScript 变量。
- 检测 localStorage.setItem、sessionStorage.setItem 或客户端代码中赋值 VtexIdclientAutCookie;一旦发现立即停止,token 必须只由服务端管理。
- 在 VTEX 登录回调中从 req.cookies['VtexIdclientAutCookie'] 提取 token,存入 req.session.vtexAuthToken,然后 res.clearCookie('VtexIdclientAutCookie'),并重定向到前端账户页。
- BFF 代理从 req.session.vtexAuthToken 取 token,以 Cookie: VtexIdclientAutCookie=${vtexToken} 调用 VTEX API,例如 /api/checkout/pub/profiles。
- 让 VTEX_APP_KEY 和 VTEX_APP_TOKEN 只存在于服务端环境变量,不得出现在任何被打包、被服务或浏览器可访问的文件中。
- 检测 src/、public/、app/ 目录,或 NEXT_PUBLIC_、VITE_、REACT_APP_ 前缀环境变量中的 VTEX_APP_KEY、VTEX_APP_TOKEN、X-VTEX-API-AppKey、X-VTEX-API-AppToken;一旦发现立即停止并移到服务端专用环境变量。
- 服务端 VTEX client 从 process.env.VTEX_ACCOUNT、process.env.VTEX_APP_KEY、process.env.VTEX_APP_TOKEN 读取配置,并在请求中注入 X-VTEX-API-AppKey 和 X-VTEX-API-AppToken,而不是让前端读取。
- 采用请求流:前端仅直接调用 Intelligent Search API(公共、只读);其他所有请求进入 BFF Layer(Node.js/Express);BFF 注入 VtexIdclientAutCookie、X-VTEX-API-AppKey 和 X-VTEX-API-AppToken,验证并清理输入,然后代理到 Checkout API (/api/checkout/pub/...)、OMS API (/api/oms/pvt/...)、Profile API (/api/profile-system/pvt/...) 及其他 VTEX services。
- 搭建最小 BFF server:使用 express、express-session、cookie-parser、cors;调用 express.json()、cookieParser();cors({ origin: process.env.FRONTEND_URL, credentials: true });session 配置 secret: process.env.SESSION_SECRET!、resave: false、saveUninitialized: false、cookie: { secure: process.env.NODE_ENV === 'production', httpOnly: true, sameSite: 'strict', maxAge: 24 * 60 * 60 * 1000 }(匹配 VtexIdclientAutCookie TTL)。
- 生产 BFF 多副本时配置共享 session store(如 connect-redis、connect-memcached、Postgres-backed store),不要依赖默认 MemoryStore,否则 orderFormId、vtexAuthToken 等值会在请求被路由到不同副本时静默丢失。
- 对浏览器已能看到、仅跨步骤临时使用的值(例如订单放置 Step 1 到 Step 3 之间的 orderGroup),优先通过 request body 传递,而不是写入 session;相关 checkout 细节见 headless-checkout-proxy。
- 挂载 BFF routes:app.use('/api/bff/checkout', checkoutRoutes)、app.use('/api/bff/profile', profileRoutes)、app.use('/api/bff/orders', ordersRoutes),监听 PORT(默认 3001)。
- 实现 vtex-api-client.ts:定义 BASE_URL = https://${VTEX_ACCOUNT}.${VTEX_ENVIRONMENT}.com.br,默认 VTEX_ENVIRONMENT 为 vtexcommercestable;提供 vtexRequest<T>,接收 path、method、body、authType ('app-key' | 'user-token')、userToken;authType 为 app-key 时设置 X-VTEX-API-AppKey 和 X-VTEX-API-AppToken,为 user-token 且存在 userToken 时设置 Cookie: VtexIdclientAutCookie=${userToken};设置 Accept: application/json、Content-Type: application/json;响应非 ok 时抛错。
- 在 BFF route handler 中做 session 鉴权和输入校验:例如 ordersRoutes.get('/:orderId') 先用 /^[a-zA-Z0-9-]+$/ 校验 orderId;再检查 req.session.vtexAuthToken,没有则返回 401;然后用 vtexRequest 调用 /api/oms/pvt/orders/${orderId},authType 为 app-key;成功返回订单,失败记录并返回 500。
- 实现登录流程:/login 将用户重定向到 https://${VTEX_ACCOUNT}.myvtex.com/login?returnUrl=${encodeURIComponent(`${FRONTEND_URL}/auth/callback`)}。
- 实现 /callback:读取 req.cookies['VtexIdclientAutCookie'];没有则重定向到 ${FRONTEND_URL}/login?error=auth_failed;有则存 req.session.vtexAuthToken,res.clearCookie('VtexIdclientAutCookie'),重定向到 ${FRONTEND_URL}/account。
- 实现 /logout:req.session.destroy,失败返回 500;成功后 res.clearCookie('connect.sid') 并返回 { success: true }。
- 实现 /status:返回 { authenticated: !!req.session.vtexAuthToken }。
- 不要将 Intelligent Search 代理到 BFF;它增加延迟和服务器负载,应直接从浏览器调用公共只读 API。例如直接 fetch https://${STORE_ACCOUNT}.vtexcommercestable.com.br/api/io/_v/api/intelligent-search/product_search/?query=${encodeURIComponent(query)}&from=${from}&to=${to}&locale=en-US。
- 不要在所有 BFF 操作共享一个广泛权限的 API key(如 Owner role);按 BFF 模块创建独立 API key,例如 oms、checkout、catalog 各自使用 VTEX_OMS_APP_KEY/VTEX_OMS_APP_TOKEN、VTEX_CHECKOUT_APP_KEY/VTEX_CHECKOUT_APP_TOKEN、VTEX_CATALOG_APP_KEY/VTEX_CATALOG_APP_TOKEN。
- 不要在调试时记录 API credentials 或 auth tokens;在日志中脱敏敏感 headers:x-vtex-api-appkey、x-vtex-api-apptoken、cookie、authorization;例如 requestLogger 把匹配项替换为 [REDACTED]。
- 按 review checklist 核查:BFF 层是否存在(无例外);所有 /pvt/ 私有 API 调用是否走 BFF;VTEX_APP_KEY 和 VTEX_APP_TOKEN 是否只在服务端环境变量;API keys 是否不出现在 NEXT_PUBLIC_*、VITE_*、REACT_APP_* 环境变量;VtexIdclientAutCookie 是否在服务端 session 而非 localStorage/sessionStorage;Intelligent Search 是否直接前端调用而非不必要代理;不同 BFF 模块是否使用独立最小权限 API keys;日志是否脱敏敏感 headers。
适用边界 · 注意事项
- Checkout 专属代理逻辑与 OrderForm 管理应使用 headless-checkout-proxy,不要用本经验。
- Search API 集成细节应使用 headless-intelligent-search,不要用本经验。
- 缓存与 TTL 策略应使用 headless-caching-strategy,不要用本经验。