mcpField
V
vtex-io-client-integration

vtex-io-client-integration

github.com/vtex开发471.2k 次安装2 周前更新
代码仓库

关于 vtex-io-client-integration

为 VTEX IO 后端开发提供服务集成指导,帮助选择和配置客户端、注册客户端并通过请求上下文调用。重点涵盖原生与自定义客户端的取舍及避免直接使用 HTTP 库。

主要功能

  1. 01选择原生或自定义客户端
  2. 02在 IOClients 中注册客户端
  3. 03通过 ctx.clients 调用集成
  4. 04配置重试、超时、缓存与指标
  5. 05避免后端直接发起 HTTP 请求

适用场景

  1. 01为 VTEX 服务选择合适客户端
  2. 02接入非 VTEX 外部 API
  3. 03审查使用原始 HTTP 库的代码

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/vtex/skills --skill vtex-io-client-integration

安装后在 Claude Code 中输入/vtex-io-client-integration确认已加载。

详细介绍

客户端集成与服务访问

适用场景

当主要需要决定 VTEX IO 后端应用如何通过 VTEX IO 客户端系统调用 VTEX 服务或外部 API 时,使用此技能。

  • 在 node/clients/ 下创建自定义客户端
  • 在 @vtex/api 或 @vtex/clients 的原生客户端与自定义客户端之间进行选择
  • 在 IOClients 中注册客户端,并通过 ctx.clients 提供访问
  • 配置 InstanceOptions,例如重试、超时、请求头或缓存
  • 检查当前使用原始 HTTP 库的后端集成

以下场景不适用:

  • 决定 manifest.json 中的应用契约
  • 构建 node/index.ts 的结构或调整 service.json
  • 设计 GraphQL 架构或解析器契约
  • 建模路由授权或安全权限
  • 构建店面或管理后台的前端集成

决策规则

  • 如果 @vtex/api 或 @vtex/clients 中已有原生客户端可用于目标 VTEX 服务,优先使用。常见示例包括目录、结账、物流和 OMS 客户端。只有在没有合适的原生客户端或工厂时,才编写自定义客户端。
  • ExternalClient 主要用于非 VTEX 外部 API。当存在或更适合使用 @vtex/clients、JanusClient 或其他有文档说明的更高层 VTEX 客户端时,避免将其用于 *.myvtex.com 或 *.vtexcommercestable.com.br 等由 VTEX 托管的端点。
  • Janus 是 VTEX 的 Core Commerce API 网关。只有在需要通过 Janus 调用 VTEX Core Commerce API,且 @vtex/clients 中没有合适的原生客户端时,才使用 JanusClient。
  • 只有在有明确文档指引的情况下,才使用 InfraClient 对接 VTEX IO 基础设施服务。在合作伙伴应用中,优先使用 masterData 或 vbase 等更高层客户端和工厂,而不是直接扩展 InfraClient。
  • 在 node/clients/index.ts 中通过扩展 IOClients 的 Clients 类注册每个自定义或原生客户端。
  • 通过 ctx.clients 使用集成,切勿在中间件、解析器或事件处理器中实例化客户端类。
  • 客户端应专注于传输、请求选项、端点路径和简单的响应处理。业务规则、授权决策和编排应放在客户端之外。
  • 构建自定义客户端时,始终依赖 VTEX IO 传入的 IOContext,例如 account、workspace 和可用的身份验证令牌,不要硬编码账户名称、工作区或特定环境的 VTEX URL。
  • 在运行时客户端配置中配置共享的 InstanceOptions;只有在某项集成确有不同需求时,才使用特定客户端的覆盖配置。
  • 对重要的客户端调用使用 metric 选项,以便在客户端层跟踪和监控集成,而不仅仅是在处理器层进行跟踪和监控。
  • 将错误规范化处理放在客户端边界附近,但避免隐藏对可观测性和调试至关重要的 HTTP 状态码或传输故障。
  • 与外部服务集成时,确认应用契约中声明了所需的出站策略;但具体的策略建模应由 auth 或 app-contract 技能负责。
  • 在少数迁移或遗留场景中,可以暂时使用 ExternalClient 访问由 VTEX 托管的端点,但应将其视为例外。长期目标应是转向原生客户端或有相应文档说明的 VTEX 客户端抽象,以保持路由、身份验证和可观测性的一致性。

客户端选择指南:

