vtex-io-client-integration について
VTEX IO のバックエンド開発者向けに、VTEX サービスや外部 API の統合方法を案内するスキルです。クライアントの選定、登録、設定とリクエストコンテキストの利用を扱い、直接の HTTP 呼び出しを避ける指針を示します。
主な機能
- 01ネイティブまたはカスタムクライアントの選定
- 02IOClients へのクライアント登録
- 03ctx.clients 経由での統合利用
- 04リトライ、タイムアウト、キャッシュ、メトリクスの設定
- 05バックエンドでの直接 HTTP 呼び出しの回避
活用シーン
- 01VTEX サービス向けクライアントの選定
- 02VTEX 以外の外部 API の統合
- 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 クライアント抽象化への移行を目指してください。
クライアント選択ガイド:
| クライアントの種類 | 使用する場合 | 使用を避ける場合 |
|---|---|---|
ExternalClient | VTEX 外部の 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 ライブラリは含まれていませんか?
- リトライ、タイムアウト、ヘッダー、メトリクスはハンドラー間に分散させず、クライアント層で設定していますか?
- ビジネスルールをクライアント層から除外していますか?
参考資料
- Node クライアントの使用 -
ctx.clientsを通じてクライアントを利用する方法 - クライアントの開発 -
@vtex/apiを使用してカスタムクライアントを構築する方法 - VTEX IO クライアントの使用 - Core Commerce API に VTEX クライアントを使用する方法
- クライアント - VTEX IO クライアントのアーキテクチャとネイティブクライアントの一覧