# リモートフェッチ

## remote\_fetchとは何ですか？

`remote_fetch` は、レンダリング時にHTTPリクエストを実行し、解析結果を任意の別名で公開するカスタムのLiquidインラインタグです。天気、在庫、パーソナライズなど、ユーザーごとの最新データが必要なテンプレートコンテンツ向けに作られています。

ハイライト：

* シンプル：URLを取得して、その結果をテンプレート内で使えます。
* 安全：エラー時は既定でソフトフェイルします。
* 組み合わせ可能：複数の取得処理を連結できます（例：1つ目の出力を2つ目に使う）。
* 柔軟：ヘッダー、タイムアウト、レスポンスサイズ上限をサポートします。

## 基本構文

```liquid
{% remote_fetch 'https://api.example.com/users/{{profile.user_id}}' alias:'data' %}
こんにちは {{ data.first_name }}
```

主要な部分：

* URL（必須）：引用符で囲んだ文字列。ユーザープロフィールからのLiquid補間を含めることができます。
* alias: 解析されたレスポンスを利用できる変数名。既定値は `response`

タグ内では好きなものをレンダリングできます。aliasはスコープ内で利用可能です。

## 対応オプション

* `alias`
  * 解析された値にバインドされる変数名。
  * 既定値： `response`
* `timeout_ms`:
  * リクエストごとの読み取り/接続タイムアウト。
  * 既定値：750ms
* `max_bytes`:
  * レスポンスサイズのバイト単位での上限。予期しない大きなペイロードから保護します。
  * 既定値：1MB
* `headers`:
  * 引用符で囲んだJSONオブジェクト。ヘッダー値にはLiquid補間を含められます。
  * ヘッダー名は大文字小文字を区別せずに扱われます。
  * 例： `' { "Header-Name": "value", "X-Foo": "bar-{{macro}}" }'`

## URL補間

URL内にLiquid式を埋め込めます：

```liquid
{% remote_fetch 'https://api.example.com/users/{{profile.user_id}}?lang={{profile.locale}}' alias:'user' %}
```

これらはリクエスト前に解決されます。

## リクエストの連結

推奨される、分かりやすいアプローチ：

* 最初のURLを取得して、別名Aを設定します。
* Aが存在する場合、2つ目のURLを取得して別名Bを設定します。
* 最後に、AとBが存在すれば成功をレンダリングし、そうでなければ単一のフォールバックをレンダリングします。

例：

特定の記事URLについて、許可されていれば「今すぐ読む」CTAを表示し、そうでなければ「購読して読む」を表示します。

```liquid
{% remote_fetch 'https://cms.example.com/resolve?path={{landing_url}}' alias:'doc' %}

{% if doc %}
  {% remote_fetch 'https://paywall.example.com/entitlements?user_id={{profile.user_id}}&doc_id={{landing_url}}' alias:'ent' %}
{% endif %}

{% if doc and ent and ent.allowed %}
  今すぐ読む
{% else %}
  購読して読む
{% endif %}
```

## ヘッダー

headersオプションを使って、引用符で囲んだJSONオブジェクトとしてカスタムヘッダーを追加します：

```liquid
{% remote_fetch 'https://api.example.com/secure'
   headers:'{"Authorization":"Bearer {{secrets.api_key.token}}"}',
   alias:'data' %}
```

* 値はまず補間され、その後送信されます。
* 可能であれば、プレーンテキストのシークレットはテンプレートに残さないようにしてください。Secretsドキュメントに記載されているように、テンプレート内に埋め込まれた管理対象シークレットを優先してください。

## シークレット

{% hint style="info" %}
Remote Fetch用のSecretsの設定方法の詳細については、アカウントマネージャーにお問い合わせください。
{% endhint %}

### 概要

* Secretsを使うと、APIキーやトークンのような機密値をハードコードせずにテンプレート内で参照できます。
* これらはテンプレートコンテキストに `secrets` オブジェクトとして注入されます。
* 単純なシークレットと構造化されたシークレットの両方を参照でき、remote\_fetchのURL/ヘッダーで使用したり、Liquidフィルターと組み合わせたりできます。

### 使い方

* 単純なシークレット：
  * `{{ secrets.partner_api_key }}`
* 構造化シークレット（複数フィールドを持つオブジェクト）：
  * `{{ secrets.basic_auth.username }}`
  * `{{ secrets.basic_auth.password }}`
  * `{{ secrets.partner.token }}`

remote\_fetchでシークレットを使う