客户端类型适用场景不适用场景
ExternalClient调用非 VTEX 的外部 API已有原生客户端或基于 Janus 的抽象的 VTEX 托管 API
JanusClient调用尚未由 @vtex/clients 封装的 VTEX Core Commerce API已有原生客户端的 VTEX 服务,例如 Catalog、Checkout、Logistics 或 OMS
InfraClient仅在有明确文档指导的情况下,实现高级基础设施类客户端合作伙伴应用中的一般 VTEX 或外部 API

InstanceOptions 启发式建议:

  • 从较小且明确的客户端默认值开始,例如 retries: 2,以及介于 1000 到 3000 毫秒之间的请求 timeout。
  • 对幂等操作使用较小的有限重试值,例如 1 到 3。
  • 除非上游 API 明确说明幂等操作是安全的,否则避免对非幂等操作自动重试。
  • 不要使用较高的重试次数来掩盖上游不稳定。应清楚地呈现反复发生的故障,并在业务层有针对性地处理。
  • 优先使用客户端级别的请求头和指标,而不是在各个处理程序中分散定义请求头。
  • 仅在重复读取确有需要且响应可以安全复用时,才使用内存或磁盘缓存选项。
  • 将身份验证设置放在客户端构造函数或工厂配置中,不要在各个处理程序中重复设置。

硬性约束

约束:所有服务间 HTTP 调用都必须通过 VTEX IO 客户端

来自 VTEX IO 后端应用的 HTTP 通信 MUST 通过 @vtex/api 或 @vtex/clients 客户端进行。服务集成不得使用 axios、fetch、got 或 node-fetch 等原始库。

原因

VTEX IO 客户端提供原始库绕过的传输行为,包括身份验证上下文、重试、指标、缓存选项以及可感知基础设施的请求执行。原始 HTTP 调用会使集成更难观测,也更容易配置错误。

检测

如果在 VTEX IO 后端服务中看到 axios、fetch、got、node-fetch 或直接编写的临时 HTTP 代码,请立即停止,并将其替换为合适的 VTEX IO 客户端模式。

正确

import type { IOContext, InstanceOptions } from '@vtex/api'
import { ExternalClient } from '@vtex/api'

export class WeatherClient extends ExternalClient {
  constructor(context: IOContext, options?: InstanceOptions) {
    super('https://api.weather.com', context, {
      ...options,
      headers: {
        'X-VTEX-Account': context.account,
        'X-VTEX-Workspace': context.workspace,
        'X-Api-Key': process.env.WEATHER_API_KEY,
        ...options?.headers,
      },
    })
  }

  public getForecast(city: string) {
    return this.http.get(`/v1/forecast/${city}`, {
      metric: 'weather-forecast',
    })
  }
}

错误

import axios from 'axios'

export async function getForecast(city: string) {
  const response = await axios.get(`https://api.weather.com/v1/forecast/${city}`, {
    headers: {
      'X-Api-Key': process.env.WEATHER_API_KEY,
    },
  })

  return response.data
}
约束:客户端必须在 IOClients 中注册,并通过 ctx.clients 使用

客户端 MUST 在继承 IOClients 的 Clients 类中注册,中间件、解析器或事件处理程序 MUST 通过 ctx.clients 访问客户端。

原因

VTEX IO 客户端注册表可确保一致地应用当前请求上下文、选项、缓存行为和检测机制。在处理程序中直接实例化客户端会绕过这一共享生命周期,并产生脆弱的集成代码。

检测

如果在中间件、解析器或事件处理程序中看到 new MyClient(...),请立即停止。将客户端移至 node/clients/,在 IOClients 中注册,并通过 ctx.clients 使用。

正确

import { IOClients } from '@vtex/api'
import { Catalog } from '@vtex/clients'

export class Clients extends IOClients {
  public get catalog() {
    return this.getOrSet('catalog', Catalog)
  }
}
export async function getSku(ctx: Context) {
  const sku = await ctx.clients.catalog.getSkuById(ctx.vtex.route.params.id)
  ctx.body = sku
}

错误

import { Catalog } from '@vtex/clients'

export async function getSku(ctx: Context) {
  const catalog = new Catalog(ctx.vtex, {})
  const sku = await catalog.getSkuById(ctx.vtex.route.params.id)
  ctx.body = sku
}
约束:选择与集成边界相匹配的最具体客户端类型

每项集成 MUST 根据其边界使用正确的客户端抽象。如果已有更具体的客户端类型或原生包,不要默认对所有集成都使用 ExternalClient 或 JanusClient。

