--- name: accessing-webapp-data description: Salesforce data access patterns. Use when adding or modifying any code that fetches data from Salesforce (records, Chatter, Connect API, etc.). paths: - "**/*.ts" - "**/*.tsx" - "**/*.graphql" --- # Salesforce Data Access Guidance for accessing Salesforce data from web apps. **All Salesforce data fetches MUST use the Data SDK** (`@salesforce/sdk-data`). The SDK provides authentication, CSRF handling, and correct base URL resolution — direct `fetch` or `axios` calls bypass these and are not allowed. ## Mandatory: Use the Data SDK > **Every Salesforce data fetch must go through the Data SDK.** Obtain it via `createDataSDK()`, then use `sdk.graphql?.()` or `sdk.fetch?.()`. Never call `fetch()` or `axios` directly for Salesforce endpoints. ## Optional Chaining and Graceful Handling **Always use optional chaining** when calling `sdk.graphql` or `sdk.fetch` — these methods may be undefined in some surfaces (e.g., Salesforce ACC, MCP Apps). Handle the case where they are not available gracefully: ```typescript const sdk = await createDataSDK(); // ✅ Use optional chaining const response = await sdk.graphql?.(query); // ✅ Check before using fetch if (!sdk.fetch) { throw new Error("Data SDK fetch is not available in this context"); } const res = await sdk.fetch(url); ``` For GraphQL, if `sdk.graphql` is undefined, the call returns `undefined` — handle that in your logic (e.g., throw a clear error or return a fallback). For `sdk.fetch`, check availability before calling when the operation is required. ## Preference: GraphQL First **GraphQL is the preferred method** for querying and mutating Salesforce records. Use it when: - Querying records (Account, Contact, Opportunity, custom objects) - Creating, updating, or deleting records (when GraphQL supports the operation) - Fetching related data, filters, sorting, pagination **Use `sdk.fetch` only when GraphQL is not sufficient.** For REST API usage, invoke the `fetching-rest-api` skill, which documents: - Chatter API (e.g., `/services/data/v65.0/chatter/users/me`) - Connect REST API (e.g., `/services/data/v65.0/connect/file/upload/config`) - Apex REST (e.g., `/services/apexrest/auth/login`) - UI API REST (e.g., `/services/data/v65.0/ui-api/records/{recordId}`) - Einstein LLM Gateway --- ## Getting the SDK ```typescript import { createDataSDK } from "@salesforce/sdk-data"; const sdk = await createDataSDK(); ``` --- ## Example 1: GraphQL (Preferred) For record queries and mutations, use GraphQL via the Data SDK. Invoke the `using-graphql` skill for the full workflow (schema exploration, query authoring, codegen, lint validate). ```typescript 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 { value } } } } } } } `; export async function getAccounts() { const sdk = await createDataSDK(); const response = await sdk.graphql?.(GET_ACCOUNTS); if (response?.errors?.length) { throw new Error(response.errors.map((e) => e.message).join("; ")); } return response?.data?.uiapi?.query?.Account?.edges?.map((e) => e?.node) ?? []; } ``` --- ## Example 2: Fetch (When GraphQL Is Not Sufficient) For REST endpoints that have no GraphQL equivalent, use `sdk.fetch`. **Invoke the `fetching-rest-api` skill** for full documentation of Chatter, Connect REST, Apex REST, UI API REST, and Einstein LLM endpoints. ```typescript import { createDataSDK } from "@salesforce/sdk-data"; declare const __SF_API_VERSION__: string; const API_VERSION = typeof __SF_API_VERSION__ !== "undefined" ? __SF_API_VERSION__ : "65.0"; export async function getCurrentUser() { const sdk = await createDataSDK(); const response = await sdk.fetch?.(`/services/data/v${API_VERSION}/chatter/users/me`); if (!response?.ok) throw new Error(`HTTP ${response?.status}`); const data = await response.json(); return { id: data.id, name: data.name }; } ``` --- ## Anti-Patterns (Forbidden) ### Direct fetch to Salesforce ```typescript // ❌ FORBIDDEN — bypasses Data SDK auth and CSRF const res = await fetch("/services/data/v65.0/chatter/users/me"); ``` ### Direct axios to Salesforce ```typescript // ❌ FORBIDDEN — bypasses Data SDK const res = await axios.get("/services/data/v65.0/chatter/users/me"); ``` ### Correct approach ```typescript // ✅ CORRECT — use Data SDK const sdk = await createDataSDK(); const res = await sdk.fetch?.("/services/data/v65.0/chatter/users/me"); ``` --- ## Clarifying Vague Data Requests When a user asks about data and the request is vague, **clarify before implementing**. Ask which of the following they want: - **Application code** — Add or modify code in a specific web app so the app performs the data interaction at runtime (e.g., GraphQL query in the React app) - **Local SF CLI** — Run Salesforce CLI commands locally (e.g., `sf data query`, `sf data import tree`) to interact with the org from the terminal - **Local example data** — Update or add local fixture/example data files (e.g., JSON in `data/`) for development or testing - **Other** — Data export, report generation, setup script, etc. Do not assume. A request like "fetch accounts" could mean: (1) add a GraphQL query to the app, (2) run `sf data query` in the terminal, or (3) update sample data files. Confirm the intent before proceeding. --- ## Decision Flow 1. **Need to query or mutate Salesforce records?** → Use GraphQL via the Data SDK. Invoke the `using-graphql` skill. 2. **Need Chatter, Connect REST, Apex REST, UI API REST, or Einstein LLM?** → Use `sdk.fetch`. Invoke the `fetching-rest-api` skill. 3. **Never** use `fetch`, `axios`, or similar directly for Salesforce API calls. --- ## Reference - GraphQL workflow: invoke the `using-graphql` skill (`.a4drules/skills/using-graphql/`) - REST API via fetch: invoke the `fetching-rest-api` skill (`.a4drules/skills/fetching-rest-api/`) - Data SDK package: `@salesforce/sdk-data` (`createDataSDK`, `gql`, `NodeOfConnection`) - `createRecord` for UI API record creation: `@salesforce/webapp-experimental/api` (uses Data SDK internally)