* ヘッダー内（推奨）：
  * 可能であれば、認証情報はURLではなくヘッダーで渡してください。
  * 例：&#x20;

    ```
    {%
      remote_fetch 'https://api.partner.com/v1/data?city={{profile.city }}' 
      headers:'{ "X-Api-Key": "{{ secrets.secret_name.token }}" }', 
      alias:'data' 
    %}
    ```

* URL内：
  * クエリ文字列内にトークンを必要とするプロバイダー向けです。
  * 例：<br>

    ```
    {%
       remote_fetch 'https://api.partner.com/v1/data?token={{secrets.partner.token }}&city={{ profile.city }}' 
       alias:'data' 
    %}
    ```

構造化シークレットの扱い

* 基本認証の例：
  * ユーザー名： `{{ secrets.basic_auth.username }}`
  * パスワード： `{{ secrets.basic_auth.password }}`
  * ヘッダー内で組み合わせる： <br>

    ```
    {% 
      remote_fetch 'https://api.example.com/account' 
      headers:'{ "Authorization": "Basic {{ secrets.basic_auth.username }}:{{ secrets.basic_auth.password }}" }', 
      alias:'account'
    %}
    ```

### ベストプラクティス

* 認証情報はヘッダーを優先する
  * プロバイダーが対応しているなら、Authorizationまたはカスタムヘッダーを使用してください。
* を使う `default` フィルターで堅牢性を確保する
  * シークレットが欠落している、またはまだ設定されていない可能性がある場合は、必ず適切なフォールバックを用意してください。
* コンテンツ内にシークレットを出力しない
  * タイトル、本文、ユーザーに見える出力にシークレット（部分的なものも含む）を含めないでください。
* 欠落しているシークレット&#x20;
  * Liquidは既定で欠落キーを空として解決します。フォールバックを提供するにはdefaultを使ってください。

## タイムアウトとサイズ制限

* `timeout_ms` リクエストごとのタイムアウト（接続＋読み取り）を設定します。ユーザー体験とバックエンドSLAのバランスが取れる値を選んでください。
* `max_bytes` レスポンス本文のサイズに上限を設定します。エンドポイントには安全な上限を使ってください。サーバーとレンダラーの両方を保護します。

## キャッシュと重複するremote\_fetch呼び出し

多くの作成者は、同じremote\_fetch URLを複数のテンプレートプロパティ（たとえば、タイトル、本文、ランディングURL）に配置します。このセクションでは、不要なネットワーク呼び出しを避けるために、キャッシュの仕組みを説明します。

### 概要

* リクエストはURL + メソッド + ヘッダーでメモリ内にキャッシュされます。
* 一意のリクエストが初めて行われたときにネットワークへアクセスし、レスポンスを短時間キャッシュします。
* その後の同一リクエストは（同じテンプレートレンダリング内でも）キャッシュから提供されます。

### キャッシュされるもの

* GETリクエストのレスポンス本文とステータス
* キーは次で決まります：
  * 完全にレンダリングされたURL（クエリ文字列とLiquid補間を含む）
  * HTTPメソッド（GET）
  * 送信された有効なヘッダー（名前は正規化されます）

これらのいずれかが変わると、別のキャッシュキーと見なされ、別リクエストになります。

### キャッシュが使われるとき

* 同じレンダリング内：最初の呼び出しでキャッシュが作成されます。そのレンダリング中の以降の同一呼び出しはキャッシュされたレスポンスを再利用します。
* 近接したレンダリング間：キャッシュエントリが期限切れになっておらず、メモリ圧迫で追い出されていない限り、同一リクエストはキャッシュから提供されます。

### スコープと寿命

* スコープ：実行中プロセスにローカルなメモリ内キャッシュ。サーバーやリージョン間で共有されません。
* TTL：各エントリには、「十分に新しい」パーソナライズに適した短い有効期間があります。TTL経過後、次の同一リクエストで更新されます。
* 容量：キャッシュには上限があります。満杯の場合はまず期限切れエントリが削除され、それでも満杯なら古いエントリが追い出されます。

## よくある質問

* 複数の呼び出しを連結するときに、単一のフォールバックをどう処理しますか？
  * 順次パターンを使います。2つ目の取得を `{% if firstAlias %}`でガードし、最後に1つの `{% if firstAlias and secondAlias %} … {% else %} fallback {% endif %}`.
* 取得エラー時にカスタムコンテンツを表示できますか？
  * はい。既定のソフトフェイルでは、aliasは未設定になります。条件分岐を使ってメッセージを表示してください。
* Liquidの値を使うヘッダーを渡せますか？
  * はい、 `headers:'{...}'`を介して可能です。値には `{{ ... }}`.