原因

客户端类型体现设计意图,并决定如何处理身份验证、URL 和服务边界。使用错误的抽象会让集成更难理解,也更容易偏离 VTEX IO 惯例。

检测

如果目标是 VTEX Core Commerce API,请立即停止,并确认使用 @vtex/clients 中的原生客户端或 JanusClient 是否比 ExternalClient 更合适。如果目标由 VTEX 托管,请立即停止,并确认不存在有文档说明的、更具体的 VTEX 客户端抽象后,再默认使用 ExternalClient。

正确

import type { IOContext, InstanceOptions } from '@vtex/api'
import { JanusClient } from '@vtex/api'

export class RatesAndBenefitsClient extends JanusClient {
  constructor(context: IOContext, options?: InstanceOptions) {
    super(context, options)
  }
}

错误

import type { IOContext, InstanceOptions } from '@vtex/api'
import { ExternalClient } from '@vtex/api'

export class RatesAndBenefitsClient extends ExternalClient {
  constructor(context: IOContext, options?: InstanceOptions) {
    super(`https://${context.account}.vtexcommercestable.com.br`, context, options)
  }
}

推荐模式

推荐的文件布局:

node/
├── clients/
│   ├── index.ts
│   ├── catalog.ts
│   └── partnerApi.ts
├── middlewares/
│   └── getData.ts
└── index.ts

在同一处注册原生客户端和自定义客户端:

import { IOClients } from '@vtex/api'
import { Catalog } from '@vtex/clients'
import { PartnerApiClient } from './partnerApi'

export class Clients extends IOClients {
  public get catalog() {
    return this.getOrSet('catalog', Catalog)
  }

  public get partnerApi() {
    return this.getOrSet('partnerApi', PartnerApiClient)
  }
}

使用明确的路由和选项创建自定义客户端:

import type { IOContext, InstanceOptions } from '@vtex/api'
import { ExternalClient } from '@vtex/api'

export class PartnerApiClient extends ExternalClient {
  private routes = {
    order: (id: string) => `/orders/${id}`,
  }

  constructor(context: IOContext, options?: InstanceOptions) {
    super('https://partner.example.com', context, {
      ...options,
      retries: 2,
      timeout: 2000,
      headers: {
        'X-VTEX-Account': context.account,
        'X-VTEX-Workspace': context.workspace,
        ...options?.headers,
      },
    })
  }

  public getOrder(id: string) {
    return this.http.get(this.routes.order(id), {
      metric: 'partner-get-order',
    })
  }
}

在运行时配置共享客户端选项:

import type { ClientsConfig } from '@vtex/api'
import { Clients } from './clients'

const clients: ClientsConfig<Clients> = {
  implementation: Clients,
  options: {
    default: {
      retries: 2,
      timeout: 2000,
    },
  },
}

在处理程序中通过 ctx.clients 使用客户端:

export async function getOrder(ctx: Context) {
  const order = await ctx.clients.partnerApi.getOrder(ctx.vtex.route.params.id)
  ctx.body = order
}

如果客户端文件变得过大,请按边界明确的集成领域拆分,并将 node/clients/index.ts 保持为简洁的注册表。

常见故障模式

  • 在后端处理程序中使用 axios、fetch 或其他原始 HTTP 库,而不是 VTEX IO 客户端。
  • 直接在处理程序中实例化客户端,而不是在 IOClients 中注册客户端。
  • 选择 ExternalClient,而原生 VTEX 客户端或更具体的应用客户端已经存在。
  • 将业务规则、验证或编排放入客户端,而不是让客户端仅作为传输封装层。
  • 将标头、身份验证设置和重试设置散落在各个处理程序中,而不是集中配置在客户端或共享客户端配置中。
  • 忘记自定义客户端中声明外部集成所需的出站访问策略。

审查清单

  • 每个集成是否使用了正确的 VTEX IO 客户端抽象?
  • 是否优先使用 @vtex/api 或 @vtex/clients 中可用的原生客户端?
  • 是否在 IOClients 中注册客户端,并通过 ctx.clients 使用客户端?
  • 后端集成代码中是否未使用原始 HTTP 库?
  • 重试、超时、标头和指标是否在客户端层配置,而不是散落在各个处理程序中?
  • 是否避免在客户端层放置业务规则?

参考资料

评论

支持 Markdown