Building LDS GraphQL
Generate schema-validated Salesforce LDS GraphQL queries (read or mutation), either as standalone queries or wired into a Lightning Web Component via the lightning/graphql adapters. The skill encodes the schema-as-source-of-truth workflow end-to-end. Two bundled bash scripts handle the org-aware steps — scripts/fetch-lds-graphql-schema.sh for schema introspection and scripts/test-lds-graphql-query.sh for live-org validation.
When to Use
- User wants to create, modify, or integrate a Salesforce GraphQL query (standard objects, custom objects, or setup objects).
- Building or updating an LWC that consumes/mutates LDS data through GraphQL.
- Introspecting an org's schema before writing a query.
- Validating a generated query end-to-end against a connected org.
Do NOT use this skill for:
- REST-based UI API (use LDS wire adapters; see
experience-lds-best-practices-apply).
- Apex callouts or custom GraphQL endpoints — this skill is LDS-scoped.
- Data-requirements discovery — that's
experience-lds-data-requirements-generate.
Prerequisites
- A connected Salesforce org (username or alias). User must confirm it before schema fetch — never assume.
- Salesforce CLI /
sf available in the shell for the schema fetch.
- Decision on:
- Namespace —
uiapi (default: standard + custom objects) or setup (setup objects like permission sets, profiles).
- Query type —
read (default) or mutation.
- Output format —
standalone (raw GraphQL + variables) or LWC integration (full component wiring).
Core Rules
These apply to every step — they are the rules this skill enforces:
- Sequential execution — Steps 1→6 run in order. Every step is mandatory unless its triggering conditions are not met.
- Hard stop on failure — A failed step blocks subsequent steps until remediation is complete.
- Schema is the single source of truth — Every entity name, field name, field type, and relationship must come from
schema.graphql introspection. Never use common Salesforce knowledge (e.g., do not assume Owner is a User — it may be polymorphic).
- Report each step — Use the provided templates before advancing.
- Error reporting — Categorize errors; never echo raw tool output into the chat.
Workflow
The full normative workflow lives in references/generation-guide.md. Read it before starting. Read-query specifics are in references/generation-query.md; mutation specifics are in references/generation-mutation.md.
Step 1 — General query information
Collect and echo back:
Query type: [read | mutation]
Namespace: [uiapi | setup]
Output format: [standalone | LWC integration]
If any is unclear, ask once and wait.
Step 2 — Acquire the schema
- Ask for the
usernameOrAlias. If a default is inferred from context, present it and wait for explicit confirmation.
- Run
scripts/fetch-lds-graphql-schema.sh USERNAME_OR_ALIAS [OUTPUT_PATH] [API_VERSION] with the confirmed alias. The script writes the SDL to schema.graphql (or the path you pass) so the schema never enters the chat context. If a non-empty schema.graphql already exists at OUTPUT_PATH, the script exits early (set LDS_FETCH_FORCE=1 to re-fetch).
- On failure: hard stop, report category, ask user to resolve org access, then retry.
Using the schema file
The schema is 265,000+ lines. NEVER read the whole file — use targeted grep calls only.
- Object type:
^type <ObjectName> implements Record with -A 100.
- Filter:
^input <ObjectName>_Filter with -A 50.
- OrderBy:
^input <ObjectName>_OrderBy with -A 30.
- Mutation input:
^input <ObjectName>(Create|Update)Input with -A 50.
Search budget: max 4–5 grep calls per entity. Plan before executing.
Step 3 — Entity identification
- Entity names are PascalCase.
- If names aren't given, extract candidates from
^type <Name> implements Record matches.
- If any entity is still unresolved, ask the user and wait.
- Report:
Identified entities:
- EntityName (Field1, Field2, ...)
Unknown entities:
- <textual name>
Step 3 status: SUCCESS | FAILED
- If
Unknown entities is non-empty → status FAILED → ask for clarification → restart Step 3.
Step 4 — Iterative entity introspection
Iteration limit: 3 cycles (primary entity → references → child relationships). Hard-stop after 3.
Per cycle:
- Remove already-introspected entities from the list.
- Grep for the remaining entities' fields using the schema patterns above.
- Extract standard field types.
- Identify reference fields (
Owner: User). Fields with the same name on different entities may have different types — check each entity independently. If a field resolves to multiple entity types, mark it polymorphic and plan to use inline fragments (... on TypeA, ... on TypeB).
- Identify child relationships (Connection types, e.g.,
Contacts: ContactConnection). Add new entities to the unknown list.
- If unknown list not empty and iterations < 3, loop.
- Report:
[PASS|FAIL] EntityName
- Standard fields: FieldName (type), ...
- Reference fields: FieldName → TargetType, ...
- Polymorphic fields: FieldName → [TypeA, TypeB], ...
- Child relationships: RelationshipName → ChildType, ...
- Unknown fields: FieldName, ...
Introspection cycles used: N/3
Step 4 status: SUCCESS | FAILED
- If any entity is
[FAIL] → global FAILED → remediation → resume from cycle start.
Step 5 — Read query generation (only if query type is read)
Author the read query per references/generation-query.md, feeding in the introspection data, entity list, field types, output format, and usernameOrAlias.
Apply the rules in references/generation-query.md — covers:
- Query root / namespace selection (
uiapi.query vs setup.query).
- Field selection discipline (ask only for fields actually needed — every field is a billable scan).
- Filter operators (
eq, ne, in, nin, gt, gte, lt, lte, like, contains).
- OrderBy (per-field direction).
- Pagination (
first, after, last, before; edges.node, pageInfo).
- Polymorphic inline fragments.
- Aliasing and variable placeholders.
- Standalone vs LWC output (wire adapter from
lightning/graphql, gql tagged template, refreshGraphQL for imperative refresh).
If the tool returns an error, categorize it and ask the user how to proceed.
Step 6 — Mutation query generation (only if query type is mutation)
Author the mutation per references/generation-mutation.md — covers:
create, update, delete operation shape.
- Input types (
<Entity>CreateInput, <Entity>UpdateInput) discovered via the input grep pattern.
- Required vs optional fields (from schema's
! annotation).
- Reference-field updates using
Id only.
- Return selection — what to read back after the mutation to drive cache consistency.
- Error handling (
record.errors[]).
- LWC integration: imperative mutation via
graphqlMutate from lightning/graphql.
Step 7 — Test the query
Run scripts/test-lds-graphql-query.sh USERNAME_OR_ALIAS 'QUERY' '<VARIABLES_JSON>' against the confirmed usernameOrAlias and present the response shape/sample to the user. Errors are categorized, not echoed verbatim.
Cross-References
- Bundled scripts:
scripts/fetch-lds-graphql-schema.sh — schema acquisition via a GraphQL introspection query against the org's /services/data/vX/graphql endpoint (LDS exposes no /graphql/sdl route); call once per org/session before query authoring.
scripts/test-lds-graphql-query.sh — org-backed validation of the generated query against /services/data/vX/graphql.
- Related skills:
experience-lds-best-practices-apply — general LDS principles, cache semantics, and wire-vs-imperative choice.
experience-lds-data-requirements-generate — pre-work that decides what to query before this skill decides how.
experience-lwc-generate — host the generated wire adapter cleanly.
Examples
Standalone read — minimal
query Accounts($limit: Int = 10) {
uiapi {
query {
Account(first: $limit) {
edges {
node {
Id
Name { value }
}
}
}
}
}
}
LWC integration — read with wire
import { LightningElement, wire } from 'lwc';
import { gql, graphql } from 'lightning/graphql';
export default class AccountList extends LightningElement {
@wire(graphql, {
query: gql`
query Accounts($limit: Int = 10) {
uiapi {
query {
Account(first: $limit) {
edges { node { Id Name { value } } }
}
}
}
}
`,
variables: '$variables'
})
accounts;
variables = { limit: 10 };
get records() {
return this.accounts?.data?.uiapi?.query?.Account?.edges ?? [];
}
}
Verification
Step 3 status: SUCCESS before Step 4; Step 4 status: SUCCESS before Steps 5/6.
- Every field in the generated query appears in the introspection report (no hallucinated fields).
- Polymorphic fields use inline fragments; non-polymorphic fields do not.
- For mutations, every required input field (
! in schema) is present.
scripts/test-lds-graphql-query.sh returns without errors; or, on error, a categorized remediation is presented.
- If output format is
LWC integration, the component imports from lightning/graphql, uses gql tagged template, and exposes data via a getter (not directly in HTML).
1---
2name: experience-lds-graphql-generate
3description: "Use ALWAYS when a prompt mentions GraphQL, lightning/uiGraphQLApi, @wire(graphql, ...), or gql template tags in an LWC context — even if the surface ask is \"build an LWC\". Owns the ENTIRE flow: introspect the org's LDS GraphQL schema, identify entities/fields, construct the schema-validated gql query or mutation, wire it into the LWC via lightning/uiGraphQLApi, verify against a connected org. REQUIRED whenever a prompt asks to render, list, or edit Salesforce records (Account, Contact, Case, custom objects) via GraphQL — the .html and .js scaffolding IS in scope. DO NOT delegate to experience-lwc-generate: that skill has NO GraphQL schema introspection, NO create_lds_graphql_read_query binding, and defaults to getRecord/getRelatedListRecords wire adapters which will not satisfy a GraphQL prompt. DO NOT TRIGGER only if the prompt forbids GraphQL, chooses UIAPI/Apex (use experience-lds-best-practices-apply), or asks for data requirements (use experience-lds-data-requirements-generate)."
4metadata:
5 relatedSkills:
6 - "experience-lds-best-practices-apply"
7 - "experience-lds-data-requirements-generate"
8 - "experience-lwc-generate"
9 version: "1.0"
10 domains: ["Experience", "Platform"]
11 cliTools:
12 - tool: ["python3"]
13 semver: ">=3.8"
14 - tool: ["sf"]
15 semver: ">=2.0.0"
16---
17
18<!-- adk-managed-skill -->
19
20# Building LDS GraphQL
21
22Generate schema-validated Salesforce LDS GraphQL queries (read or mutation), either as standalone queries or wired into a Lightning Web Component via the `lightning/graphql` adapters. The skill encodes the schema-as-source-of-truth workflow end-to-end. Two bundled bash scripts handle the org-aware steps — `scripts/fetch-lds-graphql-schema.sh` for schema introspection and `scripts/test-lds-graphql-query.sh` for live-org validation.
23
24## When to Use
25
26- User wants to create, modify, or integrate a Salesforce GraphQL query (standard objects, custom objects, or setup objects).
27- Building or updating an LWC that consumes/mutates LDS data through GraphQL.
28- Introspecting an org's schema before writing a query.
29- Validating a generated query end-to-end against a connected org.
30
31Do NOT use this skill for:
32- REST-based UI API (use LDS wire adapters; see `experience-lds-best-practices-apply`).
33- Apex callouts or custom GraphQL endpoints — this skill is LDS-scoped.
34- Data-requirements discovery — that's `experience-lds-data-requirements-generate`.
35
36## Prerequisites
37
38- A connected Salesforce org (username or alias). User must confirm it before schema fetch — never assume.
39- Salesforce CLI / `sf` available in the shell for the schema fetch.
40- Decision on:
41 - **Namespace** — `uiapi` (default: standard + custom objects) or `setup` (setup objects like permission sets, profiles).
42 - **Query type** — `read` (default) or `mutation`.
43 - **Output format** — `standalone` (raw GraphQL + variables) or `LWC integration` (full component wiring).
44
45## Core Rules
46
47These apply to every step — they are the rules this skill enforces:
48
491. **Sequential execution** — Steps 1→6 run in order. Every step is mandatory unless its triggering conditions are not met.
502. **Hard stop on failure** — A failed step blocks subsequent steps until remediation is complete.
513. **Schema is the single source of truth** — Every entity name, field name, field type, and relationship must come from `schema.graphql` introspection. Never use common Salesforce knowledge (e.g., do not assume `Owner` is a `User` — it may be polymorphic).
524. **Report each step** — Use the provided templates before advancing.
535. **Error reporting** — Categorize errors; never echo raw tool output into the chat.
54
55## Workflow
56
57The full normative workflow lives in [references/generation-guide.md](references/generation-guide.md). Read it before starting. Read-query specifics are in [references/generation-query.md](references/generation-query.md); mutation specifics are in [references/generation-mutation.md](references/generation-mutation.md).
58
59### Step 1 — General query information
60
61Collect and echo back:
62
63```text
64Query type: [read | mutation]
65Namespace: [uiapi | setup]
66Output format: [standalone | LWC integration]
67```
68
69If any is unclear, ask once and wait.
70
71### Step 2 — Acquire the schema
72
731. Ask for the `usernameOrAlias`. If a default is inferred from context, present it and wait for explicit confirmation.
742. Run `scripts/fetch-lds-graphql-schema.sh USERNAME_OR_ALIAS [OUTPUT_PATH] [API_VERSION]` with the confirmed alias. The script writes the SDL to `schema.graphql` (or the path you pass) so the schema never enters the chat context. If a non-empty `schema.graphql` already exists at `OUTPUT_PATH`, the script exits early (set `LDS_FETCH_FORCE=1` to re-fetch).
753. On failure: **hard stop**, report category, ask user to resolve org access, then retry.
76
77#### Using the schema file
78
79The schema is 265,000+ lines. **NEVER** read the whole file — use targeted `grep` calls only.
80
81- Object type: `^type <ObjectName> implements Record` with `-A 100`.
82- Filter: `^input <ObjectName>_Filter` with `-A 50`.
83- OrderBy: `^input <ObjectName>_OrderBy` with `-A 30`.
84- Mutation input: `^input <ObjectName>(Create|Update)Input` with `-A 50`.
85
86**Search budget**: max 4–5 grep calls per entity. Plan before executing.
87
88### Step 3 — Entity identification
89
901. Entity names are PascalCase.
912. If names aren't given, extract candidates from `^type <Name> implements Record` matches.
923. If any entity is still unresolved, ask the user and wait.
934. Report:
94 ```text
95 Identified entities:
96 - EntityName (Field1, Field2, ...)
97 Unknown entities:
98 - <textual name>
99 Step 3 status: SUCCESS | FAILED
100 ```
1015. If `Unknown entities` is non-empty → status `FAILED` → ask for clarification → restart Step 3.
102
103### Step 4 — Iterative entity introspection
104
105Iteration limit: **3 cycles** (primary entity → references → child relationships). Hard-stop after 3.
106
107Per cycle:
108
1091. Remove already-introspected entities from the list.
1102. Grep for the remaining entities' fields using the schema patterns above.
1113. Extract standard field types.
1124. Identify **reference fields** (`Owner: User`). Fields with the same name on different entities may have different types — check each entity independently. If a field resolves to multiple entity types, mark it **polymorphic** and plan to use inline fragments (`... on TypeA`, `... on TypeB`).
1135. Identify **child relationships** (Connection types, e.g., `Contacts: ContactConnection`). Add new entities to the unknown list.
1146. If unknown list not empty and iterations < 3, loop.
1157. Report:
116 ```text
117 [PASS|FAIL] EntityName
118 - Standard fields: FieldName (type), ...
119 - Reference fields: FieldName → TargetType, ...
120 - Polymorphic fields: FieldName → [TypeA, TypeB], ...
121 - Child relationships: RelationshipName → ChildType, ...
122 - Unknown fields: FieldName, ...
123 Introspection cycles used: N/3
124 Step 4 status: SUCCESS | FAILED
125 ```
1268. If any entity is `[FAIL]` → global `FAILED` → remediation → resume from cycle start.
127
128### Step 5 — Read query generation (only if query type is `read`)
129
130Author the read query per [references/generation-query.md](references/generation-query.md), feeding in the introspection data, entity list, field types, output format, and `usernameOrAlias`.
131
132Apply the rules in [references/generation-query.md](references/generation-query.md) — covers:
133- Query root / namespace selection (`uiapi.query` vs `setup.query`).
134- Field selection discipline (ask only for fields actually needed — every field is a billable scan).
135- Filter operators (`eq`, `ne`, `in`, `nin`, `gt`, `gte`, `lt`, `lte`, `like`, `contains`).
136- OrderBy (per-field direction).
137- Pagination (`first`, `after`, `last`, `before`; `edges.node`, `pageInfo`).
138- Polymorphic inline fragments.
139- Aliasing and variable placeholders.
140- Standalone vs LWC output (wire adapter from `lightning/graphql`, `gql` tagged template, `refreshGraphQL` for imperative refresh).
141
142If the tool returns an error, categorize it and ask the user how to proceed.
143
144### Step 6 — Mutation query generation (only if query type is `mutation`)
145
146Author the mutation per [references/generation-mutation.md](references/generation-mutation.md) — covers:
147- `create`, `update`, `delete` operation shape.
148- Input types (`<Entity>CreateInput`, `<Entity>UpdateInput`) discovered via the `input` grep pattern.
149- Required vs optional fields (from schema's `!` annotation).
150- Reference-field updates using `Id` only.
151- Return selection — what to read back after the mutation to drive cache consistency.
152- Error handling (`record.errors[]`).
153- LWC integration: imperative mutation via `graphqlMutate` from `lightning/graphql`.
154
155### Step 7 — Test the query
156
157Run `scripts/test-lds-graphql-query.sh USERNAME_OR_ALIAS 'QUERY' '<VARIABLES_JSON>'` against the confirmed `usernameOrAlias` and present the response shape/sample to the user. Errors are categorized, not echoed verbatim.
158
159## Cross-References
160
161- Bundled scripts:
162 - `scripts/fetch-lds-graphql-schema.sh` — schema acquisition via a GraphQL introspection query against the org's `/services/data/vX/graphql` endpoint (LDS exposes no `/graphql/sdl` route); call once per org/session before query authoring.
163 - `scripts/test-lds-graphql-query.sh` — org-backed validation of the generated query against `/services/data/vX/graphql`.
164- Related skills:
165 - `experience-lds-best-practices-apply` — general LDS principles, cache semantics, and wire-vs-imperative choice.
166 - `experience-lds-data-requirements-generate` — pre-work that decides *what* to query before this skill decides *how*.
167 - `experience-lwc-generate` — host the generated wire adapter cleanly.
168
169## Examples
170
171**Standalone read — minimal**
172
173```graphql
174query Accounts($limit: Int = 10) {
175 uiapi {
176 query {
177 Account(first: $limit) {
178 edges {
179 node {
180 Id
181 Name { value }
182 }
183 }
184 }
185 }
186 }
187}
188```
189
190**LWC integration — read with wire**
191
192```javascript
193import { LightningElement, wire } from 'lwc';
194import { gql, graphql } from 'lightning/graphql';
195
196export default class AccountList extends LightningElement {
197 @wire(graphql, {
198 query: gql`
199 query Accounts($limit: Int = 10) {
200 uiapi {
201 query {
202 Account(first: $limit) {
203 edges { node { Id Name { value } } }
204 }
205 }
206 }
207 }
208 `,
209 variables: '$variables'
210 })
211 accounts;
212
213 variables = { limit: 10 };
214
215 get records() {
216 return this.accounts?.data?.uiapi?.query?.Account?.edges ?? [];
217 }
218}
219```
220
221## Verification
222
223- `Step 3 status: SUCCESS` before Step 4; `Step 4 status: SUCCESS` before Steps 5/6.
224- Every field in the generated query appears in the introspection report (no hallucinated fields).
225- Polymorphic fields use inline fragments; non-polymorphic fields do not.
226- For mutations, every required input field (`!` in schema) is present.
227- `scripts/test-lds-graphql-query.sh` returns without errors; or, on error, a categorized remediation is presented.
228- If output format is `LWC integration`, the component imports from `lightning/graphql`, uses `gql` tagged template, and exposes data via a getter (not directly in HTML).