using-ui-bundle-salesforce-data について
React UI bundle で `@salesforce/sdk-data` を使った Salesforce データアクセスを構築するスキルです。スキーマで確認した GraphQL によるレコード操作を優先し、REST の利用先を明示された対応エンドポイントに限定します。
主な機能
- 01Salesforce Data SDK の利用
- 02GraphQL スキーマに基づくフィールド確認
- 03GraphQL によるレコードの照会と変更
- 04ページネーションと項目アクセスの保護
- 05対応済み REST エンドポイントへの限定
活用シーン
- 01Salesforce UI bundle のレコードクエリ構築
- 02レコードの作成、更新、削除の追加
- 03GraphQL で不足する場合のレコードメタデータ取得
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ forcedotcom/ sf-skills --skill using-ui-bundle-salesforce-dataインストール後、Claude Code で/using-ui-bundle-salesforce-dataを入力して読み込まれたか確認します。
同梱ファイルscripts/graphql-search.sh
詳細
Salesforce データアクセス
Data SDK の要件
Salesforce データへのアクセスには、必ず Data SDK(
@salesforce/sdk-data)を使用してください。 SDK が認証、CSRF、ベース URL の解決を処理します。
import { createDataSDK, gql } from "@salesforce/sdk-data";
import type { ResponseTypeQuery } from "../graphql-operations-types";
const sdk = await createDataSDK();
// GraphQL for record queries/mutations (PREFERRED)
const response = await sdk.graphql?.<ResponseTypeQuery>(query, variables);
// REST for Connect REST, Apex REST, UI API (when GraphQL insufficient)
const res = await sdk.fetch?.("/services/apexrest/my-resource");
常にオプショナルチェーン(sdk.graphql?.()、sdk.fetch?.())を使用してください。サーフェスによっては、これらのメソッドが未定義の場合があります。
前提条件 — 開始前に確認してください
| # | 要件 | 確認方法 | 不足している場合 |
|---|---|---|---|
| 1 | @salesforce/sdk-data がインストールされている | UI バンドルディレクトリの package.json を確認する | 続行できません — ユーザーにインストールを依頼してください |
| 2 | プロジェクトルートに schema.graphql がある | ファイルが存在するか確認する | UI バンドルディレクトリから npm run graphql:schema を実行する |
| 3 | カスタムオブジェクト/項目がデプロイされている | graphql-search.sh <Entity> を実行する — 出力がなければ未デプロイです | ユーザーにメタデータのデプロイと権限セットの割り当てを依頼してください |
前提条件が満たされていない場合でも、コンポーネント、ルート、レイアウト、UI ロジックのひな型は作成できます。ただし、データには空の配列/null を使用し、クエリの場所に // TODO: add query after schema verification を記して、要件を解決して GraphQL を記述するために戻ることを計画に含めてください。スキーマのワークフローが完了するまでは、GraphQL クエリ文字列を記述しないでください。
サポート対象 API
許可されている API は以下のみです。 ここに記載されていないエンドポイントは使用できません。
| API | メソッド | エンドポイント/ユースケース |
|---|---|---|
| GraphQL | sdk.graphql | uiapi { } 名前空間を使用したすべてのレコードのクエリとミューテーション |
| UI API REST | sdk.fetch | /services/data/v{ver}/ui-api/records/{id} — GraphQL では不十分な場合のレコードメタデータ |
| Apex REST | sdk.fetch | /services/apexrest/{resource} — カスタムのサーバーサイドロジック、集計、複数ステップのトランザクション |
| Connect REST | sdk.fetch | /services/data/v{ver}/connect/file/upload/config — ファイルアップロードの設定 |
| Einstein LLM | sdk.fetch | /services/data/v{ver}/einstein/llm/prompt/generations — AI テキスト生成 |
サポート対象外:
- Enterprise REST クエリエンドポイント(SOQL を使用する
/services/data/v*/query)— プロキシレベルでブロックされています。レコードの読み取りには GraphQL を使用してください。サーバーサイドの SOQL 集計が必要な場合は Apex REST を使用してください。 - Aura 対応 Apex(
@AuraEnabled)— React UI バンドルから呼び出す手段のない LWC/Aura パターンです。 - Chatter API(
/chatter/users/me)— 代わりに GraphQL クエリでuiapi { currentUser { ... } }を使用してください。 - 上記のサポート対象テーブルに記載されていない、その他すべての Salesforce REST エンドポイント。
選択方法:GraphQL と REST
| 必要な処理 | メソッド | 例 |
|---|---|---|
| レコードのクエリ/変更 | sdk.graphql | Account、Contact、カスタムオブジェクト |
| 現在のユーザー情報 | sdk.graphql | uiapi { currentUser { Id Name { value } } } |
| UI API のレコードメタデータ | sdk.fetch | /ui-api/records/{id} |
| Connect REST | sdk.fetch | /connect/file/upload/config |
| Apex REST | sdk.fetch | /services/apexrest/auth/login |
| Einstein LLM | sdk.fetch | /einstein/llm/prompt/generations |
レコード操作には GraphQL を優先してください。 ユースケースを GraphQL で処理できない場合に限り、REST を使用してください。
GraphQL の譲れないルール
これらのルールは、Salesforce GraphQL には標準の GraphQL とは異なるプラットフォーム固有の動作があるために設けられています。違反すると、実行時にエラーが発生しても通知されないことがあります。
-
HTTP 200 は成功を意味しません — Salesforce は操作が失敗しても HTTP 200 を返します。レスポンス本文の
errors配列を必ず解析してください。 -
スキーマを唯一の信頼できる情報源とする — クエリで使用する各エンティティ名、フィールド名、型は、スキーマ検索スクリプトで確認する必要があります。推測は禁物です。Salesforce のフィールド名では大文字と小文字が区別され、リレーションシップはポリモーフィックである場合があり、カスタムオブジェクトにはサフィックス(
__c、__e)が付けられます。v60 以降で UI API に追加されたオブジェクトには、_Recordサフィックスが付く場合があります(例:FeedItemではなくFeedItem_Record)。 -
すべてのレコードフィールド(読み取りクエリ)に
@optionalを付ける — Salesforce の項目レベルセキュリティ(FLS)により、ユーザーがフィールドに 1 つでもアクセスできない場合、クエリ全体が失敗します。@optionalディレクティブ(v65 以降)を使うと、サーバーはアクセスできないフィールドを省略し、失敗を回避できます。すべてのスカラー項目、親リレーションシップ、子リレーションシップに適用してください。値を使用するコードでは、オプショナルチェーン(?.)と Null 合体演算子(??)を使用する必要があります。 -
正しいミューテーション構文を使う — ミューテーションは
uiapi(input: { allOrNone: true/false })の下に記述します。uiapi { ... }とだけ記述してはいけません。allOrNoneは必ず明示的に設定してください。出力フィールドには、子リレーションシップや参照フィールドをたどったフィールドを含められません。 -
ページネーションを明示する — すべてのクエリに
first:を必ず含めてください。省略すると、サーバーは通知せずにデフォルトで 10 件のレコードを返します。ページネーションが必要になる可能性のあるクエリには、pageInfo { hasNextPage endCursor }を含めてください。前方へのみページネーションできます(first/after)。last/beforeはサポートされていません。 -
SOQL に基づく実行制限 — 1 リクエストあたりのサブクエリは最大 10 件、子から親へのたどりは最大 5 レベル、親から子へのたどりは最大 1 レベル(孫は不可)、サブクエリあたりのレコード数は最大 2,000 件です。クエリがこれらの制限を超える場合は、複数のリクエストに分割してください。
-
要求されたフィールドのみを使う — ユーザーが明示的に求めたフィールドのみを生成してください。追加のフィールドを加えてはいけません。
-
複合フィールド — フィルタリングまたは並べ替えでは、複合フィールドのラッパー(
BillingAddress)ではなく、構成フィールド(例:BillingCity、BillingCountry)を使用してください。複合フィールドのラッパーは選択にのみ使用できます。
GraphQL のワークフロー
| 手順 | アクション | 主な出力 |
|---|---|---|
| 1 | スキーマを取得する | schema.graphql が存在する |
| 2 | エンティティを検索する | フィールド名、型、リレーションシップを確認する |
| 3 | クエリを生成する | .graphql ファイルまたはインラインの gql タグ |
| 4 | 型を生成する | graphql-operations-types.ts |
| 5 | 検証する | Lint + codegen に合格する |
手順 1:スキーマを取得する
schema.graphql ファイル(265K 行以上)が信頼できる唯一の情報源です。ファイルを直接開いたり解析したりしてはいけません。cat、less、head、tail、エディター、プログラムによるパーサーのいずれも使用しないでください。
前提条件 1~3(前提条件を参照)を確認してから、手順 2 に進んでください。
手順 2:エンティティのスキーマを検索する
ユーザーの意図を PascalCase の名前に対応付け(「accounts」→ Account)、sfdx-project フォルダー(プロジェクトルート)から検索スクリプトを実行してください。
bash scripts/graphql-search.sh Account
# Multiple entities:
bash scripts/graphql-search.sh Account Contact Opportunity
スクリプトはエンティティごとに、次の 7 つのセクションを出力します。
- 型定義 — クエリ可能なすべてのフィールドとリレーションシップ
- フィルターオプション —
where:条件で使用できるフィールド - 並べ替えオプション —
orderBy:で使用できるフィールド - 作成ミューテーションラッパー —
<Entity>CreateInput - 作成ミューテーションフィールド —
<Entity>CreateRepresentation(作成ミューテーションで受け付けられるフィールド) - 更新ミューテーションラッパー —
<Entity>UpdateInput - 更新ミューテーションフィールド —
<Entity>UpdateRepresentation(更新ミューテーションで受け付けられるフィールド)
スクリプトは最大 2 回まで実行できます。 それでもエンティティが見つからない場合はユーザーに確認してください。オブジェクトがデプロイされていない可能性があります。
エンティティの識別
候補が一致しない場合:
- カスタムオブジェクトには
__cサフィックス、プラットフォームイベントには__eを試してください _Recordサフィックスを試してください — v60+ で追加されたオブジェクトは<EntityName>_Recordを使用する場合があります- それでも解決しない場合は、ユーザーに確認してください — 推測しないでください
反復的なイントロスペクション (最大 3 サイクル)
- イントロスペクション — 未解決の各エンティティに対してスクリプトを実行します
- フィールド — 型定義から要求されたフィールド名と型を抽出します
- 参照 — 参照フィールドを識別します。ポリモーフィック (複数の型) の場合は、インラインフラグメントを使用します。新たに発見されたエンティティ型を作業リストに追加します。
- 子リレーションシップ — Connection 型を識別します。子エンティティ型を作業リストに追加します。
- 未解決のエンティティが残っている場合は繰り返します (最大 3 サイクル)
強制終了: エンティティに対してデータが返されなかった場合は、停止してください — そのエンティティはデプロイされていない可能性があります。3 サイクル後も未知のエンティティが残っている場合は、ユーザーに確認してください。未確認のエンティティやフィールドを含むクエリを生成しないでください。
ステップ 3: クエリの生成
すべてのフィールド名は、ステップ 2 のスクリプト出力から必ず検証する必要があります。
読み取りクエリテンプレート
query QueryName($after: String) {
uiapi {
query {
EntityName(
first: 10
after: $after
where: { ... }
orderBy: { ... }
) {
edges {
node {
Id
FieldName @optional { value }
# Parent relationship (non-polymorphic)
Owner @optional { Name { value } }
# Parent relationship (polymorphic — use fragments)
What @optional {
...WhatAccount
...WhatOpportunity
}
# Child relationship — max 1 level, no grandchildren
Contacts @optional(first: 10) {
edges { node { Name @optional { value } } }
}
}
}
pageInfo { hasNextPage endCursor }
}
}
}
}
fragment WhatAccount on Account {
Id
Name @optional { value }
}
fragment WhatOpportunity on Opportunity {
Id
Name @optional { value }
}
消費側のコードは、欠落したフィールドに対して防御する必要があります:
const name = node.Name?.value ?? "";
const relatedName = node.Owner?.Name?.value ?? "N/A";
フィルタリング
# Implicit AND
Account(where: { Industry: { eq: "Technology" }, AnnualRevenue: { gt: 1000000 } })
# Explicit OR
Account(where: { OR: [{ Industry: { eq: "Technology" } }, { Industry: { eq: "Finance" } }] })
# NOT
Account(where: { NOT: { Industry: { eq: "Technology" } } })
# Date literal
Opportunity(where: { CloseDate: { eq: { value: "2024-12-31" } } })
# Relative date
Opportunity(where: { CloseDate: { gte: { literal: TODAY } } })
# Relationship filter (nested objects, NOT dot notation)
Contact(where: { Account: { Name: { like: "Acme%" } } })
# Polymorphic relationship filter
Account(where: { Owner: { User: { Username: { like: "admin%" } } } })
文字列の等価比較 (eq) は大文字と小文字を区別しません。15 文字と 18 文字のレコード ID の両方が受け付けられます。
並べ替え
Account(
first: 10,
orderBy: { Name: { order: ASC }, CreatedDate: { order: DESC } }
) { ... }
並べ替えでサポートされていない項目: 複数選択ピックリスト、リッチテキスト、長テキストエリア、暗号化フィールド。確定的な並べ替えのために、タイブレーカーとして Id を追加してください。
UpperBound ページネーション (v59+)
1 ページあたり 200 件を超える場合、または合計 4,000 件を超えるレコードの場合は、upperBound を使用します。設定する場合、first は 200~2000 でなければなりません。
Account(first: 2000, after: $cursor, upperBound: 10000) {
edges { node { Id Name @optional { value } } }
pageInfo { hasNextPage endCursor }
}
セミジョインとアンチジョイン
親の Id に対して inq (セミジョイン) または ninq (アンチジョイン) を使用して、子エンティティの条件で親エンティティをフィルタリングします。唯一の条件が子の存在である場合は、Id: { ne: null } を使用します。
query SemiJoinExample {
uiapi {
query {
Account(where: {
Id: {
inq: {
Contact: { LastName: { like: "Smith%" } }
ApiName: "AccountId"
}
}
}, first: 10) {
edges { node { Id Name @optional { value } } }
}
}
}
}
アンチジョインの場合は inq を ninq に置き換えます。制限事項: サブクエリに OR を含めることはできません、サブクエリに orderBy を含めることはできません、ジョインを互いにネストすることはできません。
現在のユーザー
標準のクエリパターンの代わりに、uiapi.currentUser (引数なし) を使用します:
query CurrentUser {
uiapi { currentUser { Id Name { value } } }
}
フィールド値ラッパー
スキーマフィールドは型付きラッパーを使用します — .value 経由でアクセスします:
| ラッパー型 | 基になる型 | ラッパー型 | 基になる型 |
|---|---|---|---|
StringValue | String | BooleanValue | Boolean |
IntValue | Int | DoubleValue | Double |
CurrencyValue | Currency | PercentValue | Percent |
DateTimeValue | DateTime | DateValue | Date |
PicklistValue | Picklist | LongValue | Long |
IDValue | ID | TextAreaValue | TextArea |
EmailValue | Email | PhoneNumberValue | PhoneNumber |
UrlValue | Url |
すべてのラッパーは displayValue: String (toLabel()/format() を介してサーバー側でレンダリング) も公開します — クライアント側でフォーマットする代わりに UI 表示に使用してください。
Mutation テンプレート
API v66+ でミューテーションが GA になりました。操作は 作成、更新、削除 の 3 種類です。
# Create
mutation CreateAccount($input: AccountCreateInput!) {
uiapi(input: { allOrNone: true }) {
AccountCreate(input: $input) {
Record { Id Name { value } }
}
}
}
# Update — must include Id
mutation UpdateAccount {
uiapi(input: { allOrNone: true }) {
AccountUpdate(input: { Id: "001xx000003GYkZAAW", Account: { Name: "New Name" } }) {
Record { Id Name { value } }
}
}
}
入力制約:
- 作成:必須フィールド(
defaultedOnCreateの場合を除く)のみ。createableフィールドのみ指定でき、子リレーションは指定できません。参照フィールドはApiNameで設定します(例:AccountId)。 - 更新:
Idが必須です。updateableフィールドのみ指定でき、子リレーションは指定できません。 - 削除:
Idのみ指定します。 IdOrRef型:更新と削除の入力におけるIdフィールドはIdOrRef型を使用します。この型では、レコード ID のリテラル(例:"001xx...")またはミューテーションの連鎖参照("@{Alias}")を指定できます。作成入力の参照フィールド(例:AccountId)も、連鎖に@{Alias}を指定できます。- 生の値:カンマ、通貨記号、ロケール形式の書式設定は使用しないでください(例:
"$80,000"ではなく80000)。
出力制約:
- 作成/更新:子リレーションと、ナビゲートした参照フィールド(
ApiNameメンバーのみ許可)を除外します。出力フィールド名は常にRecordです。 - 削除:
Idのみです。
allOrNone のセマンティクス:
true(デフォルト)— すべての操作が成功するか、すべてロールバックされます。false— 各操作は個別に成功しますが、依存する操作(@{alias}を使用する操作)は引き続きまとめてロールバックされます。
ミューテーションの連鎖
先行するミューテーションの Id を参照する @{alias} を使って、関連するミューテーションを連鎖させます。親子の作成には必須です(子のネストした作成はサポートされていません)。
mutation CreateAccountAndContact {
uiapi(input: { allOrNone: true }) {
AccountCreate(input: { Account: { Name: "Acme" } }) {
Record { Id }
}
ContactCreate(input: { Contact: { LastName: "Smith", AccountId: "@{AccountCreate}" } }) {
Record { Id }
}
}
}
ルール:クエリでは A が B より前にある必要があります。@{A} は常にミューテーション A の Id を参照します。連鎖元にできるのは Create または Delete のみです(Update は不可)。
削除ミューテーション
削除では汎用の RecordDeleteInput を使用します(エンティティ固有ではありません)。出力は Id のみで、Record フィールドはありません。
mutation DeleteAccount($id: ID!) {
uiapi(input: { allOrNone: true }) {
AccountDelete(input: { Id: $id }) {
Id
}
}
}
オブジェクトのメタデータと選択リスト値
フィールドのメタデータまたは選択リスト値を取得するには、uiapi { objectInfos(...) } を使用します。apiNames または objectInfoInputs のどちらか一方を渡してください。両方を渡すことはできません。
// Object metadata
const GET_OBJECT_INFO = gql`
query GetObjectInfo($apiNames: [String!]!) {
uiapi {
objectInfos(apiNames: $apiNames) {
ApiName
label
labelPlural
fields { ApiName label dataType updateable createable }
}
}
}
`;
// Picklist values (use objectInfoInputs + inline fragment)
const GET_PICKLIST_VALUES = gql`
query GetPicklistValues($objectInfoInputs: [ObjectInfoInput!]!) {
uiapi {
objectInfos(objectInfoInputs: $objectInfoInputs) {
ApiName
fields {
ApiName
... on PicklistField {
picklistValuesByRecordTypeIDs {
recordTypeID
picklistValues { label value }
}
}
}
}
}
}
`;
ステップ 4:型を生成する(codegen)
.graphql ファイルにクエリを記述した場合も、gql を使ってインラインで記述した場合も、クエリの作成後に TypeScript の型を生成します。
# Run from UI bundle dir
npm run graphql:codegen
出力先:src/api/graphql-operations-types.ts
生成される型の命名規則:
<OperationName>Query/<OperationName>Mutation— レスポンス型<OperationName>QueryVariables/<OperationName>MutationVariables— 変数型
sdk.graphql を呼び出す際は、必ず生成された型をインポートして使用してください。
import type { GetAccountsQuery, GetAccountsQueryVariables } from "../graphql-operations-types";
const response = await sdk.graphql?.<GetAccountsQuery, GetAccountsQueryVariables>(GET_ACCOUNTS, variables);
Connection からノード型を抽出して型をすっきり記述するには、NodeOfConnection<T> を使用します。
import { type NodeOfConnection } from "@salesforce/sdk-data";
type AccountNode = NodeOfConnection<GetAccountsQuery["uiapi"]["query"]["Account"]>;
ステップ 5:検証とテスト
- Lint:UI バンドルディレクトリで
npx eslint <file>を実行します - codegen:UI バンドルディレクトリで
npm run graphql:codegenを実行します
よくあるエラーパターン
| エラーに含まれる文字列 | 解決方法 |
|---|---|
Cannot query field / ValidationError | フィールド名が誤っています — graphql-search.sh <Entity> を再実行します |
Unknown type | 型名が誤っています — スクリプトを使ってエンティティ名が PascalCase であることを確認します |
Unknown argument | 引数が誤っています — スクリプト出力の Filter/OrderBy セクションを確認します |
invalid syntax / InvalidSyntax | エラーメッセージに従って構文を修正します |
VariableTypeMismatch / UnknownType | スキーマから正しい引数の型を確認します |
invalid cross reference id | エンティティが削除されています — 有効な Id を確認します |
OperationNotSupported | オブジェクトの利用可否と API バージョンを確認します |
is not currently available in mutation results | ミューテーション出力からフィールドを削除します |
Cannot invoke JsonElement.isJsonObject() | 更新ミューテーションの Record 選択には API バージョン 64+ を使用します |
PARTIAL の場合 ミューテーションがデータとエラーの両方を返す(部分的な成功)場合:アクセスできないフィールドを報告し、ミューテーション出力に含められないことを説明して、削除を提案します。変更前にユーザーの同意を待ってください。
UI バンドルへの統合(React)
統合パターンは 2 つあります。
パターン 1 — 外部の .graphql ファイル(複雑なクエリ)
.graphql ファイルごとに操作は 1 つです。 各ファイルには、query または mutation を 1 つだけ(フラグメントを含む)記述します。1 つのファイルに複数の操作をまとめないでください。
import { createDataSDK, type NodeOfConnection } from "@salesforce/sdk-data";
import MY_QUERY from "./query/myQuery.graphql?raw"; // ?raw suffix required
import type { GetMyDataQuery, GetMyDataQueryVariables } from "../graphql-operations-types";
const sdk = await createDataSDK();
const response = await sdk.graphql?.<GetMyDataQuery, GetMyDataQueryVariables>(MY_QUERY, variables);
.graphql ファイルを作成または変更した後、npm run graphql:codegen を実行して、型を src/api/graphql-operations-types.ts に生成します。
パターン 2 — インラインの gql タグ(シンプルなクエリ)
gql を使用する必要があります — 通常のテンプレート文字列では、ESLint のスキーマ検証を回避してしまいます。
import { createDataSDK, gql } from "@salesforce/sdk-data";
import type { GetAccountsQuery } from "../graphql-operations-types";
const GET_ACCOUNTS = gql`
query GetAccounts {
uiapi {
query {
Account(first: 10) {
edges { node { Id Name @optional { value } } }
}
}
}
}
`;
const sdk = await createDataSDK();
const response = await sdk.graphql?.<GetAccountsQuery>(GET_ACCOUNTS);
エラー処理
// Strict (default) — any errors = failure
if (response?.errors?.length) {
throw new Error(response.errors.map(e => e.message).join("; "));
}
// Tolerant — log errors, use available data
if (response?.errors?.length) {
console.warn("GraphQL partial errors:", response.errors);
}
// Discriminated — fail only when no data returned
if (!response?.data && response?.errors?.length) {
throw new Error(response.errors.map(e => e.message).join("; "));
}
const accounts = response?.data?.uiapi?.query?.Account?.edges?.map(e => e.node) ?? [];
REST API のパターン
GraphQL では不十分な場合は、sdk.fetch を使用します。許可リストの全容については、サポート対象の API テーブルを参照してください。
declare const __SF_API_VERSION__: string;
const API_VERSION = typeof __SF_API_VERSION__ !== "undefined" ? __SF_API_VERSION__ : "65.0";
// Connect — file upload config
const res = await sdk.fetch?.(`/services/data/v${API_VERSION}/connect/file/upload/config`);
// Apex REST (no version in path)
const res = await sdk.fetch?.("/services/apexrest/auth/login", {
method: "POST",
body: JSON.stringify({ email, password }),
headers: { "Content-Type": "application/json" },
});
// UI API — record with metadata (prefer GraphQL for simple reads)
const res = await sdk.fetch?.(`/services/data/v${API_VERSION}/ui-api/records/${recordId}`);
// Einstein LLM
const res = await sdk.fetch?.(`/services/data/v${API_VERSION}/einstein/llm/prompt/generations`, {
method: "POST",
body: JSON.stringify({ promptTextorId: prompt }),
});
現在のユーザー:Chatter(/chatter/users/me)は使用しないでください。代わりに GraphQL を使用します。
const GET_CURRENT_USER = gql`
query CurrentUser {
uiapi { currentUser { Id Name { value } } }
}
`;
const response = await sdk.graphql?.(GET_CURRENT_USER);
ディレクトリ構成
<project-root>/ ← SFDX project root
├── schema.graphql ← grep target (lives here)
├── sfdx-project.json
├── scripts/graphql-search.sh ← schema lookup script
└── force-app/main/default/uiBundles/<app-name>/ ← UI bundle dir
├── package.json ← npm scripts
└── src/
| コマンド | 実行場所 | 理由 |
|---|---|---|
npm run graphql:schema | UI バンドルディレクトリ | UI バンドルの package.json にスクリプトがあるため |
npm run graphql:codegen | UI バンドルディレクトリ | GraphQL の型を生成するため |
npx eslint <file> | UI バンドルディレクトリ | eslint.config.js を読み込むため |
bash scripts/graphql-search.sh <Entity> | プロジェクトルート | スキーマを検索するため |
クイックリファレンス
スキーマの検索(プロジェクトルートから)
検索スクリプトを実行すると、関連するスキーマ情報をすべて 1 回で取得できます。
bash scripts/graphql-search.sh <EntityName>
| スクリプトの出力セクション | 用途 |
|---|---|
| 型定義 | フィールド名、親子関係 |
| フィルターオプション | where: 条件 |
| 並べ替えオプション | orderBy: |
| CreateRepresentation | 作成ミューテーションのフィールド一覧 |
| UpdateRepresentation | 更新ミューテーションのフィールド一覧 |
エラーのカテゴリ
| エラーに含まれる文字列 | 解決方法 |
|---|---|
Cannot query field | フィールド名が誤っています — graphql-search.sh <Entity> を実行し、型定義セクションに記載された正確な名前を使用してください |
Unknown type | 型名が誤っています — graphql-search.sh <Entity> を実行し、正しいパスカルケースのエンティティ名を確認してください |
Unknown argument | 引数名が誤っています — graphql-search.sh <Entity> を実行し、Filter または OrderBy セクションを確認してください |
invalid syntax | エラーメッセージに従って構文を修正してください |
validation error | フィールド名が誤っています — graphql-search.sh <Entity> を実行して確認してください |
VariableTypeMismatch | スキーマに従って引数の型を修正してください |
invalid cross reference id | エンティティが削除されています — 有効な Id を確認してください |
チェックリスト
- すべてのフィールド名を検索スクリプトで確認済み(ステップ 2)
- すべてのレコードフィールド(読み取り)に
@optionalを適用済み - ミューテーションで
uiapi(input: { allOrNone: ... })ラッパーを使用 - すべてのクエリで
first:を指定 - 使用側のコードでオプショナルチェーンを使用
- レスポンス処理で
errors配列を確認 - Lint が成功:
npx eslint <file>