mcpField
V
vtex-io-client-integration

vtex-io-client-integration

github.com/vtex開発471.2k 回インストール2 週間前に更新
リポジトリ

vtex-io-client-integration について

VTEX IO のバックエンド開発者向けに、VTEX サービスや外部 API の統合方法を案内するスキルです。クライアントの選定、登録、設定とリクエストコンテキストの利用を扱い、直接の HTTP 呼び出しを避ける指針を示します。

主な機能

  1. 01ネイティブまたはカスタムクライアントの選定
  2. 02IOClients へのクライアント登録
  3. 03ctx.clients 経由での統合利用
  4. 04リトライ、タイムアウト、キャッシュ、メトリクスの設定
  5. 05バックエンドでの直接 HTTP 呼び出しの回避

活用シーン

  1. 01VTEX サービス向けクライアントの選定
  2. 02VTEX 以外の外部 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 サービスに対応するネイティブクライアントが @vtex/api または @vtex/clients にある場合は、そちらを優先してください。一般的な例として、カタログ、チェックアウト、ロジスティクス、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 インフラストラクチャサービスと高度な連携を行う場合に限り使用してください。パートナーアプリでは、InfraClient を直接拡張するのではなく、masterData や vbase などの高レベルクライアントやファクトリーを優先してください。
  • カスタムクライアントとネイティブクライアントはすべて、IOClients を拡張する Clients クラスを通じて node/clients/index.ts に登録してください。
  • ミドルウェア、リゾルバー、イベントハンドラー内でクライアントクラスをインスタンス化せず、必ず ctx.clients を通じて連携機能を利用してください。
  • クライアントの役割は、トランスポート、リクエストオプション、エンドポイントパス、小規模なレスポンス整形に限定してください。ビジネスルール、認可の判断、オーケストレーションはクライアントの外部に置いてください。
  • カスタムクライアントを構築する際は、アカウント名、ワークスペース、環境固有の VTEX URL をハードコードせず、account、workspace、利用可能な認証トークンなど、VTEX IO から渡される IOContext を常に使用してください。
  • 共通の InstanceOptions はランタイムのクライアント設定で構成し、明らかに異なる要件がある連携に限って、クライアント固有のオーバーライドを使用してください。
  • 重要なクライアント呼び出しでは metric オプションを使用し、ハンドラーレイヤーだけでなく、クライアントレイヤーでも連携を追跡・監視できるようにしてください。
  • エラーの正規化はクライアントの境界に近いところで行ってください。ただし、可観測性やデバッグに重要な HTTP ステータスコードやトランスポート障害を隠さないでください。
  • 外部サービスと連携する場合は、必要なアウトバウンドポリシーがアプリのコントラクトに宣言されていることを確認してください。ただし、ポリシーの詳細なモデリングは認証またはアプリコントラクトのスキルで扱ってください。
  • 移行やレガシーの例外的なケースでは、VTEX ホストのエンドポイントに対して一時的に ExternalClient を使用する場合がありますが、あくまで例外として扱ってください。ルーティング、認証、可観測性の一貫性を保つため、最終的にはネイティブクライアントまたは適切に文書化された VTEX クライアント抽象化への移行を目指してください。

クライアント選択ガイド:

クライアントの種類使用する場合使用を避ける場合
ExternalClientVTEX 外部の API を呼び出す場合ネイティブクライアントまたは Janus ベースの抽象化がすでに存在する、VTEX がホストする API
JanusClient@vtex/clients でまだラップされていない VTEX Core Commerce API を呼び出す場合Catalog、Checkout、Logistics、OMS など、ネイティブクライアントがすでに存在する VTEX サービス
InfraClient明示的に文書化されたガイダンスがある場合に限り、高度なインフラスタイルのクライアントを実装する場合パートナーアプリで一般的な VTEX API または外部 API を呼び出す場合

InstanceOptions のヒューリスティクス:

  • retries: 2 のような小さく明示的なクライアントのデフォルト設定と、1000~3000 ミリ秒のリクエスト timeout から始めてください。
  • 冪等な操作には、1~3 のような小さく有限の再試行回数を使用してください。
  • 上流 API が安全な冪等性の動作を明示的に文書化していない限り、冪等でない操作では自動再試行を避けてください。
  • 上流の不安定さを隠すために、高い再試行回数を設定しないでください。繰り返し発生する失敗は明確に表面化させ、ビジネスレイヤーで意図的に処理してください。
  • ハンドラーにヘッダー定義を散在させるのではなく、クライアントごとのヘッダーとメトリクスを優先してください。
  • メモリまたはディスクのキャッシュオプションは、読み取りの繰り返しにメリットがあり、レスポンスを安全に再利用できる場合にのみ使用してください。
  • 認証設定はクライアントのコンストラクターまたはファクトリー設定内に置き、ハンドラー間で重複させないでください。

厳守すべき制約

制約:サービス間のすべての HTTP 呼び出しは、VTEX IO クライアントを経由する必要があります

VTEX IO バックエンドアプリからの HTTP 通信は、必ず @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 を通じて使用する必要があります

クライアントは、IOClients を継承する Clients クラスに登録しなければなりません。また、ミドルウェア、リゾルバー、イベントハンドラーは、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
}
制約:連携の境界に合った、最も範囲の狭いクライアントの種類を選択してください

各連携では、その境界に適したクライアント抽象化を使用しなければなりません。より具体的なクライアントの種類やネイティブパッケージがすでに存在する場合に、すべての連携で安易に ExternalClient または JanusClient を使用しないでください。

重要な理由

クライアントの種類は意図を伝え、認証、URL、サービス境界の処理方法を決定します。誤った抽象化を使用すると、連携が理解しにくくなり、VTEX IO の規約から逸脱しやすくなります。

検出方法

対象が VTEX Core Commerce API の場合は、作業を中断し、ExternalClient よりも @vtex/clients のネイティブクライアントまたは JanusClient が適切かどうか確認してください。対象が VTEX にホストされている場合は、作業を中断し、ExternalClient をデフォルトとして使用する前に、より具体的な VTEX クライアント抽象化が文書化されていないことを確認してください。

正しい例

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

ネイティブクライアントとカスタムクライアントは、1 か所に登録します。

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 は小さなレジストリとして維持します。

よくある失敗

  • バックエンドのハンドラーで VTEX IO クライアントを使わず、axios、fetch、その他の生の HTTP ライブラリを使用する。
  • IOClients に登録せず、ハンドラー内でクライアントを直接インスタンス化する。
  • ネイティブの VTEX クライアントや、より適したアプリクライアントがすでにあるのに、ExternalClient を選択する。
  • クライアントをトランスポート層のラッパーとして保つ代わりに、ビジネスルール、検証、オーケストレーションをクライアントに含める。
  • ヘッダー、認証の設定、リトライ設定をクライアントまたは共有クライアント設定に集約せず、ハンドラー間に分散させる。
  • カスタムクライアントで宣言した外部連携に必要なアウトバウンドアクセスのポリシーを忘れる。

レビューチェックリスト

  • 各連携で、適切な VTEX IO クライアントの抽象化を使用していますか?
  • 利用可能な場合、@vtex/api または @vtex/clients のネイティブクライアントを優先していますか?
  • クライアントは IOClients に登録され、ctx.clients を通じて利用されていますか?
  • バックエンドの連携コードに生の HTTP ライブラリは含まれていませんか?
  • リトライ、タイムアウト、ヘッダー、メトリクスはハンドラー間に分散させず、クライアント層で設定していますか?
  • ビジネスルールをクライアント層から除外していますか?

参考資料

コメント