feat: consolidate webapp data skills into using-webapp-salesforce-data @W-21338965@ (#81)

feat: using-webapp-salesforce-data skill and skill validation updates

Consolidate webapp data access guidance into using-webapp-salesforce-data.
Add js-yaml-based frontmatter parsing and plain-scalar description checks in
validate-skills. Update dependent skills, samples, and package metadata.

Made-with: Cursor
This commit is contained in:
k-j-kim 2026-03-23 15:25:33 -07:00 committed by GitHub
parent 74ba11f45f
commit 9190b5c97a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
32 changed files with 528 additions and 2509 deletions

View File

@ -1,178 +0,0 @@
---
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?.<GetAccountsQuery>(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)

View File

@ -1,6 +1,6 @@
---
name: building-webapp-data-visualization
description: Adds data visualization components (charts, stat cards, KPI metrics) to React pages using Recharts. Use when the user asks to add a chart, graph, donut chart, pie chart, bar chart, stat card, KPI metric, dashboard visualization, or analytics component to the web application.
description: "Adds data visualization components (charts, stat cards, KPI metrics) to React pages using Recharts. Use when the user asks to add a chart, graph, donut chart, pie chart, bar chart, stat card, KPI metric, dashboard visualization, or analytics component to the web application."
---
# Data Visualization

View File

@ -1,6 +1,6 @@
---
name: building-webapp-react-components
description: Use when editing any React code in the web application — creating or modifying components, pages, layout, headers, footers, or any TSX/JSX files. Follow this skill for add component, add page, header/footer, and general React UI implementation patterns (shadcn UI and Tailwind CSS).
description: "Use when editing any React code in the web application — creating or modifying components, pages, layout, headers, footers, or any TSX/JSX files. Follow this skill for add component, add page, header/footer, and general React UI implementation patterns (shadcn UI and Tailwind CSS)."
---
# React Web App (Components, Pages, Layout)

View File

@ -1,6 +1,6 @@
---
name: configuring-webapp-csp-trusted-sites
description: Creates Salesforce CSP Trusted Site metadata when adding external domains. Use when the user adds an external API, CDN, image host, font provider, map tile server, or any third-party URL that the web application needs to load resources from — or when a browser console shows a CSP violation error.
description: "Creates Salesforce CSP Trusted Site metadata when adding external domains. Use when the user adds an external API, CDN, image host, font provider, map tile server, or any third-party URL that the web application needs to load resources from — or when a browser console shows a CSP violation error."
---
# CSP Trusted Sites

View File

@ -1,6 +1,6 @@
---
name: configuring-webapp-metadata
description: Use this skill when configuring web application metadata structure, webapplication.json, or bundle organization. Covers WebApplication bundle layout, meta XML, build output directory, and webapplication.json settings.
description: "Use this skill when configuring web application metadata structure, webapplication.json, or bundle organization. Covers WebApplication bundle layout, meta XML, build output directory, and webapplication.json settings."
---
# WebApplication Requirements
@ -53,7 +53,7 @@ Applies to:
Reject:
- backslashes
- leading `\`
- leading `/` or `\`
- `..` segments
- null or control characters
- globs: `*`, `?`, `**`

View File

@ -1,6 +1,6 @@
---
name: creating-webapp
description: Use this skill when creating or setting up a new SFDX React web application. Covers first steps, npm install, skills-first protocol, deployment order, and core web app rules.
description: "Use this skill when creating or setting up a new SFDX React web application. Covers first steps, npm install, skills-first protocol, deployment order, and core web app rules."
paths:
- "**/webapplications/**/*"
---
@ -111,7 +111,7 @@ Apps run behind dynamic base paths. Router navigation (`<Link to>`, `navigate()`
## Module Restrictions
React apps must NOT import Salesforce platform modules like `lightning/*` or `@wire` (LWC-only). For data access, invoke the **accessing-data** skill.
React apps must NOT import Salesforce platform modules like `lightning/*` or `@wire` (LWC-only). For data access, invoke the **using-salesforce-data** skill.
# Frontend Aesthetics
@ -138,4 +138,3 @@ Only stop when:
- All checklist items are completed and quality gates pass, or
- A blocking error cannot be resolved after reasonable remediation, or
- The user explicitly asks to pause.

View File

@ -1,8 +1,6 @@
---
name: deploying-webapp-to-salesforce
description: Enforces the correct order for deploying metadata, assigning permission sets, and fetching GraphQL schema. Use for ANY deployment to a Salesforce org — webapps, LWC, Aura, Apex, metadata, schema fetch, or org sync. Codifies setup-cli.mjs.
paths:
- "**/*"
description: "Enforces the correct order for deploying metadata, assigning permission sets, and fetching GraphQL schema. Use for ANY deployment to a Salesforce org — webapps, LWC, Aura, Apex, metadata, schema fetch, or org sync. Codifies setup-cli.mjs."
---
# Deploying to Salesforce
@ -225,5 +223,4 @@ The project includes `scripts/setup-cli.mjs` which runs this sequence in batch.
## Related Skills
- **exploring-graphql-schema** — Schema exploration (grep-only) after schema exists
- **using-graphql** — Full GraphQL workflow (explore, query, codegen, lint)
- **using-salesforce-data** — Full data access workflow (GraphQL queries/mutations, REST APIs, schema exploration, webapp integration)

View File

@ -1,149 +0,0 @@
---
name: exploring-webapp-graphql-schema
description: Explore the Salesforce GraphQL schema via grep-only lookups. Use before generating any GraphQL query — schema exploration must complete first.
paths:
- "**/*.ts"
- "**/*.tsx"
- "**/*.graphql"
---
# Salesforce GraphQL Schema Exploration
Guidance for AI agents working with the Salesforce GraphQL API schema. **GREP ONLY** — the schema file is very large (~265,000+ lines). All lookups MUST use grep; do NOT open, read, stream, or parse the file.
## Deployment Prerequisites
The schema reflects the **current org state**. Custom objects and fields appear only after metadata is deployed.
- **Before** running `npm run graphql:schema`: Deploy all metadata (objects, permission sets, layouts) and assign the permission set to the target user. Invoke the `deploying-to-salesforce` skill for the full sequence.
- **After** any metadata deployment: Re-run `npm run graphql:schema` and `npm run graphql:codegen` so types and queries stay in sync.
## Schema File Location
**Location:** `schema.graphql` at the **SFDX project root** (NOT inside the webapp dir). All grep commands **must be run from the project root** where `schema.graphql` lives.
> ⚠️ **Important (Access Policy - GREP ONLY)**: Do NOT open, view, stream, paginate, or parse the schema with any tool other than grep. All lookups MUST be done via grep using anchored patterns with minimal context as defined below.
If the file is not present, generate it by running (from the **webapp dir**, not the project root):
```bash
# Run from webapp dir (force-app/main/default/webapplications/<app-name>/)
npm run graphql:schema
```
**BEFORE generating any GraphQL query, you MUST:**
1. **Check if schema exists**: Look for `schema.graphql` in the **SFDX project root**
2. **If schema is missing**:
- `cd` to the **webapp dir** and run `npm run graphql:schema` to download it
- Wait for the command to complete successfully
- Then proceed with grep-only lookups as defined below.
3. **If schema exists**: Proceed with targeted searches as described below
> ⚠️ **DO NOT** generate GraphQL queries without first having access to the schema. Standard field assumptions may not match the target org's configuration.
## Schema Structure Overview
Main entry points: `Query { uiapi }` for reads; `Mutation { uiapi(input: ...) }` for creates/updates/deletes. Record queries use `uiapi.query.<ObjectName>`.
## Allowed Lookups (grep-only)
Use ONLY these grep commands to locate specific definitions in schema.graphql. Do not use editors (VS Code/vim/nano), cat/less/more/head/tail, or programmatic parsers (node/python/awk/sed/jq).
- Always include:
- `-n` (line numbers) and `-E` (extended regex)
- Anchors (`^`) and word boundaries (`\b`)
- Minimal context with `-A N` (prefer the smallest N that surfaces the needed lines)
### 1. Find Available Fields for a Record Type
Search for `type <ObjectName> implements Record` to find all queryable fields:
```bash
# Example: Find Account fields (anchored, minimal context)
grep -nE '^type[[:space:]]+Account[[:space:]]+implements[[:space:]]+Record\b' ./schema.graphql -A 60
```
### 2. Find Filter Options for a Record Type
Search for `input <ObjectName>_Filter` to find filterable fields and operators:
```bash
# Example: Find Account filter options (anchored)
grep -nE '^input[[:space:]]+Account_Filter\b' ./schema.graphql -A 40
```
### 3. Find OrderBy Options
Search for `input <ObjectName>_OrderBy` for sorting options:
```bash
# Example: Find Account ordering options (anchored)
grep -nE '^input[[:space:]]+Account_OrderBy\b' ./schema.graphql -A 30
```
### 4. Find Mutation Operations
Search for operations in `UIAPIMutations`:
```bash
# Example: Find Account mutations (extended regex)
grep -nE 'Account.*(Create|Update|Delete)' ./schema.graphql
```
### 5. Find Input Types for Mutations
Search for `input <ObjectName>CreateInput` or `input <ObjectName>UpdateInput`:
```bash
# Example: Find Account create input (anchored)
grep -nE '^input[[:space:]]+AccountCreateInput\b' ./schema.graphql -A 30
```
## Agent Workflow for Building Queries (grep-only)
**Pre-requisites (MANDATORY):**
- [ ] Verified `schema.graphql` exists in the **SFDX project root**
- [ ] If missing, ran `npm run graphql:schema` from the **webapp dir** and waited for completion
- [ ] Confirmed connection to correct Salesforce org (if downloading fresh schema)
**Workflow Steps:**
1. **Identify the target object** (e.g., Account, Contact, Opportunity)
2. **Run the "Find Available Fields" grep command** for your object (copy only the field names visible in the grep output; do not open the file)
3. **Run the "Find Filter Options" grep command** (`<Object>_Filter`) to understand filtering options
4. **Run the "Find OrderBy Options" grep command** (`<Object>_OrderBy`) for sorting capabilities
5. **Build the query** following the patterns in the `generating-graphql-read-query` or `generating-graphql-mutation-query` skill using only values returned by grep
6. **Validate field names** using grep matches (case-sensitive). Do not open or parse the file beyond grep.
## Tips for Agents
- **Always verify field names** by running the specific grep commands; do not open the schema file
- **Use grep with anchors and minimal -A context** to explore the schema efficiently—never read or stream the file
- **Check relationships** by looking for `parentRelationship` and `childRelationship` comments in type definitions
- **Look for Connection types** (e.g., `AccountConnection`) via grep to understand pagination structure
- **Custom objects** end with `__c` (e.g., `CustomObject__c`)
- **Custom fields** also end with `__c` (e.g., `Custom_Field__c`)
## Forbidden Operations
To prevent accidental large reads, the following are prohibited for schema.graphql:
- Opening in any editor (VS Code, vim, nano)
- Using cat, less, more, head, or tail
- Programmatic parsing (node, python, awk, sed, jq)
- Streaming or paginating through large portions of the file
If any of the above occurs, stop and replace the action with one of the Allowed Lookups (grep-only).
## Output Minimization
- Prefer precise, anchored patterns with word boundaries
- Use the smallest `-A` context that surfaces required lines
- If results are noisy, refine the regex rather than increasing context
## Related Skills
- For generating read queries, invoke the `generating-graphql-read-query` skill
- For generating mutation queries, invoke the `generating-graphql-mutation-query` skill

View File

@ -1,167 +0,0 @@
---
name: fetching-webapp-rest-api
description: REST API usage via the Data SDK fetch method. Use when implementing Chatter, Connect REST, Apex REST, UI API REST, or Einstein LLM calls — only when GraphQL is not sufficient.
paths:
- "**/*.ts"
- "**/*.tsx"
- "**/*.graphql"
---
# Salesforce REST API via Data SDK Fetch
Use `sdk.fetch` from the Data SDK when GraphQL is not sufficient. The SDK applies authentication, CSRF handling, and base URL resolution. **Always use optional chaining** (`sdk.fetch?.()`) and handle the case where `fetch` is not available.
Invoke this skill when you need to call Chatter, Connect REST, Apex REST, UI API REST, or Einstein LLM endpoints.
## API Version
Use the project's API version. It is typically injected as `__SF_API_VERSION__`; fallback to `"65.0"`:
```typescript
declare const __SF_API_VERSION__: string;
const API_VERSION = typeof __SF_API_VERSION__ !== "undefined" ? __SF_API_VERSION__ : "65.0";
```
## Base Path
URLs are relative to the Salesforce API base. The SDK prepends the correct base path. Use paths starting with `/services/...`.
---
## Chatter API
User and collaboration data. No GraphQL equivalent.
| Endpoint | Method | Purpose |
| -------- | ------ | ------- |
| `/services/data/v{version}/chatter/users/me` | GET | Current user (id, name, email, username) |
```typescript
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 };
```
---
## Connect REST API
File and content operations.
| Endpoint | Method | Purpose |
| -------- | ------ | ------- |
| `/services/data/v{version}/connect/file/upload/config` | GET | Upload config (token, uploadUrl) for file uploads |
```typescript
const sdk = await createDataSDK();
const configRes = await sdk.fetch?.(`/services/data/v${API_VERSION}/connect/file/upload/config`, {
method: "GET",
});
if (!configRes?.ok) throw new Error(`Failed to get upload config: ${configRes?.status}`);
const config = await configRes.json();
const { token, uploadUrl } = config;
```
---
## Apex REST
Custom Apex REST resources. Requires corresponding Apex classes in the org. CSRF protection is applied automatically for `services/apexrest` URLs.
| Endpoint | Method | Purpose |
| -------- | ------ | ------- |
| `/services/apexrest/auth/login` | POST | User login |
| `/services/apexrest/auth/register` | POST | User registration |
| `/services/apexrest/auth/forgot-password` | POST | Request password reset |
| `/services/apexrest/auth/reset-password` | POST | Reset password with token |
| `/services/apexrest/auth/change-password` | POST | Change password (authenticated) |
| `/services/apexrest/{resource}` | GET/POST | Custom Apex REST resources |
**Example (login):**
```typescript
const sdk = await createDataSDK();
const response = await sdk.fetch?.("/services/apexrest/auth/login", {
method: "POST",
body: JSON.stringify({ email, password, startUrl: "/" }),
headers: { "Content-Type": "application/json", Accept: "application/json" },
});
```
Apex REST paths do not include the API version.
---
## UI API (REST)
When GraphQL cannot cover the use case. **Prefer GraphQL** when possible.
| Endpoint | Method | Purpose |
| -------- | ------ | ------- |
| `/services/data/v{version}/ui-api/records/{recordId}` | GET | Fetch a single record |
```typescript
const sdk = await createDataSDK();
const response = await sdk.fetch?.(`/services/data/v${API_VERSION}/ui-api/records/${recordId}`);
```
---
## Einstein LLM Gateway
AI features. Requires Einstein API setup.
| Endpoint | Method | Purpose |
| -------- | ------ | ------- |
| `/services/data/v{version}/einstein/llm/prompt/generations` | POST | Generate text from Einstein LLM |
```typescript
const sdk = await createDataSDK();
const response = await sdk.fetch?.(`/services/data/v${API_VERSION}/einstein/llm/prompt/generations`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
additionalConfig: { applicationName: "PromptTemplateGenerationsInvocable" },
promptTextorId: prompt,
}),
});
if (!response?.ok) throw new Error(`Einstein LLM failed (${response?.status})`);
const data = await response.json();
return data?.generations?.[0]?.text ?? "";
```
---
## General Pattern
```typescript
import { createDataSDK } from "@salesforce/sdk-data";
const sdk = await createDataSDK();
if (!sdk.fetch) {
throw new Error("Data SDK fetch is not available in this context");
}
const response = await sdk.fetch(url, {
method: "GET", // or POST, PUT, PATCH, DELETE
headers: { "Content-Type": "application/json", Accept: "application/json" },
body: method !== "GET" ? JSON.stringify(payload) : undefined,
});
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const data = await response.json();
```
---
## Reference
- Parent: `accessing-data` — enforces Data SDK usage for all Salesforce data fetches
- GraphQL: `using-graphql` — use for record queries and mutations when possible
- `createRecord` from `@salesforce/webapp-experimental/api` for UI API record creation (uses SDK internally)

View File

@ -1,6 +1,6 @@
---
name: generating-custom-application
description: Use this skill when users need to create or configure Salesforce Custom Applications. Trigger when users mention custom apps, application metadata, app navigation, or organizing tabs into applications. Use when users want to create app containers for tabs and pages. Always use this skill for custom application work.
description: "Use this skill when users need to create or configure Salesforce Custom Applications. Trigger when users mention custom apps, application metadata, app navigation, or organizing tabs into applications. Use when users want to create app containers for tabs and pages. Always use this skill for custom application work."
---
## When to Use This Skill

View File

@ -1,6 +1,6 @@
---
name: generating-custom-field
description: Use this skill when users need to create, generate, or validate Salesforce Custom Field metadata. Trigger when users mention custom fields, field types, Roll-up Summary fields, Master-Detail relationships, Lookup relationships, formula fields, picklists, or field metadata. Also use when users encounter field deployment errors, especially around Roll-up Summary format, Master-Detail constraints, or formula issues. Always use this skill for any custom field metadata work, field generation, or field troubleshooting.
description: "Use this skill when users need to create, generate, or validate Salesforce Custom Field metadata. Trigger when users mention custom fields, field types, Roll-up Summary fields, Master-Detail relationships, Lookup relationships, formula fields, picklists, or field metadata. Also use when users encounter field deployment errors, especially around Roll-up Summary format, Master-Detail constraints, or formula issues. Always use this skill for any custom field metadata work, field generation, or field troubleshooting."
---
## When to Use This Skill

View File

@ -1,6 +1,6 @@
---
name: generating-custom-lightning-type
description: Use this skill when users need to create Custom Lightning Types (CLTs) for Einstein Agent actions or structured input/output schemas. Trigger when users mention CLT, Custom Lightning Types, JSON schemas for agents, type definitions, lightning__objectType, or editor/renderer configurations. This is complex - always use this skill for CLT work.
description: "Use this skill when users need to create Custom Lightning Types (CLTs) for Einstein Agent actions or structured input/output schemas. Trigger when users mention CLT, Custom Lightning Types, JSON schemas for agents, type definitions, lightning__objectType, or editor/renderer configurations. This is complex - always use this skill for CLT work."
---
## When to Use This Skill

View File

@ -1,6 +1,6 @@
---
name: generating-custom-object
description: Use this skill when users need to create, generate, or validate Salesforce Custom Object metadata. Trigger when users mention custom objects, creating objects, object metadata, .object files, sharing models, name fields, or validation rules on objects. Also use when users say things like "create a custom object", "generate object metadata", "set up an object for...", or when they're troubleshooting object deployment errors especially around sharing models and Master-Detail relationships. Always use this skill for any custom object metadata work.
description: "Use this skill when users need to create, generate, or validate Salesforce Custom Object metadata. Trigger when users mention custom objects, creating objects, object metadata, .object files, sharing models, name fields, or validation rules on objects. Also use when users say things like \"create a custom object\", \"generate object metadata\", \"set up an object for...\", or when they're troubleshooting object deployment errors especially around sharing models and Master-Detail relationships. Always use this skill for any custom object metadata work."
---
## When to Use This Skill

View File

@ -1,6 +1,6 @@
---
name: generating-custom-tab
description: Use this skill when users need to create or configure Salesforce Custom Tabs. Trigger when users mention tabs, navigation tabs, object tabs, web tabs, Visualforce tabs, Lightning component tabs, app page tabs, or tab configuration. Also use when users want to add navigation to custom objects, create tabs for external content, or set up Lightning page tabs. Always use this skill for any custom tab work.
description: "Use this skill when users need to create or configure Salesforce Custom Tabs. Trigger when users mention tabs, navigation tabs, object tabs, web tabs, Visualforce tabs, Lightning component tabs, app page tabs, or tab configuration. Also use when users want to add navigation to custom objects, create tabs for external content, or set up Lightning page tabs. Always use this skill for any custom tab work."
---
## When to Use This Skill

View File

@ -1,6 +1,6 @@
---
name: generating-experience-lwr-site
description: Creates, modifies, or manages Salesforce Experience Cloud LWR sites via DigitalExperience metadata. Always trigger when users mention Experience sites, LWR sites, DigitalExperience, Experience Cloud, community sites, portals, creating pages, adding routes, views, theme layouts, branding sets, previewing sites, or any DigitalExperience bundle work. Also use when users mention specific content types like sfdc_cms__route, sfdc_cms__themeLayout, etc. or when troubleshooting site deployment.
description: "Creates, modifies, or manages Salesforce Experience Cloud LWR sites via DigitalExperience metadata. Always trigger when users mention Experience sites, LWR sites, DigitalExperience, Experience Cloud, community sites, portals, creating pages, adding routes, views, theme layouts, branding sets, previewing sites, or any DigitalExperience bundle work. Also use when users mention specific content types like sfdc_cms__route, sfdc_cms__themeLayout, etc. or when troubleshooting site deployment."
---
# Experience LWR Site Builder

View File

@ -1,6 +1,6 @@
---
name: generating-experience-react-site
description: Use this skill when users need to create or configure a Salesforce Digital Experience Site specifically for hosting a React web application. Trigger when users mention creating an Experience site for a React app, setting up a React site on Salesforce, configuring Network/CustomSite/DigitalExperience metadata for a web app, or deploying site infrastructure for a React application. Also trigger when users mention site URL path prefixes, app namespaces, appDevName, guest access configuration, DigitalExperienceConfig, DigitalExperienceBundle, or sfdc_cms__site content types in the context of React apps. Always use this skill for any React web application site creation or site infrastructure configuration work, even if the user just says "create a site for my React app" or "set up the site for my web application."
description: "Use this skill when users need to create or configure a Salesforce Digital Experience Site specifically for hosting a React web application. Trigger when users mention creating an Experience site for a React app, setting up a React site on Salesforce, configuring Network/CustomSite/DigitalExperience metadata for a web app, or deploying site infrastructure for a React application. Also trigger when users mention site URL path prefixes, app namespaces, appDevName, guest access configuration, DigitalExperienceConfig, DigitalExperienceBundle, or sfdc_cms__site content types in the context of React apps. Always use this skill for any React web application site creation or site infrastructure configuration work, even if the user just says \"create a site for my React app\" or \"set up the site for my web application.\""
---
# Digital Experience Site for React Web Applications

View File

@ -1,6 +1,6 @@
---
name: generating-flexipage
description: Use this skill when users need to create, generate, modify, or validate Salesforce Lightning pages (FlexiPages). Trigger when users mention RecordPage, AppPage, HomePage, Lightning pages, page layouts, adding components to pages, or page customization. Also use when users say things like "create a Lightning page", "add a component to a page", "customize the record page", "generate a FlexiPage", or when they're working with FlexiPage XML files and need help with components, regions, or deployment errors. Always use this skill for any FlexiPage-related work, even if they just mention "page" in the context of Salesforce.
description: "Use this skill when users need to create, generate, modify, or validate Salesforce Lightning pages (FlexiPages). Trigger when users mention RecordPage, AppPage, HomePage, Lightning pages, page layouts, adding components to pages, or page customization. Also use when users say things like \"create a Lightning page\", \"add a component to a page\", \"customize the record page\", \"generate a FlexiPage\", or when they're working with FlexiPage XML files and need help with components, regions, or deployment errors. Always use this skill for any FlexiPage-related work, even if they just mention \"page\" in the context of Salesforce."
---
## When to Use This Skill

View File

@ -1,6 +1,6 @@
---
name: generating-flow
description: 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 created", "trigger daily at", "send an email when", "update the field when", "automate", "workflow", or "flow XML/metadata". This is the only skill for Salesforce Flow generation.
description: "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 created\", \"trigger daily at\", \"send an email when\", \"update the field when\", \"automate\", \"workflow\", or \"flow XML/metadata\". This is the only skill for Salesforce Flow generation."
---
## Goal

View File

@ -1,6 +1,6 @@
---
name: generating-fragment
description: Use this skill when users need to create or edit Salesforce Fragments (reusable UI pieces). Trigger when users mention fragments, UEM blocks, reusable UI templates, structured rendering across Slack/Mobile/LEX, or block-based layouts. Also use when users want to create unified experience components. Always use this skill for any fragment work.
description: "Use this skill when users need to create or edit Salesforce Fragments (reusable UI pieces). Trigger when users mention fragments, UEM blocks, reusable UI templates, structured rendering across Slack/Mobile/LEX, or block-based layouts. Also use when users want to create unified experience components. Always use this skill for any fragment work."
---
## When to Use This Skill

View File

@ -1,6 +1,6 @@
---
name: generating-list-view
description: Use this skill when users need to create, generate, or validate Salesforce List View metadata. Trigger when users mention list views, filtered record lists, creating views, setting up record columns, filtering records by criteria, or ask about list view visibility. Also use when users say things like "I need a view that shows...", "filter records by...", "create a list view for...", or when they're working with ListView XML files and need validation or troubleshooting.
description: "Use this skill when users need to create, generate, or validate Salesforce List View metadata. Trigger when users mention list views, filtered record lists, creating views, setting up record columns, filtering records by criteria, or ask about list view visibility. Also use when users say things like \"I need a view that shows...\", \"filter records by...\", \"create a list view for...\", or when they're working with ListView XML files and need validation or troubleshooting."
---
## When to Use This Skill

View File

@ -1,6 +1,6 @@
---
name: generating-permission-set
description: Generates correct, deployable Salesforce permission set metadata (PermissionSet XML) with object, field, user, and app permissions. Use this skill when creating or editing permission set metadata, object permissions, field-level security (FLS), tab visibility, or deploying permission sets.
description: "Generates correct, deployable Salesforce permission set metadata (PermissionSet XML) with object, field, user, and app permissions. Use this skill when creating or editing permission set metadata, object permissions, field-level security (FLS), tab visibility, or deploying permission sets."
compatibility: Salesforce Metadata API v60.0+
metadata:
author: afv-library

View File

@ -1,6 +1,6 @@
---
name: generating-validation-rule
description: Use this skill when users need to create, modify, or validate Salesforce Validation Rules. Trigger when users mention validation rules, field validation, data quality rules, formula validation, error messages, or validation logic. Also use when users encounter validation errors, need to update formulas, or want to enforce business rules at the data layer. Always use this skill for any validation rule work.
description: "Use this skill when users need to create, modify, or validate Salesforce Validation Rules. Trigger when users mention validation rules, field validation, data quality rules, formula validation, error messages, or validation logic. Also use when users encounter validation errors, need to update formulas, or want to enforce business rules at the data layer. Always use this skill for any validation rule work."
---
## When to Use This Skill

View File

@ -1,258 +0,0 @@
---
name: generating-webapp-graphql-mutation-query
description: Generate Salesforce GraphQL mutation queries. Use when the query to generate is a mutation query. Schema exploration must complete first — invoke exploring-graphql-schema first.
paths:
- "**/*.ts"
- "**/*.tsx"
- "**/*.graphql"
---
# Salesforce GraphQL Mutation Query Generation
**Triggering conditions**
1. Only if the schema exploration phase completed successfully (invoke `exploring-graphql-schema` first)
2. Only if the query to generate is a mutation query
## Schema Access Policy
> ⚠️ **GREP ONLY** — During mutation generation you may need to verify field names, input types, or representations. All schema lookups **MUST** use the grep-only commands defined in the `exploring-graphql-schema` skill. Do NOT open, read, stream, or parse `./schema.graphql` with any tool other than grep.
## Your Role
You are a GraphQL expert. Generate Salesforce-compatible mutation queries. Schema exploration must complete first. If the schema exploration has not been executed yet, you **MUST** run the full exploration workflow from the `exploring-graphql-schema` skill first, then return here for mutation query generation.
## Mutation Queries General Information
The GraphQL engine supports `Create`, `Update`, and `Delete` operations. `Update` and `Delete` operate on Id-based entity identification. See the [mutation query schema](#mutation-query-schema) section.
## Mutation Query Generation Workflow
Strictly follow the rules below when generating the GraphQL mutation query:
1. **Input Fields Validation** - Validate that the set of fields validate [input field constraints](#mutation-queries-input-field-constraints). Verify every field name and type against grep output from the schema — do NOT guess or assume
2. **Output Fields Validation** - Validate that the set of fields used in the select part of the query validate the [output fields constraints](#mutation-queries-output-field-constraints)
3. **Type Consistency** - Make sure variables used as query arguments and their related fields share the same GraphQL type. Verify types via grep lookup — do NOT assume types
4. **Report Phase** - Use the [Mutation Query Report Template](#mutation-query-report-template) below to report on the previous validation phases
5. **Input Arguments** - `input` is the default name for the argument, unless otherwise specified
6. **Output Field** - For `Create` and `Update` operations, the output field is always named `Record`, and is of type EntityName
7. **Field Name Validation** - Every field name in the generated mutation **MUST** match a field confirmed via grep lookup in the schema. Do NOT guess or assume field names exist
8. **Query Generation** - Use the [mutation query](#mutation-query-templates) template and adjust it based on the selected operation
9. **Output Format** - Use the [standalone](#mutation-standalone-default-output-format---clean-code-only)
10. **Lint Validation** - After writing the mutation to a file, run `npx eslint <file>` from the webapp dir to validate it against the schema. Fix any reported errors before proceeding. See [Lint Validation](#lint-validation) for details
11. **Test the Query** - Use the [Generated Mutation Query Testing](#generated-mutation-query-testing) workflow to test the generated query
1. **Report First** - Always output the generated mutation in the proper output format BEFORE initiating any test
## Mutation Query Schema
**Important**: In the schema fragments below, replace **EntityName** occurrences by the real entity name (i.e. Account, Case...).
**Important**: `Delete` operations all share the same generic `Record` entity name for both input and payload, only exposing the standard `Id` field.
```graphql
input EntityNameCreateRepresentation {
# Subset of EntityName fields here
}
input EntityNameCreateInput { EntityName: EntityNameCreateRepresentation! }
type EntityNameCreatePayload { Record: EntityName! }
input EntityNameUpdateRepresentation {
# Subset of EntityName fields here
}
input EntityNameUpdateInput { Id: IdOrRef! EntityName: EntityNameUpdateRepresentation! }
type EntityNameUpdatePayload { Record: EntityName! }
input RecordDeleteInput { Id: IdOrRef! }
type RecordDeletePayload { Id: ID }
type UIAPIMutations {
EntityNameCreate(input: EntityNameCreateInput!): EntityNameCreatePayload
EntityNameDelete(input: RecordDeleteInput!): RecordDeletePayload
EntityNameUpdate(input: EntityNameUpdateInput!): EntityNameUpdatePayload
}
```
## Mutation Queries Input Field Constraints
1. **`Create` Mutation Queries**:
1. **MUST** include all required fields
2. **MUST** only include createable fields
3. Child relationships can't be set and **MUST** be excluded
4. Fields with type `REFERENCE` can only be assigned IDs through their `ApiName` name
2. **`Update` Mutation Queries**:
1. **MUST** include the id of the entity to update
2. **MUST** only include updateable fields
3. Child relationships can't be set and **MUST** be excluded
4. Fields with type `REFERENCE` can only be assigned IDs through their `ApiName` name
3. **`Delete` Mutation Queries**:
1. **MUST** include the id of the entity to delete
## Mutation Queries Output Field Constraints
1. **`Create` and `Update` Mutation Queries**:
1. **MUST** exclude all child relationships
2. **MUST** exclude all `REFERENCE` fields, unless accessed through their `ApiName` member (no navigation to referenced entity)
3. Inaccessible fields will be reported as part of the `errors` attribute in the returned payload
4. Child relationships **CAN'T** be queried as part of a mutation
5. Fields with type `REFERENCE` can only be queried through their `ApiName` (no referenced entities navigation, no sub fields)
2. **`Delete` Mutation Queries**:
1. **MUST** only include the `Id` field
## Mutation Query Report Template
Input arguments:
- Required fields: FieldName1 (Type1), FieldName2 (Type2)...
- Other fields: FieldName3 (Type3)...
Output fields: FieldNameA (TypeA), FieldNameB (TypeB)...
## Mutation Query Templates
```graphql
mutation mutateEntityName(
# arguments
) {
uiapi {
EntityNameOperation(input: {
# the following is for `Create` and `Update` operations only
EntityName: {
# Input fields
}
# the following is for `Update` and `Delete` operations only
Id: ... # id here
}) {
# the following is for `Create` and `Update` operations only
Record {
# Output fields
}
# the following is for `Delete` operations only
Id: ... # id here
}
}
}
```
## Mutation Standalone (Default) Output Format - CLEAN CODE ONLY
```javascript
import { gql } from '@salesforce/sdk-data';
const QUERY_NAME = gql`
mutation mutateEntity($input: EntityNameOperationInput!) {
uiapi {
EntityNameOperation(input: $input) {
# select output fields here depending on operation type
}
}
}
`;
const QUERY_VARIABLES = {
input: {
// The following is for `Create` and `Update` operations only
EntityName: {
// variables here
},
// The following is for `Update` and `Delete` operations only
Id: ... // id here
}
};
```
**❌ FORBIDDEN — Do NOT include any of the following:**
- Explanatory comments about the query (inline or surrounding)
- Field descriptions or annotations
- Additional text about what the query does
- Workflow step descriptions or summaries
- Comments like `// fetches...`, `// creates...`, `/* ... */`
**✅ ONLY output:**
- The raw query string constant (using `gql` tagged template)
- The variables object constant
- Nothing else — no extra imports, no exports, no wrapper functions
## Lint Validation
After writing the generated mutation into a source file, validate it against the schema using the project's GraphQL ESLint setup:
```bash
# Run from webapp dir (force-app/main/default/webapplications/<app-name>/)
npx eslint <path-to-file-containing-mutation>
```
**How it works:** The ESLint config uses `@graphql-eslint/eslint-plugin` with its `processor`, which extracts GraphQL operations from `gql` template literals in `.ts`/`.tsx` files and validates the extracted `.graphql` virtual files against `schema.graphql`.
**Rules enforced:** `no-anonymous-operations`, `no-duplicate-fields`, `known-fragment-names`, `no-undefined-variables`, `no-unused-variables`
**On failure:** Fix the reported issues, re-run `npx eslint <file>` until clean, then proceed to testing.
> ⚠️ **Prerequisites**: The `schema.graphql` file must exist (invoke `exploring-graphql-schema` first) and project dependencies must be installed (`npm install`).
## Generated Mutation Query Testing
**Triggering conditions** — **ALL conditions must be true:**
1. The [Mutation Query Generation Workflow](#mutation-query-generation-workflow) completed with status `SUCCESS` and you have a generated query
2. The query is a mutation query
3. A non-manual method was used during schema exploration to retrieve introspection data
**Workflow**
1. **Report Step** - State the exact method you will use to test (e.g., `sf api request graphql` from the **project root**, Connect API, etc.) — this **MUST** match the method used during schema exploration
2. **Interactive Step** - Ask the user whether they want you to test the query using the proposed method
1. **STOP and WAIT** for the user's answer. Do NOT proceed until the user responds. Do NOT assume consent.
3. **Input Arguments** - You **MUST** ask the user for the input argument values to use in the test
1. **STOP and WAIT** for the user's answer. Do NOT proceed until the user provides values. Do NOT fabricate test data.
4. **Test Query** - Only if the user explicitly agrees and has provided input values:
1. Execute the mutation using the reported method (e.g., `sf api request rest` to POST the query and variables to the GraphQL endpoint):
```bash
sf api request rest /services/data/v65.0/graphql \
--method POST \
--body '{"query":"mutation mutateEntity($input: EntityNameOperationInput!) { uiapi { EntityNameOperation(input: $input) { Record { Id } } } }","variables":{"input":{"EntityName":{"Field":"Value"}}}}'
```
2. Replace `v65.0` with the API version of the target org
3. Replace the `query` value with the generated mutation query string
4. Replace the `variables` value with the user-provided input arguments
5. **Result Analysis** - Retrieve the `data` and `errors` attributes from the returned payload, and report the result of the test as one of the following options:
1. `PARTIAL` if `data` is not an empty object, but `errors` is not an empty list - Explanation: some of the queried fields are not accessible on mutations
2. `FAILED` if `data` is an empty object - Explanation: the query is not valid
3. `SUCCESS` if `errors` is an empty list
6. **Remediation Step** - If status is not `SUCCESS`, use the [`FAILED`](#failed-status-handling-workflow) or [`PARTIAL`](#partial-status-handling-workflow) status handling workflows
### `FAILED` Status Handling Workflow
The query is invalid:
1. **Error Analysis** - Parse and categorize the specific error messages
2. **Root Cause Identification** - Use error message to identify the root cause:
- **Execution** - Error contains `invalid cross reference id` or `entity is deleted`
- **Syntax** - Error contains `invalid syntax`
- **Validation** - Error contains `validation error`
- **Type** - Error contains `VariableTypeMismatch` or `UnknownType`
- **Navigation** - Error contains `is not currently available in mutation results`
- **API Version** - Query deals with updates, you're testing with Connect API and error contains `Cannot invoke JsonElement.isJsonObject()`
3. **Targeted Resolution** - Depending on the root cause categorization
- **Execution** - You're trying to update or delete an unknown/no longer available entity: either create an entity first, if you have generated the related query, or ask for a valid entity id to use. **STOP and WAIT** for the user to provide a valid Id
- **Syntax** - Update the query using the error message information to fix the syntax errors
- **Validation** - The field name is most probably invalid. Re-run the relevant grep command from the `exploring-graphql-schema` skill to verify the correct field name. If still unclear, ask the user for clarification and **STOP and WAIT** for their answer
- **Type** - Use the error details and re-verify the type via grep lookup in the schema. Correct the argument type and adjust variables accordingly
- **Navigation** - Use the [`PARTIAL` status handling workflow](#partial-status-handling-workflow) below
- **API Version** - `Record` selection is only available with API version 64 and higher, **report** the issue, and try again with API version 64
4. **Test Again** - Resume the [query testing workflow](#generated-mutation-query-testing) with the updated query (increment and track attempt counter)
5. **Escalation Path** - If targeted resolution fails after 2 attempts, ask for additional details and restart the entire GraphQL workflow from the `exploring-graphql-schema` skill
### `PARTIAL` Status Handling Workflow
The query can be improved:
1. Report the fields mentioned in the `errors` list
2. Explain that these fields can't be queried as part of a mutation query
3. Explain that the query might be considered as failing, as it will report errors
4. Offer to remove the offending fields
5. **STOP and WAIT** for the user's answer. Do NOT remove fields without explicit consent.
6. If they are OK with removing the fields restart the [generation workflow](#mutation-query-generation-workflow) with the new field list
## Related Skills
- Schema exploration: `exploring-graphql-schema` (must complete first)
- Read query generation: `generating-graphql-read-query`

View File

@ -1,253 +0,0 @@
---
name: generating-webapp-graphql-read-query
description: Generate Salesforce GraphQL read queries. Use when the query to generate is a read query. Schema exploration must complete first — invoke exploring-graphql-schema first.
paths:
- "**/*.ts"
- "**/*.tsx"
- "**/*.graphql"
---
# Salesforce GraphQL Read Query Generation
**Triggering conditions**
1. Only if the schema exploration phase completed successfully (invoke `exploring-graphql-schema` first)
2. Only if the query to generate is a read query
## Schema Access Policy
> ⚠️ **GREP ONLY** — During query generation you may need to verify field names, types, or relationships. All schema lookups **MUST** use the grep-only commands defined in the `exploring-graphql-schema` skill. Do NOT open, read, stream, or parse `./schema.graphql` with any tool other than grep.
## Field-Level Security and @optional
Field-level security (FLS) restricts which fields different users can see. Use the `@optional` directive on Salesforce record fields when possible. The server omits the field when the user lacks access, allowing the query to succeed instead of failing. Apply `@optional` to scalar fields, value-type fields (e.g. `Name { value }`), parent relationships, and child relationships. Available in API v65.0+.
**Consuming code must defend against missing fields.** When a field is omitted due to FLS, it will be `undefined` (or absent) in the response. Use optional chaining (`?.`), nullish coalescing (`??`), and explicit null/undefined checks when reading query results. Never assume an optional field is present — otherwise the app may crash or behave incorrectly for users without field access.
```ts
// ✅ Defend against missing fields
const name = node.Name?.value ?? '';
const relatedName = node.RelationshipName?.Name?.value ?? 'N/A';
// ❌ Unsafe — will throw if field omitted due to FLS
const name = node.Name.value;
```
## Your Role
You are a GraphQL expert. Generate Salesforce-compatible read queries. Schema exploration must complete first. If the schema exploration has not been executed yet, you **MUST** run the full exploration workflow from the `exploring-graphql-schema` skill first, then return here for read query generation.
## Read Query Generation Workflow
Strictly follow the rules below when generating the GraphQL read query:
1. **No Proliferation** - Only generate for the explicitly requested fields, nothing else. Do NOT add fields the user did not ask for.
2. **Unique Query** - Leverage child relationships to query entities in one single query
3. **Navigate Entities** - Always use `relationshipName` to access reference fields and child entities
1. **Exception** - if the `relationshipName` field is null, you can't navigate the related entity, and will have to return the `Id` itself
4. **Leverage Fragments** - Generate one fragment per possible type on polymorphic fields (field with `dataType="REFERENCE"` and more than one entry in `referenceToInfos` introspection attribute)
5. **Type Consistency** - Make sure variables used as query arguments and their related fields share the same GraphQL type. Verify types against grep output from the schema — do not assume types
6. **Type Enforcement** - Make sure to leverage field type information from introspection and GraphQL schema to generate field access
7. **Field Name Validation** - Every field name in the generated query **MUST** match a field confirmed via grep lookup in the schema. Do NOT guess or assume field names exist
8. **@optional for FLS** - Apply `@optional` on all Salesforce record fields when possible (see [Field-Level Security and @optional](#field-level-security-and-optional)). This lets the query succeed when the user lacks field-level access; the server omits inaccessible fields instead of failing
9. **Consuming code defense** - When generating or modifying code that consumes read query results, defend against missing fields (see [Field-Level Security and @optional](#field-level-security-and-optional)). Use optional chaining, nullish coalescing, and null/undefined checks — never assume optional fields are present
10. **Semi and anti joins** - Use the semi-join or anti-join templates to filter an entity with conditions on child entities
11. **Query Generation** - Use the [template](#read-query-template) to generate the query
12. **Output Format** - Use the [standalone](#read-standalone-default-output-format---clean-code-only)
13. **Lint Validation** - After writing the query to a file, run `npx eslint <file>` from the webapp dir to validate it against the schema. Fix any reported errors before proceeding. See [Lint Validation](#lint-validation) for details
14. **Test the Query** - Use the [Generated Read Query Testing](#generated-read-query-testing) workflow to test the generated query
1. **Report First** - Always output the generated query in the proper output format BEFORE initiating any test
## Read Query Template
```graphql
query QueryName {
uiapi {
query {
EntityName(
# conditions here
) {
edges {
node {
# Direct fields — use @optional for FLS resilience
FieldName @optional { value }
# Non-polymorphic reference (single type)
RelationshipName @optional {
Id
Name { value }
}
# Polymorphic reference (multiple types)
PolymorphicRelationshipName @optional {
...TypeAInfo
...TypeBInfo
}
# Child relationship (subquery)
RelationshipName @optional (
# conditions here
) {
edges {
node {
# fields
}
}
}
}
}
}
}
}
}
fragment TypeAInfo on TypeA {
Id
SpecificFieldA @optional { value }
}
fragment TypeBInfo on TypeB {
Id
SpecificFieldB @optional { value }
}
```
## Semi-Join and Anti-Join Condition Template
Semi-joins (resp. anti-joins) condition leverage parent-child relationships and allow filtering the parent entity using a condition on child entities.
This is a standard `where` condition, on the parent entity's `Id`, expressed using the `inq` (resp. `ninq`, i.e. not `inq`) operator. This operator accepts two attributes:
- The child entity camelcase name to apply the condition on, with a value expressing the condition
- The field name on the child entity containing the parent entity `Id`, which is the `fieldName` from the `childRelationships` information for the child entity
- If the only condition is related child entity existence, you can use an `Id: { ne: null }` condition
### Semi-Join Example - ParentEntity with at least one Matching ChildEntity
```graphql
query testSemiJoin {
uiapi {
query {
ParentEntity(
where: {
Id: {
inq: {
ChildEntity: {
# standard conditions here
Name: { like: "test%" }
Type: { eq: "some value" }
}
ApiName: "parentIdFieldInChild"
}
}
}
) {
edges {
node {
Id
Name @optional {
value
}
}
}
}
}
}
}
```
### Anti-Join Example - ParentEntity with no Matching ChildEntity
Same example as the [Semi-Join Example](#semi-join-example---parententity-with-at-least-one-matching-childentity), but replacing the `inq` operator by the `ninq` one.
## Read Standalone (Default) Output Format - CLEAN CODE ONLY
```javascript
const QUERY_NAME = `
query GetData {
# query here
}
`;
const QUERY_VARIABLES = {
// variables here
};
```
**❌ FORBIDDEN — Do NOT include any of the following:**
- Explanatory comments about the query (inline or surrounding)
- Field descriptions or annotations
- Additional text about what the query does
- Workflow step descriptions or summaries
- Comments like `// fetches...`, `// returns...`, `/* ... */`
**✅ ONLY output:**
- The raw query string constant
- The variables object constant
- Nothing else — no imports, no exports, no wrapper functions
## Lint Validation
After writing the generated query into a source file, validate it against the schema using the project's GraphQL ESLint setup:
```bash
# Run from webapp dir (force-app/main/default/webapplications/<app-name>/)
npx eslint <path-to-file-containing-query>
```
**How it works:** The ESLint config uses `@graphql-eslint/eslint-plugin` with its `processor`, which extracts GraphQL operations from `gql` template literals in `.ts`/`.tsx` files and validates the extracted `.graphql` virtual files against `schema.graphql`.
**Rules enforced:** `no-anonymous-operations`, `no-duplicate-fields`, `known-fragment-names`, `no-undefined-variables`, `no-unused-variables`
**On failure:** Fix the reported issues, re-run `npx eslint <file>` until clean, then proceed to testing.
> ⚠️ **Prerequisites**: The `schema.graphql` file must exist (invoke `exploring-graphql-schema` first) and project dependencies must be installed (`npm install`).
## Generated Read Query Testing
**Triggering conditions** — **ALL conditions must be true:**
1. The [Read Query Generation Workflow](#read-query-generation-workflow) completed with status `SUCCESS` and you have a generated query
2. The query is a read query
3. A non-manual method was used during schema exploration to retrieve introspection data
**Workflow**
1. **Report Step** - State the exact method you will use to test (e.g., `sf api request graphql` from the **project root**, Connect API, etc.) — this **MUST** match the method used during schema exploration
2. **Interactive Step** - Ask the user whether they want you to test the query using the proposed method
1. **STOP and WAIT** for the user's answer. Do NOT proceed until the user responds. Do NOT assume consent.
3. **Test Query** - Only if the user explicitly agrees:
1. Use `sf api request rest` to POST the query to the GraphQL endpoint:
```bash
sf api request rest /services/data/v65.0/graphql \
--method POST \
--body '{"query":"query GetData { uiapi { query { EntityName { edges { node { Id } } } } } }"}'
```
2. Replace `v65.0` with the API version of the target org
3. Replace the `query` value with the generated read query string
4. If the query uses variables, include them in the JSON body as a `variables` key
5. Report the result as `SUCCESS` if the query executed without error, or `FAILED` if errors were returned
6. An empty result set with no errors is `SUCCESS` — the query is valid, the org simply has no matching data
4. **Remediation Step** - If status is `FAILED`, use the [`FAILED` status handling workflows](#failed-status-handling-workflow)
### `FAILED` Status Handling Workflow
The query is invalid:
1. **Error Analysis** - Parse and categorize the specific error messages
2. **Root Cause Identification** - Use error message to identify the root cause:
- **Syntax** - Error contains `invalid syntax`
- **Validation** - Error contains `validation error`
- **Type** - Error contains `VariableTypeMismatch` or `UnknownType`
3. **Targeted Resolution** - Depending on the root cause categorization
- **Syntax** - Update the query using the error message information to fix the syntax errors
- **Validation** - The field name is most probably invalid. Re-run the relevant grep command from the `exploring-graphql-schema` skill to verify the correct field name. If still unclear, ask the user for clarification and **STOP and WAIT** for their answer
- **Type** - Use the error details and re-verify the type via grep lookup in the schema. Correct the argument type and adjust variables accordingly
4. **Test Again** - Resume the [query testing workflow](#generated-read-query-testing) with the updated query (increment and track attempt counter)
5. **Escalation Path** - If targeted resolution fails after 2 attempts, ask for additional details and restart the entire GraphQL workflow from the `exploring-graphql-schema` skill
## Related Skills
- Schema exploration: `exploring-graphql-schema` (must complete first)
- Mutation generation: `generating-graphql-mutation-query`

View File

@ -1,6 +1,6 @@
---
name: implementing-webapp-file-upload
description: Add file upload functionality to React webapps with progress tracking and Salesforce ContentVersion integration. Use when the user wants to upload files, attach documents, handle file input, create file dropzones, track upload progress, or link files to Salesforce records. This feature provides programmatic APIs ONLY — no components or hooks are exported. Build your own custom UI using the upload() API. ALWAYS use this feature instead of building file upload from scratch with FormData or XHR.
description: "Add file upload functionality to React webapps with progress tracking and Salesforce ContentVersion integration. Use when the user wants to upload files, attach documents, handle file input, create file dropzones, track upload progress, or link files to Salesforce records. This feature provides programmatic APIs ONLY — no components or hooks are exported. Build your own custom UI using the upload() API. ALWAYS use this feature instead of building file upload from scratch with FormData or XHR."
---
# File Upload API (workflow)

View File

@ -1,6 +1,6 @@
---
name: installing-webapp-features
description: Search, describe, and install pre-built UI features (authentication, shadcn components, navigation, search, GraphQL, Agentforce AI) into Salesforce webapps. Use this when the user wants to add functionality to a webapp, or when determining what salesforce-provided features are available — whether prompted by the user or on your own initiative. Always check for an existing feature before building from scratch.
description: "Search, describe, and install pre-built UI features (authentication, shadcn components, navigation, search, GraphQL, Agentforce AI) into Salesforce webapps. Use this when the user wants to add functionality to a webapp, or when determining what salesforce-provided features are available — whether prompted by the user or on your own initiative. Always check for an existing feature before building from scratch."
---
# webapps-features-experimental CLI — Agent Reference

View File

@ -1,6 +1,6 @@
---
name: managing-webapp-agentforce-conversation-client
description: Adds or modifies AgentforceConversationClient in React apps (.tsx or .jsx files). Use when user says "add chat widget", "embed agentforce", "add agent", "add chatbot", "integrate conversational AI", or asks to change colors, dimensions, styling, or configure agentId, width, height, inline mode, or styleTokens for travel agent, HR agent, employee agent, or any Salesforce agent chat.
description: "Adds or modifies AgentforceConversationClient in React apps (.tsx or .jsx files). Use when user says \"add chat widget\", \"embed agentforce\", \"add agent\", \"add chatbot\", \"integrate conversational AI\", or asks to change colors, dimensions, styling, or configure agentId, width, height, inline mode, or styleTokens for travel agent, HR agent, employee agent, or any Salesforce agent chat."
metadata:
author: ACC Components
version: 1.0.0

View File

@ -1,6 +1,6 @@
---
name: trigger-refactor-pipeline
description: Refactor Salesforce triggers into handler patterns with automated test generation and deployment. Use when modernizing legacy triggers with DML/SOQL in loops or inconsistent patterns.
description: "Refactor Salesforce triggers into handler patterns with automated test generation and deployment. Use when modernizing legacy triggers with DML/SOQL in loops or inconsistent patterns."
license: Apache-2.0
compatibility: Requires Salesforce CLI, Python 3.9+
metadata:

View File

@ -1,324 +0,0 @@
---
name: using-webapp-graphql
description: Salesforce GraphQL data access. Use when the user asks to fetch, query, or mutate Salesforce data, or add a GraphQL operation for an object like Account, Contact, or Opportunity.
paths:
- "**/*.ts"
- "**/*.tsx"
- "**/*.graphql"
---
# Salesforce GraphQL
Guidance for querying and mutating Salesforce data via the Salesforce GraphQL API. Use `createDataSDK()` + `sdk.graphql?.()` and codegen tooling.
## When to Use
- User asks to "fetch data from Salesforce"
- User asks to "query" or "mutate" Salesforce records
- User wants to add a new GraphQL operation (query or mutation)
- User asks to add data access for a Salesforce object (Account, Contact, Opportunity, etc.)
## Schema Access Policy (GREP ONLY)
> **GREP ONLY** — The `schema.graphql` file is very large (~265,000+ lines). All schema lookups **MUST** use the grep-only commands defined in the `exploring-graphql-schema` skill. Do NOT open, read, stream, or parse `./schema.graphql` with any tool other than grep.
## Directory Context
The generated app has a two-level directory structure. Commands must run from the correct directory.
```
<project-root>/ ← SFDX project root
├── schema.graphql ← grep target
├── sfdx-project.json
└── force-app/main/default/webapplications/<app-name>/ ← webapp dir
├── package.json (npm scripts: graphql:schema, graphql:codegen, lint)
├── eslint.config.js (schema ref: ../../../../../schema.graphql)
├── codegen.yml (schema ref: ../../../../../schema.graphql)
└── src/ (source code, .graphql query files)
```
| Command | Run from | Why |
| ------------------------- | ---------------- | -------------------------------------- |
| `npm run graphql:schema` | **webapp dir** | Script is in webapp's `package.json` |
| `npm run graphql:codegen` | **webapp dir** | Reads `codegen.yml` in webapp dir |
| `npx eslint <file>` | **webapp dir** | Reads `eslint.config.js` in webapp dir |
| `grep ... schema.graphql` | **project root** | `schema.graphql` lives at project root |
| `sf api request graphql` | **project root** | Needs `sfdx-project.json` |
> **Wrong directory = silent failures.** `npm run graphql:schema` from the project root will fail with "missing script." `grep ./schema.graphql` from the webapp dir will fail with "no such file."
## Prerequisites
The base React app (`base-react-app`) ships with all GraphQL dependencies and tooling pre-configured:
- `@salesforce/sdk-data` — runtime SDK for `createDataSDK` and `gql`
- `@graphql-codegen/cli` + plugins — type generation from `.graphql` files and inline `gql` queries
- `@graphql-eslint/eslint-plugin` — validates `.graphql` files and `gql` template literals against `schema.graphql` (used as a query validation gate — see Step 6)
- `graphql` — shared by codegen, ESLint, and schema introspection
Before using this skill, ensure:
1. The `@salesforce/sdk-data` package is available (provides `createDataSDK`, `gql`, `NodeOfConnection`)
2. **Deployment order**: Metadata must be deployed before schema fetch; schema must be refetched after any metadata deployment. Invoke the `deploying-to-salesforce` skill when deploying or syncing with the org.
3. A `schema.graphql` file exists at the project root. If missing, generate it:
```bash
# Run from webapp dir (force-app/main/default/webapplications/<app-name>/)
npm run graphql:schema
```
## npm Scripts
- **`npm run graphql:schema`** — _(run from webapp dir)_ Downloads the full GraphQL schema from a connected Salesforce org via introspection. Outputs `schema.graphql` to the project root.
- **`npm run graphql:codegen`** — _(run from webapp dir)_ Generates TypeScript types from `.graphql` files and inline `gql` queries. Outputs to `src/api/graphql-operations-types.ts`.
## Workflow
### Step 1: Download Schema
Ensure `schema.graphql` exists at the project root. If missing, run `npm run graphql:schema` from the webapp dir.
### Step 2: Explore the Schema (grep-only)
Before writing any query, verify the target object and its fields exist in the schema.
**Invoke the `exploring-graphql-schema` skill** for the full exploration workflow and **mandatory grep-only access policy**.
> **GREP ONLY** — All schema lookups MUST use the grep commands defined in the `exploring-graphql-schema` skill. Do NOT open, read, stream, or parse `./schema.graphql` with any tool other than grep.
Key actions (all via grep):
- `type <ObjectName> implements Record` — find available fields
- `input <ObjectName>_Filter` — find filter options
- `input <ObjectName>_OrderBy` — find sorting options
- `input <ObjectName>CreateInput` / `<ObjectName>UpdateInput` — find mutation input types
### Step 3: Choose the Query Pattern
**Pattern 1 — External `.graphql` file** (recommended for complex queries):
- Queries with variables, fragments, or shared across files
- Full codegen support, syntax highlighting, shareable
- Requires codegen step after changes
- See example: `api/utils/accounts.ts` + `api/utils/query/highRevenueAccountsQuery.graphql`
**Pattern 2 — Inline `gql` tag** (recommended for simple queries):
- Simple queries without variables; colocated with usage code
- Supports dynamic queries (field set varies at runtime)
- **MUST use `gql` tag** — plain template strings bypass `@graphql-eslint` validation
- See example: `api/utils/user.ts`
### Step 4: Write the Query
For **Pattern 1**:
1. Create a `.graphql` file under `src/api/utils/query/`
2. Follow UIAPI structure: `query { uiapi { query { ObjectName(...) { edges { node { ... } } } } } }`
3. For mutations, invoke the `generating-graphql-mutation-query` skill
4. For read queries, invoke the `generating-graphql-read-query` skill
For **Pattern 2**:
1. Define query inline using the `gql` template tag
2. Ensure the query name matches what codegen expects
### Step 5: Test Queries Against Live Org
Use the testing workflows in the `generating-graphql-read-query` and `generating-graphql-mutation-query` skills to validate queries against the connected org before integrating into the app.
### Step 6: Generate Types
```bash
# Run from webapp dir (force-app/main/default/webapplications/<app-name>/)
npm run graphql:codegen
```
This updates `src/api/graphql-operations-types.ts` with `<OperationName>Query`/`<OperationName>Mutation` and `<OperationName>QueryVariables`/`<OperationName>MutationVariables`.
### Step 7: Lint Validate
Run ESLint on the file containing the query to validate it against the schema **before** any live testing:
```bash
# Run from webapp dir
npx eslint <path-to-file>
```
The `@graphql-eslint/eslint-plugin` processor extracts GraphQL from `gql` template literals and validates them against `schema.graphql`. Fix all ESLint errors before proceeding.
### Step 8: Implement and Verify
Implement the data access function using the pattern below. Use the Quality Checklist before completing.
---
## Core Types & Function Signatures
### createDataSDK and graphql
```typescript
import { createDataSDK } from "@salesforce/sdk-data";
const sdk = await createDataSDK();
const response = await sdk.graphql?.<ResponseType, VariablesType>(query, variables);
```
`createDataSDK()` returns a `DataSDK` instance. The `graphql` method uses optional chaining (`?.`) because not all surfaces support GraphQL.
### gql Template Tag
```typescript
import { gql } from "@salesforce/sdk-data";
const MY_QUERY = gql`
query MyQuery {
uiapi { ... }
}
`;
```
The `gql` tag enables ESLint validation against the schema. Plain template strings bypass validation.
### Error Handling
Default: treat any errors as failure (Strategy A). For partial data tolerance, log errors but use data. For mutations where some return fields are inaccessible, use Strategy C (fail only when no data).
```typescript
// Default: strict
if (response?.errors?.length) {
throw new Error(response.errors.map((e) => e.message).join("; "));
}
const result = response?.data;
```
Responses follow `uiapi.query.ObjectName.edges[].node`; fields use `{ value }`.
### NodeOfConnection
```typescript
import { type NodeOfConnection } from "@salesforce/sdk-data";
type AccountNode = NodeOfConnection<GetHighRevenueAccountsQuery["uiapi"]["query"]["Account"]>;
```
---
## Pattern 1: External .graphql File
Create a `.graphql` file, run `npm run graphql:codegen`, import with `?raw` suffix, and use generated types.
**Required imports:**
```typescript
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";
```
**When to use:** Complex queries with variables, fragments, or shared across files. Does NOT support dynamic queries (field set varies at runtime).
---
## Pattern 2: Inline gql Tag
**Required imports:**
```typescript
import { createDataSDK, gql } from "@salesforce/sdk-data";
import { type CurrentUserQuery } from "../graphql-operations-types";
const MY_QUERY = gql`
query CurrentUser {
uiapi { ... }
}
`;
```
> **MUST use `gql` tag** — plain template strings bypass the `@graphql-eslint` processor entirely, meaning no lint validation against the schema.
**When to use:** Simple, colocated queries. Supports dynamic queries (field set varies at runtime).
---
## Conditional Field Selection
For dynamic fieldsets with **known** fields, use `@include(if: $condition)` and `@skip(if: $condition)` in `.graphql` files. See GraphQL spec for details.
---
## Anti-Patterns (Not Recommended)
### Direct API Calls
```typescript
// NOT RECOMMENDED: Direct axios/fetch calls for GraphQL
// PREFERRED: Use the Data SDK
const sdk = await createDataSDK();
const response = await sdk.graphql?.<ResponseType>(query, variables);
```
### Missing Type Definitions
```typescript
// NOT RECOMMENDED: Untyped GraphQL calls
// PREFERRED: Provide response type
const response = await sdk.graphql?.<GetMyDataQuery>(query);
```
### Plain String Queries (Without gql Tag)
```typescript
// NOT RECOMMENDED: Plain strings bypass ESLint validation
const query = `query { ... }`;
// PREFERRED: Use gql tag for inline queries
const QUERY = gql`query { ... }`;
```
---
## Quality Checklist
> If you have not completed the workflow above, **stop and complete it first**. Invoke the skill workflow before using this checklist.
Before completing GraphQL data access code:
### For Pattern 1 (.graphql files):
1. [ ] All field names verified via grep against `schema.graphql` (invoke `exploring-graphql-schema`)
2. [ ] Create `.graphql` file for the query/mutation
3. [ ] Run `npm run graphql:codegen` to generate types
4. [ ] Import query with `?raw` suffix
5. [ ] Import generated types from `graphql-operations-types.ts`
6. [ ] Use `sdk.graphql?.<ResponseType>()` with proper generic
7. [ ] Handle `response.errors` and destructure `response.data`
8. [ ] Use `NodeOfConnection` for cleaner node types when needed
9. [ ] Run `npx eslint <file>` from webapp dir — fix all GraphQL errors
### For Pattern 2 (inline with gql):
1. [ ] All field names verified via grep against `schema.graphql`
2. [ ] Define query using `gql` template tag (NOT a plain string)
3. [ ] Ensure query name matches generated types in `graphql-operations-types.ts`
4. [ ] Import generated types for the query
5. [ ] Use `sdk.graphql?.<ResponseType>()` with proper generic
6. [ ] Handle `response.errors` and destructure `response.data`
7. [ ] Run `npx eslint <file>` from webapp dir — fix all GraphQL errors
### General:
- [ ] Lint validation passes (`npx eslint <file>` reports no GraphQL errors)
- [ ] Query field names match the schema exactly (case-sensitive, confirmed via grep)
- [ ] Response type generic is provided to `sdk.graphql?.<T>()`
- [ ] Optional chaining is used for nested response data
---
## Reference
- Schema exploration: invoke the `exploring-graphql-schema` skill
- Read query generation: invoke the `generating-graphql-read-query` skill
- Mutation query generation: invoke the `generating-graphql-mutation-query` skill
- Shared GraphQL schema types: `shared-schema.graphqls` (in this skill directory)
- Schema download: `npm run graphql:schema` (run from webapp dir)
- Type generation: `npm run graphql:codegen` (run from webapp dir)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,363 @@
---
name: using-webapp-salesforce-data
description: "Salesforce data access for reading, writing, and querying records via REST, GraphQL, Apex, or Platform SDK. Use when the user wants to fetch, search, filter, sort, display, create, update, delete, or attach files to Salesforce records (standard objects like Accounts, Contacts, Opportunities, Cases, Quotes, or any custom object) in a web app or UI component (React, Angular, Vue, etc.); call Chatter, Connect, or Apex REST APIs; or invoke AuraEnabled Apex methods from an external app. Does not apply to authentication/OAuth setup, schema changes (adding fields, relationships), Bulk/Tooling/Metadata API usage, declarative automation (Flows, Process Builder), general LWC/Apex coding guidance without a specific data operation, or Salesforce admin/configuration tasks."
---
# Salesforce Data Access
## When to Use
Use this skill when the user wants to:
- **Fetch or display Salesforce data** — Query records (Account, Contact, Opportunity, custom objects) to show in a component
- **Create, update, or delete records** — Perform mutations on Salesforce data
- **Add data fetching to a component** — Wire up a React component to Salesforce data
- **Call REST APIs** — Use Connect REST, Apex REST, or UI API endpoints
- **Explore the org schema** — Discover available objects, fields, or relationships
## Data SDK Requirement
> **All Salesforce data access MUST use the Data SDK** (`@salesforce/sdk-data`). The SDK handles authentication, CSRF, and base URL resolution. Never use `fetch()` or `axios` directly.
```typescript
import { createDataSDK, gql } from "@salesforce/sdk-data";
const sdk = await createDataSDK();
// GraphQL for record queries/mutations (PREFERRED)
const response = await sdk.graphql?.<ResponseType>(query, variables);
// REST for Connect REST, Apex REST, UI API (when GraphQL insufficient)
const res = await sdk.fetch?.("/services/apexrest/my-resource");
```
**Always use optional chaining** (`sdk.graphql?.()`, `sdk.fetch?.()`) — these methods may be undefined in some surfaces.
## Supported APIs
**Only the following APIs are permitted.** Any endpoint not listed here must not be used.
| API | Method | Endpoints / Use Case |
|-----|--------|----------------------|
| GraphQL | `sdk.graphql` | All record queries and mutations via `uiapi { }` namespace |
| UI API REST | `sdk.fetch` | `/services/data/v{ver}/ui-api/records/{id}` — record metadata when GraphQL is insufficient |
| Apex REST | `sdk.fetch` | `/services/apexrest/{resource}` — custom server-side logic, aggregates, multi-step transactions |
| Connect REST | `sdk.fetch` | `/services/data/v{ver}/connect/file/upload/config` — file upload config |
| Einstein LLM | `sdk.fetch` | `/services/data/v{ver}/einstein/llm/prompt/generations` — AI text generation |
**Not supported:**
- **Enterprise REST query endpoint** (`/services/data/v*/query` with SOQL) — blocked at the proxy level. Use GraphQL for record reads; use Apex REST if server-side SOQL aggregates are required.
- **Aura-enabled Apex** (`@AuraEnabled`) — an LWC/Aura pattern with no invocation path from React webapps.
- **Chatter API** (`/chatter/users/me`) — use `uiapi { currentUser { ... } }` in a GraphQL query instead.
- **Any other Salesforce REST endpoint** not listed in the supported table above.
## Decision: GraphQL vs REST
| Need | Method | Example |
|------|--------|---------|
| Query/mutate records | `sdk.graphql` | Account, Contact, custom objects |
| Current user info | `sdk.graphql` | `uiapi { currentUser { Id Name { value } } }` |
| UI API record metadata | `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 is preferred** for record operations. Use REST only when GraphQL doesn't cover the use case.
---
## GraphQL Workflow
### Step 1: Acquire Schema
The `schema.graphql` file (265K+ lines) is the source of truth. **Never open or parse it directly.**
1. Check if `schema.graphql` exists at the SFDX project root
2. If missing, run from the **webapp dir**: `npm run graphql:schema`
3. Custom objects appear only after metadata is deployed
### Step 2: Look Up Entity Schema
Map user intent to PascalCase names ("accounts" → `Account`), then **run the search script from the project root**:
```bash
# From project root — look up all relevant schema info for one or more entities
bash .a4drules/skills/using-salesforce-data/graphql-search.sh Account
# Multiple entities at once
bash .a4drules/skills/using-salesforce-data/graphql-search.sh Account Contact Opportunity
```
The script outputs five sections per entity:
1. **Type definition** — all queryable fields and relationships
2. **Filter options** — available fields for `where:` conditions
3. **Sort options** — available fields for `orderBy:`
4. **Create input** — fields accepted by create mutations
5. **Update input** — fields accepted by update mutations
Use this output to determine exact field names before writing any query or mutation. **Maximum 2 script runs.** If the entity still can't be found, ask the user — the object may not be deployed.
### Step 3: Generate Query
Use the templates below. Every field name **must** be verified from the script output in Step 2.
#### Read Query Template
```graphql
query GetAccounts {
uiapi {
query {
Account(where: { Industry: { eq: "Technology" } }, first: 10) {
edges {
node {
Id
Name @optional { value }
Industry @optional { value }
# Parent relationship
Owner @optional { Name { value } }
# Child relationship
Contacts @optional {
edges { node { Name @optional { value } } }
}
}
}
}
}
}
}
```
**FLS Resilience**: Apply `@optional` to all record fields. The server omits inaccessible fields instead of failing. Consuming code must use optional chaining:
```typescript
const name = node.Name?.value ?? "";
```
#### Mutation Template
```graphql
mutation CreateAccount($input: AccountCreateInput!) {
uiapi {
AccountCreate(input: $input) {
Record { Id Name { value } }
}
}
}
```
**Mutation constraints:**
- Create: Include required fields, only `createable` fields, no child relationships
- Update: Include `Id`, only `updateable` fields
- Delete: Include `Id` only
#### Object Metadata & Picklist Values
Use `uiapi { objectInfos(...) }` to fetch field metadata or picklist values. Pass **either** `apiNames` or `objectInfoInputs` — never both in the same query.
**Object metadata** (field labels, data types, CRUD flags):
```typescript
const GET_OBJECT_INFO = gql`
query GetObjectInfo($apiNames: [String!]!) {
uiapi {
objectInfos(apiNames: $apiNames) {
ApiName
label
labelPlural
fields {
ApiName
label
dataType
updateable
createable
}
}
}
}
`;
const sdk = await createDataSDK();
const response = await sdk.graphql?.(GET_OBJECT_INFO, { apiNames: ["Account"] });
const objectInfos = response?.data?.uiapi?.objectInfos ?? [];
```
**Picklist values** (use `objectInfoInputs` + `... on PicklistField` inline fragment):
```typescript
const GET_PICKLIST_VALUES = gql`
query GetPicklistValues($objectInfoInputs: [ObjectInfoInput!]!) {
uiapi {
objectInfos(objectInfoInputs: $objectInfoInputs) {
ApiName
fields {
ApiName
... on PicklistField {
picklistValuesByRecordTypeIDs {
recordTypeID
picklistValues {
label
value
}
}
}
}
}
}
}
`;
const response = await sdk.graphql?.(GET_PICKLIST_VALUES, {
objectInfoInputs: [{ objectApiName: "Account" }],
});
const fields = response?.data?.uiapi?.objectInfos?.[0]?.fields ?? [];
```
### Step 4: Validate & Test
1. **Lint**: `npx eslint <file>` from webapp dir
2. **Test**: Ask user before testing. For mutations, request input values — never fabricate data.
**If ESLint reports a GraphQL error** (e.g. `Cannot query field`, `Unknown type`, `Unknown argument`), the field or type name is wrong. Re-run the schema search script to find the correct name — do not guess:
```bash
# From project root — re-check the entity that caused the error
bash .a4drules/skills/using-salesforce-data/graphql-search.sh <EntityName>
```
Then fix the query using the exact names from the script output.
---
## Webapp Integration (React)
```typescript
import { createDataSDK, gql } from "@salesforce/sdk-data";
const GET_ACCOUNTS = gql`
query GetAccounts {
uiapi {
query {
Account(first: 10) {
edges {
node {
Id
Name @optional { value }
Industry @optional { value }
}
}
}
}
}
}
`;
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("; "));
}
const accounts = response?.data?.uiapi?.query?.Account?.edges?.map(e => e.node) ?? [];
```
---
## REST API Patterns
Use `sdk.fetch` when GraphQL is insufficient. See the [Supported APIs](#supported-apis) table for the full allowlist.
```typescript
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 }),
});
```
**Current user**: Do not use Chatter (`/chatter/users/me`). Use GraphQL instead:
```typescript
const GET_CURRENT_USER = gql`
query CurrentUser {
uiapi { currentUser { Id Name { value } } }
}
`;
const response = await sdk.graphql?.(GET_CURRENT_USER);
```
---
## Directory Structure
```
<project-root>/ ← SFDX project root
├── schema.graphql ← grep target (lives here)
├── sfdx-project.json
└── force-app/main/default/webapplications/<app-name>/ ← webapp dir
├── package.json ← npm scripts
└── src/
```
| Command | Run From | Why |
|---------|----------|-----|
| `npm run graphql:schema` | webapp dir | Script in webapp's package.json |
| `npx eslint <file>` | webapp dir | Reads eslint.config.js |
| `bash .a4drules/skills/using-salesforce-data/graphql-search.sh <Entity>` | project root | Schema lookup |
| `sf api request rest` | project root | Needs sfdx-project.json |
---
## Quick Reference
### Schema Lookup (from project root)
Run the search script to get all relevant schema info in one step:
```bash
bash .a4drules/skills/using-salesforce-data/graphql-search.sh <EntityName>
```
| Script Output Section | Used For |
|-----------------------|----------|
| Type definition | Field names, parent/child relationships |
| Filter options | `where:` conditions |
| Sort options | `orderBy:` |
| CreateRepresentation | Create mutation field list |
| UpdateRepresentation | Update mutation field list |
### Error Categories
| Error Contains | Resolution |
|----------------|------------|
| `Cannot query field` | Field name is wrong — run `graphql-search.sh <Entity>` and use the exact name from the Type definition section |
| `Unknown type` | Type name is wrong — run `graphql-search.sh <Entity>` to confirm the correct PascalCase entity name |
| `Unknown argument` | Argument name is wrong — run `graphql-search.sh <Entity>` and check Filter or OrderBy sections |
| `invalid syntax` | Fix syntax per error message |
| `validation error` | Field name is wrong — run `graphql-search.sh <Entity>` to verify |
| `VariableTypeMismatch` | Correct argument type from schema |
| `invalid cross reference id` | Entity deleted — ask for valid Id |
### Checklist
- [ ] All field names verified via search script (Step 2)
- [ ] `@optional` applied to record fields (reads)
- [ ] Optional chaining in consuming code
- [ ] Lint passes: `npx eslint <file>`

View File

@ -0,0 +1,139 @@
#!/usr/bin/env bash
# graphql-search.sh — Look up one or more Salesforce entities in schema.graphql.
#
# Run from the SFDX project root (where schema.graphql lives):
# bash .a4drules/skills/using-salesforce-data/graphql-search.sh Account
# bash .a4drules/skills/using-salesforce-data/graphql-search.sh Account Contact Opportunity
#
# Pass a custom schema path with -s / --schema:
# bash .a4drules/skills/using-salesforce-data/graphql-search.sh -s /path/to/schema.graphql Account
# bash .a4drules/skills/using-salesforce-data/graphql-search.sh --schema ./other/schema.graphql Account Contact
#
# Output sections per entity:
# 1. Type definition — all fields and relationships
# 2. Filter options — <Entity>_Filter input (for `where:`)
# 3. Sort options — <Entity>_OrderBy input (for `orderBy:`)
# 4. Create input — <Entity>CreateRepresentation (for create mutations)
# 5. Update input — <Entity>UpdateRepresentation (for update mutations)
SCHEMA="./schema.graphql"
# ── Argument parsing ─────────────────────────────────────────────────────────
while [[ $# -gt 0 ]]; do
case "$1" in
-s|--schema)
if [[ -z "${2-}" || "$2" == -* ]]; then
echo "ERROR: --schema requires a file path argument"
exit 1
fi
SCHEMA="$2"
shift 2
;;
--)
shift
break
;;
-*)
echo "ERROR: Unknown option: $1"
echo "Usage: bash $0 [-s <schema-path>] <EntityName> [EntityName2 ...]"
exit 1
;;
*)
break
;;
esac
done
if [ $# -eq 0 ]; then
echo "Usage: bash $0 [-s <schema-path>] <EntityName> [EntityName2 ...]"
echo "Example: bash $0 Account"
echo "Example: bash $0 Account Contact Opportunity"
echo "Example: bash $0 --schema /path/to/schema.graphql Account"
exit 1
fi
if [ ! -f "$SCHEMA" ]; then
echo "ERROR: schema.graphql not found at $SCHEMA"
echo " Make sure you are running from the SFDX project root, or pass the path explicitly:"
echo " bash $0 --schema <path/to/schema.graphql> <EntityName>"
echo " If the file is missing entirely, generate it from the webapp dir:"
echo " cd force-app/main/default/webapplications/<app-name> && npm run graphql:schema"
exit 1
fi
# ── Helper: extract lines from a grep match through the closing brace ────────
# Prints up to MAX_LINES lines after (and including) the first match of PATTERN.
# Uses a generous line count — blocks are always closed by a "}" line.
extract_block() {
local label="$1"
local pattern="$2"
local max_lines="$3"
local match
match=$(grep -nE "$pattern" "$SCHEMA" | head -1)
if [ -z "$match" ]; then
echo " (not found: $pattern)"
return
fi
echo "### $label"
grep -E "$pattern" "$SCHEMA" -A "$max_lines" | \
awk '/^\}$/{print; exit} {print}' | \
head -n "$max_lines"
echo ""
}
# ── Main loop ────────────────────────────────────────────────────────────────
for ENTITY in "$@"; do
echo ""
echo "======================================================================"
echo " SCHEMA LOOKUP: $ENTITY"
echo "======================================================================"
echo ""
# 1. Type definition — all fields and relationships
extract_block \
"Type definition — fields and relationships" \
"^type ${ENTITY} implements Record" \
200
# 2. Filter input — used in `where:` arguments
extract_block \
"Filter options — use in where: { ... }" \
"^input ${ENTITY}_Filter" \
100
# 3. OrderBy input — used in `orderBy:` arguments
extract_block \
"Sort options — use in orderBy: { ... }" \
"^input ${ENTITY}_OrderBy" \
60
# 4. Create mutation inputs
extract_block \
"Create mutation wrapper — ${ENTITY}CreateInput" \
"^input ${ENTITY}CreateInput" \
10
extract_block \
"Create mutation fields — ${ENTITY}CreateRepresentation" \
"^input ${ENTITY}CreateRepresentation" \
100
# 5. Update mutation inputs
extract_block \
"Update mutation wrapper — ${ENTITY}UpdateInput" \
"^input ${ENTITY}UpdateInput" \
10
extract_block \
"Update mutation fields — ${ENTITY}UpdateRepresentation" \
"^input ${ENTITY}UpdateRepresentation" \
100
echo ""
done