@salesforce/webapp-template-base-sfdx-project-experimental 1.105.1 → 1.106.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/salesforce-data-access/SKILL.md +165 -0
- package/.a4drules/skills/salesforce-graphql/SKILL.md +323 -0
- package/.a4drules/skills/salesforce-graphql/shared-schema.graphqls +1150 -0
- package/.a4drules/skills/salesforce-graphql-explore-schema/SKILL.md +160 -0
- package/.a4drules/skills/salesforce-graphql-mutation-query/SKILL.md +258 -0
- package/.a4drules/skills/salesforce-graphql-read-query/SKILL.md +253 -0
- package/.a4drules/skills/salesforce-rest-api-fetch/SKILL.md +167 -0
- package/.a4drules/webapp-react.md +1 -49
- package/AGENT.md +5 -0
- package/CHANGELOG.md +11 -0
- package/package.json +1 -1
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: salesforce-data-access
|
|
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 `salesforce-rest-api-fetch` 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 `salesforce-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 `salesforce-rest-api-fetch` 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
|
+
## Decision Flow
|
|
153
|
+
|
|
154
|
+
1. **Need to query or mutate Salesforce records?** → Use GraphQL via the Data SDK. Invoke the `salesforce-graphql` skill.
|
|
155
|
+
2. **Need Chatter, Connect REST, Apex REST, UI API REST, or Einstein LLM?** → Use `sdk.fetch`. Invoke the `salesforce-rest-api-fetch` skill.
|
|
156
|
+
3. **Never** use `fetch`, `axios`, or similar directly for Salesforce API calls.
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## Reference
|
|
161
|
+
|
|
162
|
+
- GraphQL workflow: invoke the `salesforce-graphql` skill (`.a4drules/skills/salesforce-graphql/`)
|
|
163
|
+
- REST API via fetch: invoke the `salesforce-rest-api-fetch` skill (`.a4drules/skills/salesforce-rest-api-fetch/`)
|
|
164
|
+
- Data SDK package: `@salesforce/sdk-data` (`createDataSDK`, `gql`, `NodeOfConnection`)
|
|
165
|
+
- `createRecord` for UI API record creation: `@salesforce/webapp-experimental/api` (uses Data SDK internally)
|
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: salesforce-graphql
|
|
3
|
+
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.
|
|
4
|
+
paths:
|
|
5
|
+
- "**/*.ts"
|
|
6
|
+
- "**/*.tsx"
|
|
7
|
+
- "**/*.graphql"
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Salesforce GraphQL
|
|
11
|
+
|
|
12
|
+
Guidance for querying and mutating Salesforce data via the Salesforce GraphQL API. Use `createDataSDK()` + `sdk.graphql?.()` and codegen tooling.
|
|
13
|
+
|
|
14
|
+
## When to Use
|
|
15
|
+
|
|
16
|
+
- User asks to "fetch data from Salesforce"
|
|
17
|
+
- User asks to "query" or "mutate" Salesforce records
|
|
18
|
+
- User wants to add a new GraphQL operation (query or mutation)
|
|
19
|
+
- User asks to add data access for a Salesforce object (Account, Contact, Opportunity, etc.)
|
|
20
|
+
|
|
21
|
+
## Schema Access Policy (GREP ONLY)
|
|
22
|
+
|
|
23
|
+
> **GREP ONLY** — The `schema.graphql` file is very large (~265,000+ lines). All schema lookups **MUST** use the grep-only commands defined in the `salesforce-graphql-explore-schema` skill. Do NOT open, read, stream, or parse `./schema.graphql` with any tool other than grep.
|
|
24
|
+
|
|
25
|
+
## Directory Context
|
|
26
|
+
|
|
27
|
+
The generated app has a two-level directory structure. Commands must run from the correct directory.
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
<project-root>/ ← SFDX project root
|
|
31
|
+
├── schema.graphql ← grep target
|
|
32
|
+
├── sfdx-project.json
|
|
33
|
+
└── force-app/main/default/webapplications/<app-name>/ ← webapp dir
|
|
34
|
+
├── package.json (npm scripts: graphql:schema, graphql:codegen, lint)
|
|
35
|
+
├── eslint.config.js (schema ref: ../../../../../schema.graphql)
|
|
36
|
+
├── codegen.yml (schema ref: ../../../../../schema.graphql)
|
|
37
|
+
└── src/ (source code, .graphql query files)
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
| Command | Run from | Why |
|
|
41
|
+
| ------------------------- | ---------------- | -------------------------------------- |
|
|
42
|
+
| `npm run graphql:schema` | **webapp dir** | Script is in webapp's `package.json` |
|
|
43
|
+
| `npm run graphql:codegen` | **webapp dir** | Reads `codegen.yml` in webapp dir |
|
|
44
|
+
| `npx eslint <file>` | **webapp dir** | Reads `eslint.config.js` in webapp dir |
|
|
45
|
+
| `grep ... schema.graphql` | **project root** | `schema.graphql` lives at project root |
|
|
46
|
+
| `sf api request graphql` | **project root** | Needs `sfdx-project.json` |
|
|
47
|
+
|
|
48
|
+
> **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."
|
|
49
|
+
|
|
50
|
+
## Prerequisites
|
|
51
|
+
|
|
52
|
+
The base React app (`base-react-app`) ships with all GraphQL dependencies and tooling pre-configured:
|
|
53
|
+
|
|
54
|
+
- `@salesforce/sdk-data` — runtime SDK for `createDataSDK` and `gql`
|
|
55
|
+
- `@graphql-codegen/cli` + plugins — type generation from `.graphql` files and inline `gql` queries
|
|
56
|
+
- `@graphql-eslint/eslint-plugin` — validates `.graphql` files and `gql` template literals against `schema.graphql` (used as a query validation gate — see Step 6)
|
|
57
|
+
- `graphql` — shared by codegen, ESLint, and schema introspection
|
|
58
|
+
|
|
59
|
+
Before using this skill, ensure:
|
|
60
|
+
|
|
61
|
+
1. The `@salesforce/sdk-data` package is available (provides `createDataSDK`, `gql`, `NodeOfConnection`)
|
|
62
|
+
2. A `schema.graphql` file exists at the project root. If missing, generate it:
|
|
63
|
+
```bash
|
|
64
|
+
# Run from webapp dir (force-app/main/default/webapplications/<app-name>/)
|
|
65
|
+
npm run graphql:schema
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## npm Scripts
|
|
69
|
+
|
|
70
|
+
- **`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.
|
|
71
|
+
- **`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`.
|
|
72
|
+
|
|
73
|
+
## Workflow
|
|
74
|
+
|
|
75
|
+
### Step 1: Download Schema
|
|
76
|
+
|
|
77
|
+
Ensure `schema.graphql` exists at the project root. If missing, run `npm run graphql:schema` from the webapp dir.
|
|
78
|
+
|
|
79
|
+
### Step 2: Explore the Schema (grep-only)
|
|
80
|
+
|
|
81
|
+
Before writing any query, verify the target object and its fields exist in the schema.
|
|
82
|
+
|
|
83
|
+
**Invoke the `salesforce-graphql-explore-schema` skill** for the full exploration workflow and **mandatory grep-only access policy**.
|
|
84
|
+
|
|
85
|
+
> **GREP ONLY** — All schema lookups MUST use the grep commands defined in the `salesforce-graphql-explore-schema` skill. Do NOT open, read, stream, or parse `./schema.graphql` with any tool other than grep.
|
|
86
|
+
|
|
87
|
+
Key actions (all via grep):
|
|
88
|
+
|
|
89
|
+
- `type <ObjectName> implements Record` — find available fields
|
|
90
|
+
- `input <ObjectName>_Filter` — find filter options
|
|
91
|
+
- `input <ObjectName>_OrderBy` — find sorting options
|
|
92
|
+
- `input <ObjectName>CreateInput` / `<ObjectName>UpdateInput` — find mutation input types
|
|
93
|
+
|
|
94
|
+
### Step 3: Choose the Query Pattern
|
|
95
|
+
|
|
96
|
+
**Pattern 1 — External `.graphql` file** (recommended for complex queries):
|
|
97
|
+
|
|
98
|
+
- Queries with variables, fragments, or shared across files
|
|
99
|
+
- Full codegen support, syntax highlighting, shareable
|
|
100
|
+
- Requires codegen step after changes
|
|
101
|
+
- See example: `api/utils/accounts.ts` + `api/utils/query/highRevenueAccountsQuery.graphql`
|
|
102
|
+
|
|
103
|
+
**Pattern 2 — Inline `gql` tag** (recommended for simple queries):
|
|
104
|
+
|
|
105
|
+
- Simple queries without variables; colocated with usage code
|
|
106
|
+
- Supports dynamic queries (field set varies at runtime)
|
|
107
|
+
- **MUST use `gql` tag** — plain template strings bypass `@graphql-eslint` validation
|
|
108
|
+
- See example: `api/utils/user.ts`
|
|
109
|
+
|
|
110
|
+
### Step 4: Write the Query
|
|
111
|
+
|
|
112
|
+
For **Pattern 1**:
|
|
113
|
+
|
|
114
|
+
1. Create a `.graphql` file under `src/api/utils/query/`
|
|
115
|
+
2. Follow UIAPI structure: `query { uiapi { query { ObjectName(...) { edges { node { ... } } } } } }`
|
|
116
|
+
3. For mutations, invoke the `salesforce-graphql-mutation-query` skill
|
|
117
|
+
4. For read queries, invoke the `salesforce-graphql-read-query` skill
|
|
118
|
+
|
|
119
|
+
For **Pattern 2**:
|
|
120
|
+
|
|
121
|
+
1. Define query inline using the `gql` template tag
|
|
122
|
+
2. Ensure the query name matches what codegen expects
|
|
123
|
+
|
|
124
|
+
### Step 5: Test Queries Against Live Org
|
|
125
|
+
|
|
126
|
+
Use the testing workflows in the `salesforce-graphql-read-query` and `salesforce-graphql-mutation-query` skills to validate queries against the connected org before integrating into the app.
|
|
127
|
+
|
|
128
|
+
### Step 6: Generate Types
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
# Run from webapp dir (force-app/main/default/webapplications/<app-name>/)
|
|
132
|
+
npm run graphql:codegen
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
This updates `src/api/graphql-operations-types.ts` with `<OperationName>Query`/`<OperationName>Mutation` and `<OperationName>QueryVariables`/`<OperationName>MutationVariables`.
|
|
136
|
+
|
|
137
|
+
### Step 7: Lint Validate
|
|
138
|
+
|
|
139
|
+
Run ESLint on the file containing the query to validate it against the schema **before** any live testing:
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
# Run from webapp dir
|
|
143
|
+
npx eslint <path-to-file>
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
The `@graphql-eslint/eslint-plugin` processor extracts GraphQL from `gql` template literals and validates them against `schema.graphql`. Fix all ESLint errors before proceeding.
|
|
147
|
+
|
|
148
|
+
### Step 8: Implement and Verify
|
|
149
|
+
|
|
150
|
+
Implement the data access function using the pattern below. Use the Quality Checklist before completing.
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## Core Types & Function Signatures
|
|
155
|
+
|
|
156
|
+
### createDataSDK and graphql
|
|
157
|
+
|
|
158
|
+
```typescript
|
|
159
|
+
import { createDataSDK } from "@salesforce/sdk-data";
|
|
160
|
+
|
|
161
|
+
const sdk = await createDataSDK();
|
|
162
|
+
const response = await sdk.graphql?.<ResponseType, VariablesType>(query, variables);
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
`createDataSDK()` returns a `DataSDK` instance. The `graphql` method uses optional chaining (`?.`) because not all surfaces support GraphQL.
|
|
166
|
+
|
|
167
|
+
### gql Template Tag
|
|
168
|
+
|
|
169
|
+
```typescript
|
|
170
|
+
import { gql } from "@salesforce/sdk-data";
|
|
171
|
+
|
|
172
|
+
const MY_QUERY = gql`
|
|
173
|
+
query MyQuery {
|
|
174
|
+
uiapi { ... }
|
|
175
|
+
}
|
|
176
|
+
`;
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
The `gql` tag enables ESLint validation against the schema. Plain template strings bypass validation.
|
|
180
|
+
|
|
181
|
+
### Error Handling
|
|
182
|
+
|
|
183
|
+
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).
|
|
184
|
+
|
|
185
|
+
```typescript
|
|
186
|
+
// Default: strict
|
|
187
|
+
if (response?.errors?.length) {
|
|
188
|
+
throw new Error(response.errors.map((e) => e.message).join("; "));
|
|
189
|
+
}
|
|
190
|
+
const result = response?.data;
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
Responses follow `uiapi.query.ObjectName.edges[].node`; fields use `{ value }`.
|
|
194
|
+
|
|
195
|
+
### NodeOfConnection
|
|
196
|
+
|
|
197
|
+
```typescript
|
|
198
|
+
import { type NodeOfConnection } from "@salesforce/sdk-data";
|
|
199
|
+
|
|
200
|
+
type AccountNode = NodeOfConnection<GetHighRevenueAccountsQuery["uiapi"]["query"]["Account"]>;
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
## Pattern 1: External .graphql File
|
|
206
|
+
|
|
207
|
+
Create a `.graphql` file, run `npm run graphql:codegen`, import with `?raw` suffix, and use generated types.
|
|
208
|
+
|
|
209
|
+
**Required imports:**
|
|
210
|
+
|
|
211
|
+
```typescript
|
|
212
|
+
import { createDataSDK, type NodeOfConnection } from "@salesforce/sdk-data";
|
|
213
|
+
import MY_QUERY from "./query/myQuery.graphql?raw"; // ← ?raw suffix required
|
|
214
|
+
import type { GetMyDataQuery, GetMyDataQueryVariables } from "../graphql-operations-types";
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
**When to use:** Complex queries with variables, fragments, or shared across files. Does NOT support dynamic queries (field set varies at runtime).
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
## Pattern 2: Inline gql Tag
|
|
222
|
+
|
|
223
|
+
**Required imports:**
|
|
224
|
+
|
|
225
|
+
```typescript
|
|
226
|
+
import { createDataSDK, gql } from "@salesforce/sdk-data";
|
|
227
|
+
import { type CurrentUserQuery } from "../graphql-operations-types";
|
|
228
|
+
|
|
229
|
+
const MY_QUERY = gql`
|
|
230
|
+
query CurrentUser {
|
|
231
|
+
uiapi { ... }
|
|
232
|
+
}
|
|
233
|
+
`;
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
> **MUST use `gql` tag** — plain template strings bypass the `@graphql-eslint` processor entirely, meaning no lint validation against the schema.
|
|
237
|
+
|
|
238
|
+
**When to use:** Simple, colocated queries. Supports dynamic queries (field set varies at runtime).
|
|
239
|
+
|
|
240
|
+
---
|
|
241
|
+
|
|
242
|
+
## Conditional Field Selection
|
|
243
|
+
|
|
244
|
+
For dynamic fieldsets with **known** fields, use `@include(if: $condition)` and `@skip(if: $condition)` in `.graphql` files. See GraphQL spec for details.
|
|
245
|
+
|
|
246
|
+
---
|
|
247
|
+
|
|
248
|
+
## Anti-Patterns (Not Recommended)
|
|
249
|
+
|
|
250
|
+
### Direct API Calls
|
|
251
|
+
|
|
252
|
+
```typescript
|
|
253
|
+
// NOT RECOMMENDED: Direct axios/fetch calls for GraphQL
|
|
254
|
+
// PREFERRED: Use the Data SDK
|
|
255
|
+
const sdk = await createDataSDK();
|
|
256
|
+
const response = await sdk.graphql?.<ResponseType>(query, variables);
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
### Missing Type Definitions
|
|
260
|
+
|
|
261
|
+
```typescript
|
|
262
|
+
// NOT RECOMMENDED: Untyped GraphQL calls
|
|
263
|
+
// PREFERRED: Provide response type
|
|
264
|
+
const response = await sdk.graphql?.<GetMyDataQuery>(query);
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
### Plain String Queries (Without gql Tag)
|
|
268
|
+
|
|
269
|
+
```typescript
|
|
270
|
+
// NOT RECOMMENDED: Plain strings bypass ESLint validation
|
|
271
|
+
const query = `query { ... }`;
|
|
272
|
+
|
|
273
|
+
// PREFERRED: Use gql tag for inline queries
|
|
274
|
+
const QUERY = gql`query { ... }`;
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
---
|
|
278
|
+
|
|
279
|
+
## Quality Checklist
|
|
280
|
+
|
|
281
|
+
> If you have not completed the workflow above, **stop and complete it first**. Invoke the skill workflow before using this checklist.
|
|
282
|
+
|
|
283
|
+
Before completing GraphQL data access code:
|
|
284
|
+
|
|
285
|
+
### For Pattern 1 (.graphql files):
|
|
286
|
+
|
|
287
|
+
1. [ ] All field names verified via grep against `schema.graphql` (invoke `salesforce-graphql-explore-schema`)
|
|
288
|
+
2. [ ] Create `.graphql` file for the query/mutation
|
|
289
|
+
3. [ ] Run `npm run graphql:codegen` to generate types
|
|
290
|
+
4. [ ] Import query with `?raw` suffix
|
|
291
|
+
5. [ ] Import generated types from `graphql-operations-types.ts`
|
|
292
|
+
6. [ ] Use `sdk.graphql?.<ResponseType>()` with proper generic
|
|
293
|
+
7. [ ] Handle `response.errors` and destructure `response.data`
|
|
294
|
+
8. [ ] Use `NodeOfConnection` for cleaner node types when needed
|
|
295
|
+
9. [ ] Run `npx eslint <file>` from webapp dir — fix all GraphQL errors
|
|
296
|
+
|
|
297
|
+
### For Pattern 2 (inline with gql):
|
|
298
|
+
|
|
299
|
+
1. [ ] All field names verified via grep against `schema.graphql`
|
|
300
|
+
2. [ ] Define query using `gql` template tag (NOT a plain string)
|
|
301
|
+
3. [ ] Ensure query name matches generated types in `graphql-operations-types.ts`
|
|
302
|
+
4. [ ] Import generated types for the query
|
|
303
|
+
5. [ ] Use `sdk.graphql?.<ResponseType>()` with proper generic
|
|
304
|
+
6. [ ] Handle `response.errors` and destructure `response.data`
|
|
305
|
+
7. [ ] Run `npx eslint <file>` from webapp dir — fix all GraphQL errors
|
|
306
|
+
|
|
307
|
+
### General:
|
|
308
|
+
|
|
309
|
+
- [ ] Lint validation passes (`npx eslint <file>` reports no GraphQL errors)
|
|
310
|
+
- [ ] Query field names match the schema exactly (case-sensitive, confirmed via grep)
|
|
311
|
+
- [ ] Response type generic is provided to `sdk.graphql?.<T>()`
|
|
312
|
+
- [ ] Optional chaining is used for nested response data
|
|
313
|
+
|
|
314
|
+
---
|
|
315
|
+
|
|
316
|
+
## Reference
|
|
317
|
+
|
|
318
|
+
- Schema exploration: invoke the `salesforce-graphql-explore-schema` skill
|
|
319
|
+
- Read query generation: invoke the `salesforce-graphql-read-query` skill
|
|
320
|
+
- Mutation query generation: invoke the `salesforce-graphql-mutation-query` skill
|
|
321
|
+
- Shared GraphQL schema types: `shared-schema.graphqls` (in this skill directory)
|
|
322
|
+
- Schema download: `npm run graphql:schema` (run from webapp dir)
|
|
323
|
+
- Type generation: `npm run graphql:codegen` (run from webapp dir)
|