mcpField
E
experience-ui-bundle-salesforce-data-access

experience-ui-bundle-salesforce-data-access

github.com/forcedotcom開発1.1k5.2k 回インストール1 日前に更新
リポジトリ

experience-ui-bundle-salesforce-data-access について

Salesforce の UI bundle で `@salesforce/platform-sdk` を使う開発者向けのデータアクセス手順をまとめたスキルです。GraphQL のスキーマ確認、実行環境に応じたガード、キャッシュ、REST の利用方法を扱います。

主な機能

  1. 01SDK のクエリと変更の手順
  2. 02GraphQL スキーマの確認
  3. 03実行環境に応じた API ガード
  4. 04キャッシュと更新のガイダンス
  5. 05REST 連携のガイダンス
  6. 06旧 API からの移行

活用シーン

  1. 01Salesforce レコードのクエリ作成
  2. 02UI bundle への変更処理の追加
  3. 03キャッシュ済み・古い結果への対応
  4. 04旧 SDK 呼び出しの移行

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/forcedotcom/sf-skills --skill experience-ui-bundle-salesforce-data-access

インストール後、Claude Code で/experience-ui-bundle-salesforce-data-accessを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
caching.md
graphiti-cli.md
graphql-hand-authoring.md
migration.md
rest-and-integration.md
sdk-api.md
scripts/
graphql-search.sh

詳細

Salesforce Data Access(UI バンドル)

UI バンドルでの Salesforce データアクセスはすべて、@salesforce/platform-sdk データ SDK を経由します。SDK は認証、CSRF、ベース URL の解決を処理します。また、WebApp サーフェスではすべての GraphQL クエリをデフォルトでキャッシュします。

このファイルは、ワークフローとガードレールの中核です。詳細はリンク先のドキュメントに記載されています。

  • references/graphiti-cli.md — 小さな JSON 仕様から、スキーマに準拠し、ガードレールが適用されたクエリ、変数、型を生成する graphiti CLI(sf-gql-* コマンド)。以下の手順で GraphQL を記述する際に推奨される方法です。利用できない場合は、スキーマ検索スクリプトにフォールバックします。
  • references/sdk-api.md — query/mutate の呼び出し方法と生成型の配置先。動作上の詳細(サーフェス、エラー処理方針、QueryResult)はtier-2bに基づいています。
  • references/caching.md — デフォルトで有効なキャッシュと、2 種類の更新方法。動作の根拠は、インストール済みの場合はtier-2bのdocs/data/、そうでない場合はこのファイルに記載されたバージョン付きの完全なフォールバックです。
  • references/graphql-hand-authoring.md — スキーマの検索、読み取り/変更のテンプレート、プラットフォームのすべてのガードレール(@optional、ページネーション、制限、セミジョイン、ラッパー、エラーテーブルなど)。
  • references/rest-and-integration.md — sdk.fetch、サポート対象 API の許可リスト、リアクティブな連携/ライフサイクルのパターン。
  • references/migration.md — 古い@salesforce/sdk-dataの呼び出し形式のコードから新しい名前空間への移行。利用可能なコードとして旧 API が登場するのは、ここだけです。

基本的な考え方

const sdk = await createDataSDK()を実行します。次に、sdk.graphqlは関数ではなく名前空間です。読み取りには**sdk.graphql!.query({...})を、書き込みにはsdk.graphql!.mutate({...})**を使用します。WebApp では、すべてのquery()がデフォルトでキャッシュされます(300s)。HTTP 200 は成功を意味するとは限りません。必ずresult.errorsを確認してください。クエリを実行する前に、すべてのエンティティとフィールドがスキーマに存在することを確認してください。未確認のフィールドが 1 つでもあると、実行時にクエリ全体が失敗します。また、schema.graphqlは目視で確認するには大きすぎるため、検索してください。

import { createDataSDK, gql } from "@salesforce/platform-sdk"; // gql tags the query string so codegen + eslint validate it

const sdk = await createDataSDK();
const result = await sdk.graphql!.query({ query: GET_ACCOUNTS, variables });
if (result.errors?.length) throw new Error(result.errors.map((e) => e.message).join("; "));
const rows = result.data?.uiapi?.query?.Account?.edges?.map((e) => e.node) ?? []; // unwrap edges/node; read field values via .value

