/experience-ui-bundle-salesforce-data-access
MUST activate when a uiBundles/*/src/ project does ANY Salesforce record operation — reading, creating, updating, deleting, or caching/refreshing query results. Triggers: code importing @salesforce/platform-sdk, calls to sdk.graphql.query / sdk.graphql.mutate / sdk.fetch,
$ npx -y skills add forcedotcom/sf-skills --skill experience-ui-bundle-salesforce-data-access --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
- Slash command
/experience-ui-bundle-salesforce-data-access
Context preview
The summary Claude sees to decide when to auto-load this skill.
MUST activate when a uiBundles/*/src/ project does ANY Salesforce record operation — reading, creating, updating, deleting, or caching/refreshing query results. Triggers: code importing @salesforce/platform-sdk, calls to sdk.graphql.query / sdk.graphql.mutate / sdk.fetch,
SKILL.md
experience-ui-bundle-salesforce-data-access.SKILL.mdname: experience-ui-bundle-salesforce-data-access
description: "MUST activate when a uiBundles/*/src/ project does ANY Salesforce record operation — reading, creating, updating, deleting, or caching/refreshing query results. Triggers: code importing @salesforce/platform-sdk, calls to sdk.graphql.query / sdk.graphql.mutate / sdk.fetch, *.graphql files, stale data needing a force-refresh, or wiring up a UI bundle's data layer to read, write, or refresh Salesforce records. The default for new read/write work is the Read/Write workflow with the current @salesforce/platform-sdk API; only follow the migration path when EXISTING code already uses the old @salesforce/sdk-data callable form. Not for building app shell/UI, styling, file upload, or auth/search scaffolding — use the other ui-bundle-* skills. DO NOT TRIGGER when: OAuth setup, schema changes, Bulk/Tooling/Metadata API, or declarative automation."
metadata:
cliTools:
- tool: ["node"]
semver: ">=18.0.0"
- tool: ["npm"]
semver: ">=9.0.0"
- tool: ["npx"]
semver: ">=9.0.0"
- tool: ["sf"]
semver: ">=2.0.0"
relatedSkills:
- "platform-metadata-deploy"
version: "2.1"Salesforce Data Access (UI bundles)
All Salesforce data access in a UI bundle goes through the **`@salesforce/platform-sdk`** data SDK. The SDK handles auth, CSRF, and base-URL resolution, and — on the WebApp surface — caches every GraphQL query by default.
This file is the **workflow + guardrail spine**. Depth lives in linked docs:
- **[references/graphiti-cli.md](references/graphiti-cli.md)** — the **`graphiti` CLI** (`sf-gql-*`
commands) that compiles a small JSON spec into a schema-correct, guardrail-applied query + variables + types. The preferred way to author the GraphQL in steps below; falls back to the schema-grep script when unavailable.
- **[references/sdk-api.md](references/sdk-api.md)** — the new call API: `query`/`mutate`,
`QueryResult`, typing, error-handling stances.
- **[references/caching.md](references/caching.md)** — on-by-default cache + the **two refresh
modes** (`result.refresh`/`subscribe` vs per-call `cacheControl`).
- **[references/graphql-hand-authoring.md](references/graphql-hand-authoring.md)** — schema lookup, read /
mutation templates, every platform guardrail (`@optional`, pagination, limits, semi-join, wrappers, error table…).
- **[references/rest-and-integration.md](references/rest-and-integration.md)** — `sdk.fetch`,
the supported-API allowlist, and the reactive/lifecycle integration patterns.
- **[references/migration.md](references/migration.md)** — old `@salesforce/sdk-data` callable code
→ new namespace. The **only** place the dead API appears as usable code.
The one-paragraph mental model
`const sdk = await createDataSDK()`. Then `sdk.graphql` is a **namespace**, not a function: **`sdk.graphql!.query({...})`** for reads, **`sdk.graphql!.mutate({...})`** for writes. On WebApp, **every `query()` is cached by default** (300s). HTTP 200 never means success — always check `result.errors`. Verify every entity and field against the schema before you query it: one unverified field fails the *whole* query at runtime, and `schema.graphql` is too large to eyeball — look it up.
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 .valueTyped call params (`query<GetAccountsQuery, GetAccountsQueryVariables>`), the `CacheControl` type, and `NodeOfConnection<T>` (extracts a node type from a Connection for clean typing) all live in [references/sdk-api.md](references/sdk-api.md).
> **This changed (breaking — PR #502).** The previous callable `sdk.graphql(...)` form and the > previous package name are **dead** — the code above is the only correct form. If you encounter > the old API in existing code (or a stale `dist/` artifact), don't copy it; convert it per > [Working on existing code](#working-on-existing-code-migration). > > **`sdk.graphql!` is WebApp-only.** The non-null assertion above is correct *only* if the > bundle runs solely on WebApp. On other surfaces it can crash — decide before you write it. > See **[Surfaces — `!` vs guard](#surfaces--sdkgraphql-vs-guard)** below.
---
Surfaces — `sdk.graphql!` vs guard
`createDataSDK()` runs on multiple surfaces, and **`sdk.graphql` / `sdk.fetch` are genuinely optional** (typed `graphql?: …`). Whether you may assert them with `!` depends entirely on where the bundle runs — this is the one surface decision that turns into a *runtime crash* if you get it wrong, so make it explicitly before writing any `query`/`mutate` call:
| Surface(s) | `sdk.graphql` | Write | |---|---|---| | **WebApp only** | always present | `sdk.graphql!.query({...})` — `!` is safe; every shipped WebApp consumer uses it | | **Mosaic / OpenAI / MCPApps** (or any bundle that *might* run off-WebApp) | can be `undefined` | **guard first** (`if (!sdk.graphql) return …`), then call |
Rule of thumb: **if you cannot prove the bundle is WebApp-only, guard.** A bare `sdk.graphql!` that later ships to another surface throws `Cannot read properties of undefined` at runtime — TypeScript won't catch it because `!` silences exactly that check (same applies to `sdk.fetch!`). The portable guard snippet lives in [references/sdk-api.md](references/sdk-api.md#sdkgraphql-vs-guard).
---
Step 0 — Route the task
| The task is… | Go to | |---|---| | Read records | **[Read workflow](#read-workflow)** below | | Create / update / delete records | **[Write workflow](#write-workflow)** below | | Object/field metadata, picklist values, related-list metadat
Read more
name: experience-ui-bundle-salesforce-data-access
description: "MUST activate when a uiBundles/*/src/ project does ANY Salesforce record operation — reading, creating, updating, deleting, or caching/refreshing query results. Triggers: code importing @salesforce/platform-sdk, calls to sdk.graphql.query / sdk.graphql.mutate / sdk.fetch, *.graphql files, stale data needing a force-refresh, or wiring up a UI bundle's data layer to read, write, or refresh Salesforce records. The default for new read/write work is the Read/Write workflow with the current @salesforce/platform-sdk API; only follow the migration path when EXISTING code already uses the old @salesforce/sdk-data callable form. Not for building app shell/UI, styling, file upload, or auth/search scaffolding — use the other ui-bundle-* skills. DO NOT TRIGGER when: OAuth setup, schema changes, Bulk/Tooling/Metadata API, or declarative automation."
metadata:
cliTools:
- tool: ["node"]
semver: ">=18.0.0"
- tool: ["npm"]
semver: ">=9.0.0"
- tool: ["npx"]
semver: ">=9.0.0"
- tool: ["sf"]
semver: ">=2.0.0"
relatedSkills:
- "platform-metadata-deploy"
version: "2.1"Salesforce Data Access (UI bundles)
All Salesforce data access in a UI bundle goes through the **`@salesforce/platform-sdk`** data SDK. The SDK handles auth, CSRF, and base-URL resolution, and — on the WebApp surface — caches every GraphQL query by default.
This file is the **workflow + guardrail spine**. Depth lives in linked docs:
- **[references/graphiti-cli.md](references/graphiti-cli.md)** — the **`graphiti` CLI** (`sf-gql-*`
commands) that compiles a small JSON spec into a schema-correct, guardrail-applied query + variables + types. The preferred way to author the GraphQL in steps below; falls back to the schema-grep script when unavailable.
- **[references/sdk-api.md](references/sdk-api.md)** — the new call API: `query`/`mutate`,
`QueryResult`, typing, error-handling stances.
- **[references/caching.md](references/caching.md)** — on-by-default cache + the **two refresh
modes** (`result.refresh`/`subscribe` vs per-call `cacheControl`).
- **[references/graphql-hand-authoring.md](references/graphql-hand-authoring.md)** — schema lookup, read /
mutation templates, every platform guardrail (`@optional`, pagination, limits, semi-join, wrappers, error table…).
- **[references/rest-and-integration.md](references/rest-and-integration.md)** — `sdk.fetch`,
the supported-API allowlist, and the reactive/lifecycle integration patterns.
- **[references/migration.md](references/migration.md)** — old `@salesforce/sdk-data` callable code
→ new namespace. The **only** place the dead API appears as usable code.
The one-paragraph mental model
`const sdk = await createDataSDK()`. Then `sdk.graphql` is a **namespace**, not a function: **`sdk.graphql!.query({...})`** for reads, **`sdk.graphql!.mutate({...})`** for writes. On WebApp, **every `query()` is cached by default** (300s). HTTP 200 never means success — always check `result.errors`. Verify every entity and field against the schema before you query it: one unverified field fails the *whole* query at runtime, and `schema.graphql` is too large to eyeball — look it up.
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 .valueTyped call params (`query<GetAccountsQuery, GetAccountsQueryVariables>`), the `CacheControl` type, and `NodeOfConnection<T>` (extracts a node type from a Connection for clean typing) all live in [references/sdk-api.md](references/sdk-api.md).
> **This changed (breaking — PR #502).** The previous callable `sdk.graphql(...)` form and the > previous package name are **dead** — the code above is the only correct form. If you encounter > the old API in existing code (or a stale `dist/` artifact), don't copy it; convert it per > [Working on existing code](#working-on-existing-code-migration). > > **`sdk.graphql!` is WebApp-only.** The non-null assertion above is correct *only* if the > bundle runs solely on WebApp. On other surfaces it can crash — decide before you write it. > See **[Surfaces — `!` vs guard](#surfaces--sdkgraphql-vs-guard)** below.
---
Surfaces — `sdk.graphql!` vs guard
`createDataSDK()` runs on multiple surfaces, and **`sdk.graphql` / `sdk.fetch` are genuinely optional** (typed `graphql?: …`). Whether you may assert them with `!` depends entirely on where the bundle runs — this is the one surface decision that turns into a *runtime crash* if you get it wrong, so make it explicitly before writing any `query`/`mutate` call:
| Surface(s) | `sdk.graphql` | Write | |---|---|---| | **WebApp only** | always present | `sdk.graphql!.query({...})` — `!` is safe; every shipped WebApp consumer uses it | | **Mosaic / OpenAI / MCPApps** (or any bundle that *might* run off-WebApp) | can be `undefined` | **guard first** (`if (!sdk.graphql) return …`), then call |
Rule of thumb: **if you cannot prove the bundle is WebApp-only, guard.** A bare `sdk.graphql!` that later ships to another surface throws `Cannot read properties of undefined` at runtime — TypeScript won't catch it because `!` silences exactly that check (same applies to `sdk.fetch!`). The portable guard snippet lives in [references/sdk-api.md](references/sdk-api.md#sdkgraphql-vs-guard).
---
Step 0 — Route the task
| The task is… | Go to | |---|---| | Read records | **[Read workflow](#read-workflow)** below | | Create / update / delete records | **[Write workflow](#write-workflow)** below | | Object/field metadata, picklist values, related-list metadat
This repository provides a curated collection of Salesforce agent skills for building applications.
Repo: forcedotcom/sf-skills
Other skills on sf-skills.
- /agentforce-generate
Build, modify, optimize, debug, and deploy agents with Agentforce Agent Script. TRIGGER when: user creates, modifies, optimizes, or asks about .agent files or aiAuthoringBundle metadata; changes agent behavior, responses, or conversation logic; designs agent actions, tools,
Open skill - /agentforce-observe
Analyze production Agentforce agent behavior using session traces and Data Cloud. TRIGGER when: user queries STDM session data or Data Cloud trace records; investigates production agent failures, regressions, or performance issues; asks about session traces, conversation logs,
Open skill - /agentforce-test
Write, run, and analyze structured test suites for Agentforce agents — functional AND security. TRIGGER when: user writes or modifies test spec YAML (AiEvaluationDefinition); runs sf agent test create, run, run-eval, or results commands; asks about test coverage strategy, metric
Open skill - /automation-flow-generate
Generate Salesforce Flows using the MCP tool execute_metadata_action. Use when the user asks to create, build, or generate a flow — including Screen, Autolaunched, Record-Triggered (before/after-save), Scheduled. Also trigger for flow-like requests such as \"when a record is
Open skill - /dx-code-analyzer-configure
Set up, configure, and troubleshoot Salesforce Code Analyzer for any project. Handles installation, prerequisite checks, diagnosing broken setups, creating and editing code-analyzer.yml overrides, engine-specific settings, ignore patterns, severity overrides, and CI/CD pipeline
Open skill - /dx-code-analyzer-custom-rule-create
Create custom Code Analyzer rules for Regex (pattern matching), PMD (XPath/AST for Apex and metadata XML), and ESLint (LWC/JavaScript/TypeScript). Use when users want to enforce coding standards, ban patterns, detect hardcoded values, govern metadata, or add rules not in the
Open skill

