@salesforce/webapp-template-base-sfdx-project-experimental 1.110.1 → 1.112.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.a4drules/skills/creating-webapp/SKILL.md +1 -2
- package/.a4drules/skills/deploying-to-salesforce/SKILL.md +1 -2
- package/.a4drules/skills/using-salesforce-data/SKILL.md +268 -0
- package/CHANGELOG.md +19 -0
- package/package.json +1 -1
- package/.a4drules/skills/accessing-data/SKILL.md +0 -178
- package/.a4drules/skills/exploring-graphql-schema/SKILL.md +0 -149
- package/.a4drules/skills/fetching-rest-api/SKILL.md +0 -167
- package/.a4drules/skills/generating-graphql-mutation-query/SKILL.md +0 -258
- package/.a4drules/skills/generating-graphql-read-query/SKILL.md +0 -253
- package/.a4drules/skills/using-graphql/SKILL.md +0 -324
- package/.a4drules/skills/using-graphql/shared-schema.graphqls +0 -1150
|
@@ -111,7 +111,7 @@ Apps run behind dynamic base paths. Router navigation (`<Link to>`, `navigate()`
|
|
|
111
111
|
|
|
112
112
|
## Module Restrictions
|
|
113
113
|
|
|
114
|
-
React apps must NOT import Salesforce platform modules like `lightning/*` or `@wire` (LWC-only). For data access, invoke the **
|
|
114
|
+
React apps must NOT import Salesforce platform modules like `lightning/*` or `@wire` (LWC-only). For data access, invoke the **using-salesforce-data** skill.
|
|
115
115
|
|
|
116
116
|
# Frontend Aesthetics
|
|
117
117
|
|
|
@@ -138,4 +138,3 @@ Only stop when:
|
|
|
138
138
|
- All checklist items are completed and quality gates pass, or
|
|
139
139
|
- A blocking error cannot be resolved after reasonable remediation, or
|
|
140
140
|
- The user explicitly asks to pause.
|
|
141
|
-
|
|
@@ -225,5 +225,4 @@ The project includes `scripts/setup-cli.mjs` which runs this sequence in batch.
|
|
|
225
225
|
|
|
226
226
|
## Related Skills
|
|
227
227
|
|
|
228
|
-
- **
|
|
229
|
-
- **using-graphql** — Full GraphQL workflow (explore, query, codegen, lint)
|
|
228
|
+
- **using-salesforce-data** — Full data access workflow (GraphQL queries/mutations, REST APIs, schema exploration, webapp integration)
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: using-salesforce-data
|
|
3
|
+
description: Salesforce data access for reads, mutations, and REST APIs. Use when the user wants to fetch, display, create, update, or delete Salesforce records; add data fetching to a component; query Accounts, Contacts, Opportunities, or custom objects; call Chatter, Connect, or Apex REST APIs; or integrate Salesforce data into a webapp.
|
|
4
|
+
paths:
|
|
5
|
+
- "**/*.ts"
|
|
6
|
+
- "**/*.tsx"
|
|
7
|
+
- "**/*.graphql"
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Salesforce Data Access
|
|
11
|
+
|
|
12
|
+
## When to Use
|
|
13
|
+
|
|
14
|
+
Use this skill when the user wants to:
|
|
15
|
+
|
|
16
|
+
- **Fetch or display Salesforce data** — Query records (Account, Contact, Opportunity, custom objects) to show in a component
|
|
17
|
+
- **Create, update, or delete records** — Perform mutations on Salesforce data
|
|
18
|
+
- **Add data fetching to a component** — Wire up a React component to Salesforce data
|
|
19
|
+
- **Call REST APIs** — Use Chatter, Connect, Apex REST, or UI API endpoints
|
|
20
|
+
- **Explore the org schema** — Discover available objects, fields, or relationships
|
|
21
|
+
|
|
22
|
+
## Data SDK Requirement
|
|
23
|
+
|
|
24
|
+
> **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.
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
import { createDataSDK, gql } from "@salesforce/sdk-data";
|
|
28
|
+
|
|
29
|
+
const sdk = await createDataSDK();
|
|
30
|
+
|
|
31
|
+
// GraphQL for record queries/mutations (PREFERRED)
|
|
32
|
+
const response = await sdk.graphql?.<ResponseType>(query, variables);
|
|
33
|
+
|
|
34
|
+
// REST for Chatter, Connect, Apex REST (when GraphQL insufficient)
|
|
35
|
+
const res = await sdk.fetch?.("/services/data/v65.0/chatter/users/me");
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
**Always use optional chaining** (`sdk.graphql?.()`, `sdk.fetch?.()`) — these methods may be undefined in some surfaces.
|
|
39
|
+
|
|
40
|
+
## Decision: GraphQL vs REST
|
|
41
|
+
|
|
42
|
+
| Need | Method | Example |
|
|
43
|
+
|------|--------|---------|
|
|
44
|
+
| Query/mutate records | `sdk.graphql` | Account, Contact, custom objects |
|
|
45
|
+
| Chatter API | `sdk.fetch` | `/chatter/users/me` |
|
|
46
|
+
| Connect REST | `sdk.fetch` | `/connect/file/upload/config` |
|
|
47
|
+
| Apex REST | `sdk.fetch` | `/services/apexrest/auth/login` |
|
|
48
|
+
| Einstein LLM | `sdk.fetch` | `/einstein/llm/prompt/generations` |
|
|
49
|
+
|
|
50
|
+
**GraphQL is preferred** for record operations. Use REST only when GraphQL doesn't cover the use case.
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## GraphQL Workflow
|
|
55
|
+
|
|
56
|
+
### Step 1: Acquire Schema
|
|
57
|
+
|
|
58
|
+
The `schema.graphql` file (265K+ lines) is the source of truth. **Use grep only** — never open or parse the file directly.
|
|
59
|
+
|
|
60
|
+
1. Check if `schema.graphql` exists at the SFDX project root
|
|
61
|
+
2. If missing, run from the **webapp dir**: `npm run graphql:schema`
|
|
62
|
+
3. Custom objects appear only after metadata is deployed
|
|
63
|
+
|
|
64
|
+
### Step 2: Identify & Introspect Entities
|
|
65
|
+
|
|
66
|
+
Map user intent to PascalCase names ("accounts" → `Account`). Validate via grep:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
# From project root — find entity fields
|
|
70
|
+
grep -nE '^type Account implements Record' ./schema.graphql -A 100
|
|
71
|
+
|
|
72
|
+
# Find filter options
|
|
73
|
+
grep -nE '^input Account_Filter' ./schema.graphql -A 50
|
|
74
|
+
|
|
75
|
+
# Find mutation input
|
|
76
|
+
grep -nE '^input AccountCreateInput' ./schema.graphql -A 50
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
**Maximum 3 introspection cycles.** If entities remain unknown, ask the user.
|
|
80
|
+
|
|
81
|
+
### Step 3: Generate Query
|
|
82
|
+
|
|
83
|
+
Use the templates below. Every field name **must** be verified via grep.
|
|
84
|
+
|
|
85
|
+
#### Read Query Template
|
|
86
|
+
|
|
87
|
+
```graphql
|
|
88
|
+
query GetAccounts {
|
|
89
|
+
uiapi {
|
|
90
|
+
query {
|
|
91
|
+
Account(where: { Industry: { eq: "Technology" } }, first: 10) {
|
|
92
|
+
edges {
|
|
93
|
+
node {
|
|
94
|
+
Id
|
|
95
|
+
Name @optional { value }
|
|
96
|
+
Industry @optional { value }
|
|
97
|
+
# Parent relationship
|
|
98
|
+
Owner @optional { Name { value } }
|
|
99
|
+
# Child relationship
|
|
100
|
+
Contacts @optional {
|
|
101
|
+
edges { node { Name @optional { value } } }
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
**FLS Resilience**: Apply `@optional` to all record fields. The server omits inaccessible fields instead of failing. Consuming code must use optional chaining:
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
const name = node.Name?.value ?? "";
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
#### Mutation Template
|
|
118
|
+
|
|
119
|
+
```graphql
|
|
120
|
+
mutation CreateAccount($input: AccountCreateInput!) {
|
|
121
|
+
uiapi {
|
|
122
|
+
AccountCreate(input: $input) {
|
|
123
|
+
Record { Id Name { value } }
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
**Mutation constraints:**
|
|
130
|
+
- Create: Include required fields, only `createable` fields, no child relationships
|
|
131
|
+
- Update: Include `Id`, only `updateable` fields
|
|
132
|
+
- Delete: Include `Id` only
|
|
133
|
+
|
|
134
|
+
### Step 4: Validate & Test
|
|
135
|
+
|
|
136
|
+
1. **Lint**: `npx eslint <file>` from webapp dir
|
|
137
|
+
2. **Test**: Ask user before testing. For mutations, request input values — never fabricate data.
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
# From project root
|
|
141
|
+
sf api request rest /services/data/v65.0/graphql \
|
|
142
|
+
--method POST \
|
|
143
|
+
--body '{"query":"..."}'
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## Webapp Integration (React)
|
|
149
|
+
|
|
150
|
+
### Pattern 1: External .graphql File (Recommended)
|
|
151
|
+
|
|
152
|
+
```typescript
|
|
153
|
+
import { createDataSDK, type NodeOfConnection } from "@salesforce/sdk-data";
|
|
154
|
+
import GET_ACCOUNTS from "./query/getAccounts.graphql?raw";
|
|
155
|
+
import type { GetAccountsQuery } from "../graphql-operations-types";
|
|
156
|
+
|
|
157
|
+
const sdk = await createDataSDK();
|
|
158
|
+
const response = await sdk.graphql?.<GetAccountsQuery>(GET_ACCOUNTS);
|
|
159
|
+
|
|
160
|
+
if (response?.errors?.length) {
|
|
161
|
+
throw new Error(response.errors.map(e => e.message).join("; "));
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const accounts = response?.data?.uiapi?.query?.Account?.edges?.map(e => e.node) ?? [];
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
After creating/modifying `.graphql` files, run: `npm run graphql:codegen`
|
|
168
|
+
|
|
169
|
+
### Pattern 2: Inline gql Tag
|
|
170
|
+
|
|
171
|
+
```typescript
|
|
172
|
+
import { createDataSDK, gql } from "@salesforce/sdk-data";
|
|
173
|
+
|
|
174
|
+
const GET_CURRENT_USER = gql`
|
|
175
|
+
query CurrentUser {
|
|
176
|
+
uiapi { currentUser { Id Name { value } } }
|
|
177
|
+
}
|
|
178
|
+
`;
|
|
179
|
+
|
|
180
|
+
const response = await sdk.graphql?.<CurrentUserQuery>(GET_CURRENT_USER);
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
**Must use `gql` tag** — plain template strings bypass ESLint validation.
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
## REST API Patterns
|
|
188
|
+
|
|
189
|
+
Use `sdk.fetch` when GraphQL is insufficient:
|
|
190
|
+
|
|
191
|
+
```typescript
|
|
192
|
+
declare const __SF_API_VERSION__: string;
|
|
193
|
+
const API_VERSION = typeof __SF_API_VERSION__ !== "undefined" ? __SF_API_VERSION__ : "65.0";
|
|
194
|
+
|
|
195
|
+
// Chatter — current user
|
|
196
|
+
const res = await sdk.fetch?.(`/services/data/v${API_VERSION}/chatter/users/me`);
|
|
197
|
+
|
|
198
|
+
// Connect — file upload config
|
|
199
|
+
const res = await sdk.fetch?.(`/services/data/v${API_VERSION}/connect/file/upload/config`);
|
|
200
|
+
|
|
201
|
+
// Apex REST (no version in path)
|
|
202
|
+
const res = await sdk.fetch?.("/services/apexrest/auth/login", {
|
|
203
|
+
method: "POST",
|
|
204
|
+
body: JSON.stringify({ email, password }),
|
|
205
|
+
headers: { "Content-Type": "application/json" },
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
// Einstein LLM
|
|
209
|
+
const res = await sdk.fetch?.(`/services/data/v${API_VERSION}/einstein/llm/prompt/generations`, {
|
|
210
|
+
method: "POST",
|
|
211
|
+
body: JSON.stringify({ promptTextorId: prompt }),
|
|
212
|
+
});
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
---
|
|
216
|
+
|
|
217
|
+
## Directory Structure
|
|
218
|
+
|
|
219
|
+
```
|
|
220
|
+
<project-root>/ ← SFDX project root
|
|
221
|
+
├── schema.graphql ← grep target (lives here)
|
|
222
|
+
├── sfdx-project.json
|
|
223
|
+
└── force-app/main/default/webapplications/<app-name>/ ← webapp dir
|
|
224
|
+
├── package.json ← npm scripts
|
|
225
|
+
├── codegen.yml
|
|
226
|
+
└── src/
|
|
227
|
+
└── api/graphql-operations-types.ts ← generated types
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
| Command | Run From | Why |
|
|
231
|
+
|---------|----------|-----|
|
|
232
|
+
| `npm run graphql:schema` | webapp dir | Script in webapp's package.json |
|
|
233
|
+
| `npm run graphql:codegen` | webapp dir | Reads codegen.yml |
|
|
234
|
+
| `npx eslint <file>` | webapp dir | Reads eslint.config.js |
|
|
235
|
+
| `grep ... schema.graphql` | project root | Schema lives at root |
|
|
236
|
+
| `sf api request rest` | project root | Needs sfdx-project.json |
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
## Quick Reference
|
|
241
|
+
|
|
242
|
+
### Grep Patterns (from project root)
|
|
243
|
+
|
|
244
|
+
| Pattern | Context | Purpose |
|
|
245
|
+
|---------|---------|---------|
|
|
246
|
+
| `^type <Entity> implements Record` | `-A 100` | Entity fields |
|
|
247
|
+
| `^input <Entity>_Filter` | `-A 50` | Filter options |
|
|
248
|
+
| `^input <Entity>_OrderBy` | `-A 30` | Sort options |
|
|
249
|
+
| `^input <Entity>CreateInput` | `-A 50` | Create mutation input |
|
|
250
|
+
| `^input <Entity>UpdateInput` | `-A 50` | Update mutation input |
|
|
251
|
+
|
|
252
|
+
### Error Categories
|
|
253
|
+
|
|
254
|
+
| Error Contains | Resolution |
|
|
255
|
+
|----------------|------------|
|
|
256
|
+
| `invalid syntax` | Fix syntax per error message |
|
|
257
|
+
| `validation error` | Re-grep to verify field name |
|
|
258
|
+
| `VariableTypeMismatch` | Correct argument type from schema |
|
|
259
|
+
| `invalid cross reference id` | Entity deleted — ask for valid Id |
|
|
260
|
+
|
|
261
|
+
### Checklist
|
|
262
|
+
|
|
263
|
+
- [ ] All field names verified via grep
|
|
264
|
+
- [ ] `@optional` applied to record fields (reads)
|
|
265
|
+
- [ ] Optional chaining in consuming code
|
|
266
|
+
- [ ] `gql` tag used (not plain strings)
|
|
267
|
+
- [ ] Lint passes: `npx eslint <file>`
|
|
268
|
+
- [ ] Types generated: `npm run graphql:codegen`
|
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,25 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
# [1.112.0](https://github.com/salesforce-experience-platform-emu/webapps/compare/v1.111.0...v1.112.0) (2026-03-20)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* unify 4 GraphQL skills into single using-graphql skill ([#325](https://github.com/salesforce-experience-platform-emu/webapps/issues/325)) ([3935407](https://github.com/salesforce-experience-platform-emu/webapps/commit/393540779c2da76d7f181f62738aa3d4613a2805))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
# [1.111.0](https://github.com/salesforce-experience-platform-emu/webapps/compare/v1.110.1...v1.111.0) (2026-03-19)
|
|
18
|
+
|
|
19
|
+
**Note:** Version bump only for package @salesforce/webapp-template-base-sfdx-project-experimental
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
6
25
|
## [1.110.1](https://github.com/salesforce-experience-platform-emu/webapps/compare/v1.110.0...v1.110.1) (2026-03-19)
|
|
7
26
|
|
|
8
27
|
|
package/package.json
CHANGED
|
@@ -1,178 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: accessing-data
|
|
3
|
-
description: Salesforce data access patterns. Use when adding or modifying any code that fetches data from Salesforce (records, Chatter, Connect API, etc.).
|
|
4
|
-
paths:
|
|
5
|
-
- "**/*.ts"
|
|
6
|
-
- "**/*.tsx"
|
|
7
|
-
- "**/*.graphql"
|
|
8
|
-
---
|
|
9
|
-
|
|
10
|
-
# Salesforce Data Access
|
|
11
|
-
|
|
12
|
-
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.
|
|
13
|
-
|
|
14
|
-
## Mandatory: Use the Data SDK
|
|
15
|
-
|
|
16
|
-
> **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.
|
|
17
|
-
|
|
18
|
-
## Optional Chaining and Graceful Handling
|
|
19
|
-
|
|
20
|
-
**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:
|
|
21
|
-
|
|
22
|
-
```typescript
|
|
23
|
-
const sdk = await createDataSDK();
|
|
24
|
-
|
|
25
|
-
// ✅ Use optional chaining
|
|
26
|
-
const response = await sdk.graphql?.(query);
|
|
27
|
-
|
|
28
|
-
// ✅ Check before using fetch
|
|
29
|
-
if (!sdk.fetch) {
|
|
30
|
-
throw new Error("Data SDK fetch is not available in this context");
|
|
31
|
-
}
|
|
32
|
-
const res = await sdk.fetch(url);
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
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.
|
|
36
|
-
|
|
37
|
-
## Preference: GraphQL First
|
|
38
|
-
|
|
39
|
-
**GraphQL is the preferred method** for querying and mutating Salesforce records. Use it when:
|
|
40
|
-
|
|
41
|
-
- Querying records (Account, Contact, Opportunity, custom objects)
|
|
42
|
-
- Creating, updating, or deleting records (when GraphQL supports the operation)
|
|
43
|
-
- Fetching related data, filters, sorting, pagination
|
|
44
|
-
|
|
45
|
-
**Use `sdk.fetch` only when GraphQL is not sufficient.** For REST API usage, invoke the `fetching-rest-api` skill, which documents:
|
|
46
|
-
|
|
47
|
-
- Chatter API (e.g., `/services/data/v65.0/chatter/users/me`)
|
|
48
|
-
- Connect REST API (e.g., `/services/data/v65.0/connect/file/upload/config`)
|
|
49
|
-
- Apex REST (e.g., `/services/apexrest/auth/login`)
|
|
50
|
-
- UI API REST (e.g., `/services/data/v65.0/ui-api/records/{recordId}`)
|
|
51
|
-
- Einstein LLM Gateway
|
|
52
|
-
|
|
53
|
-
---
|
|
54
|
-
|
|
55
|
-
## Getting the SDK
|
|
56
|
-
|
|
57
|
-
```typescript
|
|
58
|
-
import { createDataSDK } from "@salesforce/sdk-data";
|
|
59
|
-
|
|
60
|
-
const sdk = await createDataSDK();
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
---
|
|
64
|
-
|
|
65
|
-
## Example 1: GraphQL (Preferred)
|
|
66
|
-
|
|
67
|
-
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).
|
|
68
|
-
|
|
69
|
-
```typescript
|
|
70
|
-
import { createDataSDK, gql } from "@salesforce/sdk-data";
|
|
71
|
-
import type { GetAccountsQuery } from "../graphql-operations-types";
|
|
72
|
-
|
|
73
|
-
const GET_ACCOUNTS = gql`
|
|
74
|
-
query GetAccounts {
|
|
75
|
-
uiapi {
|
|
76
|
-
query {
|
|
77
|
-
Account(first: 10) {
|
|
78
|
-
edges {
|
|
79
|
-
node {
|
|
80
|
-
Id
|
|
81
|
-
Name { value }
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
`;
|
|
89
|
-
|
|
90
|
-
export async function getAccounts() {
|
|
91
|
-
const sdk = await createDataSDK();
|
|
92
|
-
const response = await sdk.graphql?.<GetAccountsQuery>(GET_ACCOUNTS);
|
|
93
|
-
|
|
94
|
-
if (response?.errors?.length) {
|
|
95
|
-
throw new Error(response.errors.map((e) => e.message).join("; "));
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
return response?.data?.uiapi?.query?.Account?.edges?.map((e) => e?.node) ?? [];
|
|
99
|
-
}
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
---
|
|
103
|
-
|
|
104
|
-
## Example 2: Fetch (When GraphQL Is Not Sufficient)
|
|
105
|
-
|
|
106
|
-
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.
|
|
107
|
-
|
|
108
|
-
```typescript
|
|
109
|
-
import { createDataSDK } from "@salesforce/sdk-data";
|
|
110
|
-
|
|
111
|
-
declare const __SF_API_VERSION__: string;
|
|
112
|
-
const API_VERSION = typeof __SF_API_VERSION__ !== "undefined" ? __SF_API_VERSION__ : "65.0";
|
|
113
|
-
|
|
114
|
-
export async function getCurrentUser() {
|
|
115
|
-
const sdk = await createDataSDK();
|
|
116
|
-
const response = await sdk.fetch?.(`/services/data/v${API_VERSION}/chatter/users/me`);
|
|
117
|
-
|
|
118
|
-
if (!response?.ok) throw new Error(`HTTP ${response?.status}`);
|
|
119
|
-
const data = await response.json();
|
|
120
|
-
return { id: data.id, name: data.name };
|
|
121
|
-
}
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
---
|
|
125
|
-
|
|
126
|
-
## Anti-Patterns (Forbidden)
|
|
127
|
-
|
|
128
|
-
### Direct fetch to Salesforce
|
|
129
|
-
|
|
130
|
-
```typescript
|
|
131
|
-
// ❌ FORBIDDEN — bypasses Data SDK auth and CSRF
|
|
132
|
-
const res = await fetch("/services/data/v65.0/chatter/users/me");
|
|
133
|
-
```
|
|
134
|
-
|
|
135
|
-
### Direct axios to Salesforce
|
|
136
|
-
|
|
137
|
-
```typescript
|
|
138
|
-
// ❌ FORBIDDEN — bypasses Data SDK
|
|
139
|
-
const res = await axios.get("/services/data/v65.0/chatter/users/me");
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
### Correct approach
|
|
143
|
-
|
|
144
|
-
```typescript
|
|
145
|
-
// ✅ CORRECT — use Data SDK
|
|
146
|
-
const sdk = await createDataSDK();
|
|
147
|
-
const res = await sdk.fetch?.("/services/data/v65.0/chatter/users/me");
|
|
148
|
-
```
|
|
149
|
-
|
|
150
|
-
---
|
|
151
|
-
|
|
152
|
-
## Clarifying Vague Data Requests
|
|
153
|
-
|
|
154
|
-
When a user asks about data and the request is vague, **clarify before implementing**. Ask which of the following they want:
|
|
155
|
-
|
|
156
|
-
- **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)
|
|
157
|
-
- **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
|
|
158
|
-
- **Local example data** — Update or add local fixture/example data files (e.g., JSON in `data/`) for development or testing
|
|
159
|
-
- **Other** — Data export, report generation, setup script, etc.
|
|
160
|
-
|
|
161
|
-
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.
|
|
162
|
-
|
|
163
|
-
---
|
|
164
|
-
|
|
165
|
-
## Decision Flow
|
|
166
|
-
|
|
167
|
-
1. **Need to query or mutate Salesforce records?** → Use GraphQL via the Data SDK. Invoke the `using-graphql` skill.
|
|
168
|
-
2. **Need Chatter, Connect REST, Apex REST, UI API REST, or Einstein LLM?** → Use `sdk.fetch`. Invoke the `fetching-rest-api` skill.
|
|
169
|
-
3. **Never** use `fetch`, `axios`, or similar directly for Salesforce API calls.
|
|
170
|
-
|
|
171
|
-
---
|
|
172
|
-
|
|
173
|
-
## Reference
|
|
174
|
-
|
|
175
|
-
- GraphQL workflow: invoke the `using-graphql` skill (`.a4drules/skills/using-graphql/`)
|
|
176
|
-
- REST API via fetch: invoke the `fetching-rest-api` skill (`.a4drules/skills/fetching-rest-api/`)
|
|
177
|
-
- Data SDK package: `@salesforce/sdk-data` (`createDataSDK`, `gql`, `NodeOfConnection`)
|
|
178
|
-
- `createRecord` for UI API record creation: `@salesforce/webapp-experimental/api` (uses Data SDK internally)
|
|
@@ -1,149 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: exploring-graphql-schema
|
|
3
|
-
description: Explore the Salesforce GraphQL schema via grep-only lookups. Use before generating any GraphQL query — schema exploration must complete first.
|
|
4
|
-
paths:
|
|
5
|
-
- "**/*.ts"
|
|
6
|
-
- "**/*.tsx"
|
|
7
|
-
- "**/*.graphql"
|
|
8
|
-
---
|
|
9
|
-
|
|
10
|
-
# Salesforce GraphQL Schema Exploration
|
|
11
|
-
|
|
12
|
-
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.
|
|
13
|
-
|
|
14
|
-
## Deployment Prerequisites
|
|
15
|
-
|
|
16
|
-
The schema reflects the **current org state**. Custom objects and fields appear only after metadata is deployed.
|
|
17
|
-
|
|
18
|
-
- **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.
|
|
19
|
-
- **After** any metadata deployment: Re-run `npm run graphql:schema` and `npm run graphql:codegen` so types and queries stay in sync.
|
|
20
|
-
|
|
21
|
-
## Schema File Location
|
|
22
|
-
|
|
23
|
-
**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.
|
|
24
|
-
|
|
25
|
-
> ⚠️ **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.
|
|
26
|
-
|
|
27
|
-
If the file is not present, generate it by running (from the **webapp dir**, not the project root):
|
|
28
|
-
|
|
29
|
-
```bash
|
|
30
|
-
# Run from webapp dir (force-app/main/default/webapplications/<app-name>/)
|
|
31
|
-
npm run graphql:schema
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
**BEFORE generating any GraphQL query, you MUST:**
|
|
35
|
-
|
|
36
|
-
1. **Check if schema exists**: Look for `schema.graphql` in the **SFDX project root**
|
|
37
|
-
2. **If schema is missing**:
|
|
38
|
-
- `cd` to the **webapp dir** and run `npm run graphql:schema` to download it
|
|
39
|
-
- Wait for the command to complete successfully
|
|
40
|
-
- Then proceed with grep-only lookups as defined below.
|
|
41
|
-
3. **If schema exists**: Proceed with targeted searches as described below
|
|
42
|
-
|
|
43
|
-
> ⚠️ **DO NOT** generate GraphQL queries without first having access to the schema. Standard field assumptions may not match the target org's configuration.
|
|
44
|
-
|
|
45
|
-
## Schema Structure Overview
|
|
46
|
-
|
|
47
|
-
Main entry points: `Query { uiapi }` for reads; `Mutation { uiapi(input: ...) }` for creates/updates/deletes. Record queries use `uiapi.query.<ObjectName>`.
|
|
48
|
-
|
|
49
|
-
## Allowed Lookups (grep-only)
|
|
50
|
-
|
|
51
|
-
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).
|
|
52
|
-
|
|
53
|
-
- Always include:
|
|
54
|
-
- `-n` (line numbers) and `-E` (extended regex)
|
|
55
|
-
- Anchors (`^`) and word boundaries (`\b`)
|
|
56
|
-
- Minimal context with `-A N` (prefer the smallest N that surfaces the needed lines)
|
|
57
|
-
|
|
58
|
-
### 1. Find Available Fields for a Record Type
|
|
59
|
-
|
|
60
|
-
Search for `type <ObjectName> implements Record` to find all queryable fields:
|
|
61
|
-
|
|
62
|
-
```bash
|
|
63
|
-
# Example: Find Account fields (anchored, minimal context)
|
|
64
|
-
grep -nE '^type[[:space:]]+Account[[:space:]]+implements[[:space:]]+Record\b' ./schema.graphql -A 60
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
### 2. Find Filter Options for a Record Type
|
|
68
|
-
|
|
69
|
-
Search for `input <ObjectName>_Filter` to find filterable fields and operators:
|
|
70
|
-
|
|
71
|
-
```bash
|
|
72
|
-
# Example: Find Account filter options (anchored)
|
|
73
|
-
grep -nE '^input[[:space:]]+Account_Filter\b' ./schema.graphql -A 40
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
### 3. Find OrderBy Options
|
|
77
|
-
|
|
78
|
-
Search for `input <ObjectName>_OrderBy` for sorting options:
|
|
79
|
-
|
|
80
|
-
```bash
|
|
81
|
-
# Example: Find Account ordering options (anchored)
|
|
82
|
-
grep -nE '^input[[:space:]]+Account_OrderBy\b' ./schema.graphql -A 30
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
### 4. Find Mutation Operations
|
|
86
|
-
|
|
87
|
-
Search for operations in `UIAPIMutations`:
|
|
88
|
-
|
|
89
|
-
```bash
|
|
90
|
-
# Example: Find Account mutations (extended regex)
|
|
91
|
-
grep -nE 'Account.*(Create|Update|Delete)' ./schema.graphql
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
### 5. Find Input Types for Mutations
|
|
95
|
-
|
|
96
|
-
Search for `input <ObjectName>CreateInput` or `input <ObjectName>UpdateInput`:
|
|
97
|
-
|
|
98
|
-
```bash
|
|
99
|
-
# Example: Find Account create input (anchored)
|
|
100
|
-
grep -nE '^input[[:space:]]+AccountCreateInput\b' ./schema.graphql -A 30
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
## Agent Workflow for Building Queries (grep-only)
|
|
104
|
-
|
|
105
|
-
**Pre-requisites (MANDATORY):**
|
|
106
|
-
|
|
107
|
-
- [ ] Verified `schema.graphql` exists in the **SFDX project root**
|
|
108
|
-
- [ ] If missing, ran `npm run graphql:schema` from the **webapp dir** and waited for completion
|
|
109
|
-
- [ ] Confirmed connection to correct Salesforce org (if downloading fresh schema)
|
|
110
|
-
|
|
111
|
-
**Workflow Steps:**
|
|
112
|
-
|
|
113
|
-
1. **Identify the target object** (e.g., Account, Contact, Opportunity)
|
|
114
|
-
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)
|
|
115
|
-
3. **Run the "Find Filter Options" grep command** (`<Object>_Filter`) to understand filtering options
|
|
116
|
-
4. **Run the "Find OrderBy Options" grep command** (`<Object>_OrderBy`) for sorting capabilities
|
|
117
|
-
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
|
|
118
|
-
6. **Validate field names** using grep matches (case-sensitive). Do not open or parse the file beyond grep.
|
|
119
|
-
|
|
120
|
-
## Tips for Agents
|
|
121
|
-
|
|
122
|
-
- **Always verify field names** by running the specific grep commands; do not open the schema file
|
|
123
|
-
- **Use grep with anchors and minimal -A context** to explore the schema efficiently—never read or stream the file
|
|
124
|
-
- **Check relationships** by looking for `parentRelationship` and `childRelationship` comments in type definitions
|
|
125
|
-
- **Look for Connection types** (e.g., `AccountConnection`) via grep to understand pagination structure
|
|
126
|
-
- **Custom objects** end with `__c` (e.g., `CustomObject__c`)
|
|
127
|
-
- **Custom fields** also end with `__c` (e.g., `Custom_Field__c`)
|
|
128
|
-
|
|
129
|
-
## Forbidden Operations
|
|
130
|
-
|
|
131
|
-
To prevent accidental large reads, the following are prohibited for schema.graphql:
|
|
132
|
-
|
|
133
|
-
- Opening in any editor (VS Code, vim, nano)
|
|
134
|
-
- Using cat, less, more, head, or tail
|
|
135
|
-
- Programmatic parsing (node, python, awk, sed, jq)
|
|
136
|
-
- Streaming or paginating through large portions of the file
|
|
137
|
-
|
|
138
|
-
If any of the above occurs, stop and replace the action with one of the Allowed Lookups (grep-only).
|
|
139
|
-
|
|
140
|
-
## Output Minimization
|
|
141
|
-
|
|
142
|
-
- Prefer precise, anchored patterns with word boundaries
|
|
143
|
-
- Use the smallest `-A` context that surfaces required lines
|
|
144
|
-
- If results are noisy, refine the regex rather than increasing context
|
|
145
|
-
|
|
146
|
-
## Related Skills
|
|
147
|
-
|
|
148
|
-
- For generating read queries, invoke the `generating-graphql-read-query` skill
|
|
149
|
-
- For generating mutation queries, invoke the `generating-graphql-mutation-query` skill
|