型付き呼び出しパラメーター(query<GetAccountsQuery, GetAccountsQueryVariables>)、CacheControl型、そして Connection からノード型を抽出して型付けを簡潔にするNodeOfConnection<T>は、すべてreferences/sdk-api.mdに記載されています。

これは変更されました(互換性のない変更 — PR #502)。 以前の呼び出し形式 sdk.graphql(...)と以前のパッケージ名は廃止されています。上記のコードだけが正しい形式です。既存のコード(または古いdist/の成果物)で旧 API を見つけても、コピーせず、既存コードの作業に従って変換してください。

sdk.graphql!は WebApp 専用です。 上記の非 null アサーションが正しいのは、バンドルが WebApp のみで実行される場合に限ります。他のサーフェスではクラッシュする可能性があるため、記述する前に判断してください。詳しくは、以下の**サーフェス — !とガード**を参照してください。


インストール済みの型に基づいて SDK の契約を確認する(tier-2a)

@salesforce/platform-sdk は共有のバージョン系列で強制的に公開され、更新も速いパッケージです。この SKILL の説明は、ある時点での呼び出し契約を示すスナップショットです。実際に使用しているバージョンでは、インストール済みの型宣言が正となります。query/mutate を記述する前に、インストール済みの型を読み、そちらを優先してください。

  • node_modules/@salesforce/platform-sdk/dist/core/data.d.ts — query/mutate のシグネチャ、QueryResult(subscribe/refresh を持つ)と MutationResult(仕様上、どちらも持たない)、CacheControl のユニオン型、デフォルトの TTL。
  • node_modules/@salesforce/platform-sdk/dist/data/index.d.ts — createDataSDK、gql、NodeOfConnection。

優先順位 — インストール済みの .d.ts は、この SKILL の説明より優先されます。 ここに記載されたシグネチャ、型、またはデフォルト値がインストール済みの宣言と異なる場合は、宣言に従い、相違を記録してください。説明に合わせて型を「修正」しないでください。

根拠の段階(1 つのモデル、2 つの軸):

Tier根拠答える内容手段
tier-1GraphQL スキーマどのようなデータが存在するかgraphiti / graphql-search.sh(Precondition #2)
tier-2aSDK 契約どのように呼び出すか上記のインストール済み .d.ts
tier-2bSDK 動作どのように動作するかインストール済みの docs/data/ フォルダー(以下を参照)
spineこの SKILL.md3 つすべてを統括するワークフローとガードレール。各 tier で根拠を確認できない場合のフォールバック

.d.ts がない場合のフォールバック — パッケージはインストールされているものの、型宣言を同梱していない(古い、または型が除去されたビルド成果物)場合は、この SKILL の説明をできる限りの根拠として使用してください。このフォールバックは、パッケージがない場合には適用されません。@salesforce/platform-sdk がインストールされていない場合は、停止してインストールしてください(Precondition #1)。手元にない依存関係に対して、説明だけを根拠に呼び出しを記述しないでください。


インストール済みのドキュメントに基づいて SDK の動作を確認する(tier-2b)

同じパッケージには、型と併せて作成された動作ガイドも含まれています。 node_modules/@salesforce/platform-sdk/docs/data/(番号順にファイルを読みます)。tier-2a の .d.ts が呼び出しの契約を定めるのに対し、このフォルダーは契約に明記されていない動作、すなわちキャッシュモデル、サーフェスでの ! とガードの選択、エラー処理の方針、移行時の考え方についての正本です。キャッシュポリシー、サーフェスのアサーション、またはエラー処理の方針を選ぶ前に読み、記載内容を優先してください。tier-2a と同じ優先順位です(インストール済みのソースはこの説明より優先され、存在する場合は、より詳細でバージョンに即した内容です)。

フォルダーがない場合のフォールバック(古い SDK、または型のみのビルド):この SKILL では、作業を進めるために必要な最小限の動作ごとのフォールバックを以下および各セクションに記載しています。それに従ってください。tier-2a と同様に、パッケージがない場合は別です。@salesforce/platform-sdk がインストールされていない場合は、停止してインストールしてください(Precondition #1)。


サーフェス — sdk.graphql! とガード

sdk.graphql/sdk.fetch は実際にオプショナル(型は graphql?: …)であり、! でアサートしてよいかどうかは、実行時クラッシュに関わる判断です。query/mutate を記述する前に判断してください。**フォールバックルール:WebApp 専用のバンドルなら sdk.graphql! を安全に使用できます。WebApp 以外(Mosaic/OpenAI/MCPApps)で実行される可能性があるバンドルなら、先にガードしてください(if (!sdk.graphql) return …)。その後で呼び出します。**WebApp 専用だと証明できない場合はガードしてください。後に別の場所へ出荷される可能性のある裸の ! は Cannot read properties of undefined を発生させますが、TypeScript では検出されません(sdk.fetch! も同様です)。

サーフェスのマトリックス、ポータブルなガードのスニペット、および詳細な理由は、tier-2b の docs/data/ が根拠です(上記のフォールバックを参照)。ガードのスニペットは references/sdk-api.md にも記載されています。

ステップ 0 — タスクを振り分ける

タスクの内容移動先
レコードを読み取る以下の 読み取りワークフロー
レコードを作成/更新/削除する以下の 書き込みワークフロー
オブジェクト/項目のメタデータ、選択リスト値、関連リストのメタデータ、集計以下の レコード CRUD 以外
データが古い/「更新ボタンを追加」/「キャッシュ期間を延長」以下の 鮮度とキャッシュ
GraphQL では表現できないもの(Apex REST、ファイルアップロード、Einstein)references/rest-and-integration.md
古い sdk.graphql?.(query, vars) コードを移行する以下の 既存コードの作業

GraphQL で扱えるのはレコードの読み取りと書き込みだけではありません。uiapi 名前空間で公開されているものなら何でも、GraphQL を優先してください(レコード CRUD 以外を参照)。REST を使うのは、データが実際に uiapi の外部にある場合(Apex REST、ファイルアップロード、Einstein)に限ります。references/rest-and-integration.mdを参照してください。


前提条件 — クエリを書く前に確認する

以下の <skill-dir> は、このスキルがインストールされている場所(この SKILL.md が読み込まれたディレクトリ)を指します。スキーマ検索スクリプトはその中にあります。このスクリプトは、ディレクトリを上にたどって schema.graphql を探すことはありません。親ディレクトリにあるスキーマは別の組織に属している可能性があり、異なる組織の項目に対して検証してしまうためです。スキーマを明示的に指定してください。schema.graphql がある SFDX プロジェクトのルートから実行するか、--schema <path> を渡すか、GRAPHQL_SCHEMA=<path> を設定してください。スクリプトは解決したスキーマを標準エラー出力に [graphql-search] using schema: … と表示します。確認のため、これをざっと見て、正しいファイルを基準にしていることを確かめてください。

#要件確認方法要件を満たさない場合
1@salesforce/platform-sdk がインストールされており、その仕様および動作のドキュメントを読んでいるUI バンドルディレクトリの package.json に記載されていることを確認します。次に dist/core/data.d.ts と dist/data/index.d.ts(tier-2a)および docs/data/ フォルダー(tier-2b)を読み、この SKILL の説明よりもそれらを優先してください未インストール → ユーザーにインストールするよう伝え、作業を続行できません。インストール済みだが .d.ts/docs/ がない(古い、または型定義のみの成果物)→ 説明文をフォールバックとして使用します
2基準を確認するツールが解決できる推奨: UI バンドルディレクトリから npx graphiti sf-gql-discover '{"org":"<alias>","mode":"list_objects"}' を実行すると、オブジェクトが返されます。代替: プロジェクトのルートから bash <skill-dir>/scripts/graphql-search.sh <Entity> を実行すると、schema.graphql not found ではなく検索結果が出力されますgraphiti の依存関係がない/組織の初期化ができない → スクリプトを使用します。スクリプトで schema.graphql が見つからない → --schema <path> を渡すか、UI バンドルディレクトリから npm run graphql:schema を実行します。(CLI の設定については references/graphiti-cli.md を参照してください)
3対象のオブジェクト/項目がデプロイされているオブジェクトが sf-gql-discover に表示される(または graphql-search.sh <Entity> が出力を返す)ことを確認しますエンティティが見つからない場合、通常はデプロイされていないか、キャッシュ/スキーマが古くなっています。更新するには、npx graphiti sf-gql-connect '{"org":"<alias>","forceRefresh":true}'(CLI)または npm run graphql:schema(スクリプト)を実行します。それでも見つからない場合は、メタデータをデプロイし(platform-metadata-deploy スキルで対応できます)、権限セットを割り当ててから再確認してください

前提条件を満たしていなくても、コンポーネント、ルート、レイアウトのひな形は作成できます。ただし、データには空の配列/null を使用し、クエリの箇所に // TODO: add query after schema verification を記して、後で対応する計画項目を追加してください。スキーマのワークフローが完了するまでは、GraphQL 文字列を書かないでください。


読み取りワークフロー

  1. まずスキーマを調べます。名前を推測してはいけません。 推奨(graphiti): API 名が少しでも不確かな場合は、説明する前に一覧表示します — npx graphiti sf-gql-discover '{"org":"<alias>","mode":"list_objects","search":"<intent>"}' で実際の名前を確認し、続いて npx graphiti sf-gql-discover '{"org":"<alias>","mode":"describe_object","object":"<Entity>"}' で正確なフィールド名/型名、選択リストの値、フィルター可能/ソート可能かどうかを確認します。空のリストやオブジェクトが見つからないことは組織についての事実(名前が間違っている、またはデプロイされていない)であり、ツールの失敗ではありません — 再度一覧表示するか、forceRefresh を実行してください。この場合にスクリプトへフォールバックしてはいけません(ガードレール 2 を参照)。フォールバックが必要なのは、CLI を本当に実行できない場合のみです(graphiti 依存関係がない/組織をプライムできない): SFDX プロジェクトのルートから bash <skill-dir>/scripts/graphql-search.sh <Entity> を実行します。 (詳細なルール:references/graphql-hand-authoring.md。)

  2. クエリを作成します。 推奨 — graphiti でコンパイルします: npx graphiti sf-gql-list '{"org":"<alias>","object":"<Entity>","fields":[…],"first":N}' は、@optional、value/displayValue、edges/node、first:/pageInfo が適用済みの { query, variables, types, warnings } エンベロープを返します。warnings: [] であることを確認してください(空でない配列は、オブジェクトがプライム済みスキーマに含まれていないことを意味し、クエリの機能が低下しています。そのまま使用してはいけません)。その後、query をそのままインラインの gql(シンプルな場合)または外部の .graphql ファイル(1 ファイルにつき 1 オペレーション。バンドラーの ?raw サフィックスを使ってインポートします — import Q from "./q.graphql?raw" により、ファイルがプレーンな文字列として読み込まれます)に貼り付けます。フォールバック — 手動で作成する場合: 選択可能な FLS 制御対象フィールドすべてに @optional を適用します — スカラーリーフフィールド(Name @optional { value })、親子リレーションシップ、およびそれらの内部のフィールドが対象です。ただし、Id、コネクションの構造要素(edges、node、コネクションフィールド自体)、pageInfo には適用しません。graphiti の出力ではこれらにディレクティブが付かず、これが標準的な配置です。常に first: を設定し、ページングされる可能性がある場合は pageInfo を含めてください。いずれの場合も、詳しい仕組みとプライム済み/機能低下時の動作については、references/graphiti-cli.md を参照してください。

  3. 型を生成します — npm run graphql:codegen(UI バンドルディレクトリから実行)→ src/api/graphql-operations-types.ts。

  4. 生成された型を使って query() を呼び出します:

    import type { GetAccountsQuery, GetAccountsQueryVariables } from "../graphql-operations-types";
    
    const result = await sdk.graphql!.query<GetAccountsQuery, GetAccountsQueryVariables>({
      query: GET_ACCOUNTS,
      variables: { first: 20 },
      // cacheControl,            // optional — see Freshness & caching
    });
    
  5. 結果を処理します。 result.data と result.errors は初期スナップショット、 result.subscribe/result.refresh はリアクティブなハンドルです。data を読み取る前に、必ず errors を確認してください:

    if (result.errors?.length) throw new Error(result.errors.map((e) => e.message).join("; "));
    const rows = result.data?.uiapi?.query?.Account?.edges?.map((e) => e.node) ?? [];
    

@optional によってフィールドが省略される可能性があるため、?./?? を使って、結果を利用するコードで問題が起きないようにしてください。エラー処理の方針(厳格/許容/判別)はティア 2bの docs/data/ に基づきます(フォールバック: ガードレール #1 — 必ず result.errors を確認してください)。NodeOfConnection の型定義については、references/sdk-api.md を参照してください。


書き込みワークフロー

上記の1~3(スキーマの参照 → ミューテーションの作成 → コード生成)と同じです。graphiti でミューテーションをコンパイルするには、sf-gql-create / sf-gql-update / sf-gql-delete を使用します。これらは uiapi { <Object>Create(input: $input) { Record {…} } } という形式を出力します。types フィールドから入力の形式を確認できます。詳細はreferences/graphiti-cli.mdをご覧ください。 4. mutate() を呼び出します — オプションのキーは query ではなく mutation であり、 ミューテーションはキャッシュされないことに注意してください。実行時の variables の形式は操作ごとに異なります — 値は生の値({value} でラップしません。これは読み取り時の形式であり、書き込みが失敗します)で、 エンティティのキーの下にネストします。

// create — input.<Entity> holds the new field values
variables: { input: { Account: { Name: "Acme", Industry: "Technology" } } }
// update — sibling Id alongside the entity key
variables: { input: { Id: "001…", Account: { Industry: "Finance" } } }
// delete — Id only, no entity key (generic RecordDeleteInput)
variables: { input: { Id: "001…" } }

const { data, errors } = await sdk.graphql!.mutate<CreateAccountMutation, CreateAccountMutationVariables>({
  mutation: CREATE_ACCOUNT,
  variables: { input: { Account: { Name: "Acme" } } },
});
if (errors?.length) throw new Error(errors.map((e) => e.message).join("; "));

これは、spine が管理する variables の形式です。CLI の types フィールドの解釈については references/graphiti-cli.mdを、GraphQL ドキュメントのフィールド制約 (createable/updateable、ApiName の参照、@{alias} による連鎖)については references/graphql-hand-authoring.mdをご覧ください。 5. 影響を受ける読み取りを再取得します。 mutate() に refresh はありません。書き込み後にライブリストを 更新するには、先ほどの query() 呼び出しから QueryResult(例: accountsResult)を保持し、await accountsResult.refresh() を呼び出します(強制的に再取得し、 購読者に変更を通知します) — これは読み取り側のハンドルであり、mutate() の戻り値ではないことに 注意してください。**鮮度とキャッシュ**をご覧ください。

ミューテーションの構文には厳密なルールがあります。uiapi(input: { allOrNone: ... }) の内側にラップし、createable/updateable フィールドのみを使用します。Create/Update の出力は常に Record ですが、Delete には Record フィールドがありません — Id のみを選択してください。完全なテンプレート、連鎖、制約については、 references/graphql-hand-authoring.mdをご覧ください。


レコードの CRUD 以外

uiapi 名前空間は、レコードの読み書きだけに限りません。REST を使う前に、GraphQL ですでに対応しているか確認してください — 同じ sdk.graphql!.query() 呼び出しで、サブセレクションを変えて利用できます。トップレベルの uiapi フィールドは次のとおりです。

必要な機能使用するもの返される値
レコードのクエリuiapi { query { <Entity>(...) } }レコード(読み取りワークフロー)
行を取得せずに件数、合計、グループ化した集計値を取得uiapi { aggregate { <Entity>(groupBy: …) } }集計バケット
オブジェクト/フィールドのメタデータ — ラベル、データ型、createable/updateable、レコードタイプuiapi { objectInfos(apiNames: […]) }ObjectInfo[]
選択リスト値(レコードタイプごと)uiapi { objectInfos(objectInfoInputs: […]) { fields … on PicklistField { … } } }選択リスト値
関連リストのメタデータ — 親の関連リストに表示する列、並び順uiapi { relatedListByName(parentApiName, relatedListName) }RelatedListInfo

レコードの読み取りと同じルールに従います。すべての型/フィールドを事前に確認し、FLS が適用される場合は @optional を使用して、result.errors を確認してください。集計は npx graphiti sf-gql-aggregate でコンパイルできます(groupBy と aggregations を渡します)。オブジェクトのメタデータ、選択リスト、関連リストは手動で作成します — テンプレートはreferences/graphql-hand-authoring.mdをご覧ください。

関連する2つの機能(現在のユーザーのレコードと レイアウトの提供)については、 このスキルでクエリ形式を説明する前に、最新の組織スキーマで確認する必要があります — 追加対応事項として記録されており、現時点ではここでは扱いません。


更新頻度とキャッシュ

キャッシュモデルの根拠は tier-2b の docs/data/ にあります。キャッシュキーの仕組み、何がキャッシュされるか、baseUrl による共有の詳細、キャッシュされないサーフェスの意味、リアクティブハンドルに関する注意点はすべてここに記載されています(references/caching.md では、バージョンスタンプ付きのフォールバックとしてこれを再掲しています)。フォルダーがない場合でも作業できるだけの、重要なフォールバックは次のとおりです。

  • WebApp ではキャッシュがデフォルトで有効です。すべての query() が 300s でキャッシュされます。オプトイン用フラグも、ファクトリーも、/cache サブパスもありません。独自のキャッシュを実装しないでください(React Query、SWR、localStorage、手書きの Map)。mutate() はキャッシュされません。
  • ホストと API バージョンで共有されます。同じホスト と apiVersion を対象とする別の createDataSDK() からの、同じクエリと変数はキャッシュヒットとなり、ネットワークリクエストは 1 回だけ発生します。インスタンスごとのフェッチパイプラインは分離されたままです。
  • 2 種類の更新頻度制御ツールは別々のものです。混同しないでください。
    1. 呼び出しごとの cacheControl(オプションオブジェクトに指定する一度限りのポリシー):「no-cache」(キャッシュを迂回して、結果をキャッシュに書き戻す)/「only-if-cached」/{ type: "max-age", maxAge: <seconds> }。デフォルトは 300s です。読み取り関数のオプションパラメーターとして渡し、各ポリシーを同じデータレイヤーファイル内の薄い名前付きエクスポートとして公開します(refreshAccounts → 「no-cache」、offlineAccounts → 「only-if-cached」など)。「only-if-cached」でキャッシュミスが発生すると、extensions.code === "CACHE_MISS" を含む result.errors が返されます。空の状態を表示し、ネットワークにフォールバックしないでください(オフラインファーストの意味がなくなります)。
    2. リアクティブな subscribe / refresh(QueryResult のライブハンドル):subscribe(cb) は以降のスナップショットでのみ呼び出されます(破棄時には必ず unsubscribe してください)。refresh() はキャッシュを迂回して再取得し、サブスクライバーに結果を通知します。mutate() には refresh がないため、その後に使用してください。複数のサブスクライバーへの通知と独立性については、tier-2b の docs/data/ を根拠としてください。

既存コードの作業(移行)

既存コードが実際に古い API を使用している場合にのみ、この手順に進んでください。つまり、@salesforce/sdk-data をインポートしているか、呼び出し可能な sdk.graphql(query, vars) 形式を使用している場合です。新しい読み取りや書き込みでは、移行については無視し、読み取りワークフロー/書き込みワークフローを使用してください。これらには、すでに唯一正しい API が示されています。

古いコードを変換する場合は、変更前と変更後の差分(インポート、query/mutate の呼び出し、オプショナルチェーンから非 null アサーションへの変更、codegen の型の配置)とチェックリストが記載された references/migration.md を参照してください。移行先の API は、上記の読み取り/書き込みワークフローで指定されているものとまったく同じです。移行では、古い形式を置き換えるだけです。


プラットフォームのガードレール — 決して後退させないでください

これらは SDK とは独立した Salesforce GraphQL プラットフォームの動作です。違反すると、ランタイムでエラーが通知されずに失敗します。(詳細とテンプレート:references/graphql-hand-authoring.md。)

  1. HTTP 200 ≠ 成功 — 必ず result.errors を解析してください。失敗時でも Promise は解決されます。
  2. スキーマだけが信頼できる情報源です。検証し、決して推測しないでください。 使用前に、graphiti の sf-gql-discover(推奨)または bash <skill-dir>/scripts/graphql-search.sh <Entity> を使って、すべての エンティティ/フィールド/型を検証してください。大文字と小文字は区別されます。 __c/__e、エンティティのサフィックス _Record(v60+)。graphiti がプライミング済みの場合、 「not found」/空の結果/Cannot query field という回答(graphql-codegen/@graphql-eslint からのものも含み、 メッセージが schema.graphql を示している場合も同様)は、組織についての事実です。名前が誤っているか、 メタデータがデプロイされていない、またはアクセスできないことを意味し、ツールの 失敗ではありません。操作を修正するか、メタデータをデプロイし(platform-metadata-deploy スキル)、 権限を割り当ててから、更新してください(sf-gql-connect --forceRefresh/npm run graphql:schema)。スクリプトにフォールバックしたり、 その場しのぎで手書きしたり、名前を推測したりしないでください。エンティティ名やフィールド名を推測すると、 クエリ全体が実行時にエラーなく失敗します。検索しても解決しない場合は、いつまでも試行を繰り返さず、ユーザーに尋ねてください。schema.graphql とコード生成の出力 (src/api/graphql-operations-types.ts)は、読み取り専用の生成ミラーです。決して開いたり編集したりしないでください (# DO NOT EDIT マーカーがあれば必ず従ってください)。不足している型を手作業で追加すれば、コード生成や lint は通りますが、 組織へのアクセス権は得られません。実行時まで失敗を隠すだけです。スクリプトへのフォールバックが許されるのは、 CLI をまったく実行できない場合(依存関係がない/SCHEMA_PRIME_FAILED)に限られます。
  3. FLS で制限されるすべてのフィールドに、各ネストレベルで @optional を付けてください — スカラーのリーフフィールドに加え、 各親/子リレーションシップと、その内部のフィールドにも付けてください(付けないとクエリ全体が FLS エラーになります。v65+)。 Id、コネクションの構成要素(edges、node、コネクションフィールド)、pageInfo には 付けないでください。これらは FLS の対象ではなく、graphiti の出力でも装飾なしのままです。 ?./?? を使って値を受け取ってください。配置ルール:references/graphql-hand-authoring.md。
  4. ミューテーションは uiapi(input: { allOrNone: ... }) の内側に配置します。allOrNone を明示的に設定してください。 出力には子/ナビゲート参照フィールドが含まれません。出力フィールド名は文字どおり Record です(ルール 2 のエンティティサフィックス _Record とは無関係です)。Delete → Id のみ。GA v66+。
  5. ページネーションは明示的に指定してください — 必ず first: を設定してください。サーバーはエラーなく上限を 10 に制限するため、 行が欠落します。前方向のみ対応です(first/after。last/before は不可)。 upperBound(v59+)を設定すると、1 リクエストあたりの上限を引き上げ、大きなデータセットを扱えます (設定する場合、first は 200~2000 にする必要があります)。
  6. SOQL のガバナー制限が適用されます — uiapi クエリは SOQL にコンパイルされるため、同じガバナー 制限が継承されます。サブクエリは ≤10、子から親への階層は ≤5、親から子への階層は ≤1、 サブクエリあたりのレコード数は ≤2,000 です。制限を超える場合は、複数のリクエストに分割してください。
  7. フィールド値のラッパー — 生の値は .value で取得します。displayValue はサーバーで UI 向けに 書式設定された文字列です。フィールドを表示にも操作にも使う場合(通貨、日付、選択リスト)は、 クライアント側で再書式設定しないよう、value と displayValue の両方を選択してください。 表示専用フィールドでは displayValue のみで構いません。
  8. 複合フィールド — ラッパー(BillingAddress)ではなく、構成要素(BillingCity)で絞り込み/並べ替えを行ってください。
  9. サポートされている API のみ使用してください — GraphQL(uiapi)、UI API REST、Apex REST、Connect REST、 sdk.fetch 経由の Einstein LLM。使用不可:Enterprise SOQL /query、Aura 対応 Apex、Chatter (uiapi.currentUser を使用)。references/rest-and-integration.md を参照してください。

1 つの SDK 規約はこの一覧ではなく、ワークフローに記載されています(プラットフォームの動作ではありません)。 操作を書いた後は必ず npm run graphql:codegen を実行し、生成された型を使用してください (Read workflow のステップ 3)。Pre-flight checklist にも記載されています。

graphiti がこれらの大半を自動で適用します。 プライミング済みスキーマ内のオブジェクトに対して sf-gql-* でクエリをコンパイルすると、 ルール 3(@optional)、4(ミューテーションの Record 出力 エンベロープとエンティティをキーにした入力 — allOrNone は対象外。これは引き続き自分で追加します)、 5(first:/pageInfo)、7(value/displayValue のラッパー)はすでに満たされます。 そのため、query を再導出せず、そのまま貼り付けることが重要です。ルール 1 (result.errors の確認)、6(ガバナー制限)、8(複合フィールド)、9(サポート対象 API)は引き続き自分で対応してください。 また、自動化が実行されるのはオブジェクトがプライミングされている場合だけです。warnings 配列が空でない場合はプライミングされておらず、出力されるクエリは機能が制限された状態(裸のフィールド、ガードレールなし)です。 references/graphiti-cli.md を参照してください。


コマンドと構成

<skill-dir>/                            ← wherever this skill is installed
└── scripts/graphql-search.sh           ← schema lookup (ships with the skill)

<project-root>/                         ← SFDX project root; run the script from here
├── schema.graphql                      ← generated mirror; grep target (never open or edit; script reads ./schema.graphql)
└── force-app/main/default/uiBundles/<app>/   ← UI bundle dir
    ├── package.json                    ← npm scripts
    └── src/api/                        ← queries, generated types, SDK calls
コマンド実行場所目的
npx graphiti sf-gql-discover '{…}'UI バンドルディレクトリライブ組織のオブジェクト/フィールドを検出します(推奨される根拠確認方法)
npx graphiti sf-gql-<list|detail|aggregate|create|update|delete|raw> '{…}'UI バンドルディレクトリガードレールが適用されたクエリ/ミューテーションをコンパイルします(references/graphiti-cli.md)
npx graphiti sf-gql-connect '{"org":"<alias>","forceRefresh":true}'UI バンドルディレクトリデプロイ後に graphiti のスキーマキャッシュを更新します
bash <skill-dir>/scripts/graphql-search.sh <Entity>プロジェクトルート(または --schema <path> を指定。ディレクトリ階層をたどった検索はしません)スキーマ検索のフォールバック(ローカルの schema.graphql を grep します)
npm run graphql:schemaUI バンドルディレクトリschema.graphql を取得/更新します(フォールバックスクリプト用)
npm run graphql:codegenUI バンドルディレクトリ操作の型を生成します
npx eslint <file>UI バンドルディレクトリリントします(gql スキーマ違反を検出します)

事前確認チェックリスト

  • 使用するサーフェスを決定済み:WebApp のみの場合に限り sdk.graphql! を使用し、それ以外では if (!sdk.graphql) … でガードします(Surfaces)
  • SDK の契約をインストール済みの dist/*.d.ts で確認済み(説明文より型を優先します)(tier-2a)
  • SDK の動作をインストール済みの docs/data/ で確認済み(説明文よりドキュメントを優先し、ない場合はフォールバックします)(tier-2b)
  • すべてのフィールド/エンティティを確認済み — sf-gql-discover(推奨)または graphql-search.sh(フォールバック。適切なスキーマを対象にします)
  • graphiti でコンパイルした場合:warnings: [] を確認済み(空でない場合は機能が制限されたクエリのため、リリースしないでください)。query をそのまま貼り付け済み
  • FLS で制御されるフィールドとリレーションシップに @optional を指定済み(Id/edges/node/pageInfo は対象外)。利用側のコードで ?./?? を使用済み
  • result.data を読み取る前に result.errors を確認済み
  • キャッシュを検討済み:デフォルトの 300s で問題ないか、cacheControl/refresh を意図的に選択済み
  • npm run graphql:codegen を実行し、生成された型を使用済み。npx eslint に合格済み

コメント