afv-library/skills/experience-ui-bundle-salesforce-data-access/references/migration.md

120 lines
5.1 KiB
Markdown
Raw Normal View History

# Migration — old Data SDK → `@salesforce/platform-sdk`
The Data SDK changed in two breaking ways (PR #502, shipped in `@salesforce/platform-sdk`
v10.10.1):
1. **Package renamed**`@salesforce/sdk-data`**`@salesforce/platform-sdk`**.
2. **`sdk.graphql` reshaped** from a **callable method** into a **namespace** with `.query()`
and `.mutate()`, both taking an **options object**.
> Any remaining `@salesforce/sdk-data` strings in the webapps repo are **stale `dist/` build
> artifacts only** — not canonical. Always import from `@salesforce/platform-sdk`.
This is the **only** document where the dead callable API appears, for comparison. Everywhere
else in this skill uses the new API exclusively.
---
## 1. Import
```diff
- import { createDataSDK, gql, type NodeOfConnection } from "@salesforce/sdk-data";
+ import { createDataSDK, gql, type NodeOfConnection, type CacheControl } from "@salesforce/platform-sdk";
```
`createDataSDK`, `gql`, `NodeOfConnection`, and `CacheControl` all export from
`@salesforce/platform-sdk`.
## 2. Query call
```diff
- // OLD — callable, positional args, returns a response object with .data / .errors
- const response = await sdk.graphql?.<GetAccountsQuery, GetAccountsQueryVariables>(GET_ACCOUNTS, variables);
- const accounts = response?.data?.uiapi?.query?.Account?.edges ?? [];
- if (response?.errors?.length) { /* ... */ }
+ // NEW — namespace method, options bag, returns a reactive QueryResult
+ const result = await sdk.graphql!.query<GetAccountsQuery, GetAccountsQueryVariables>({
+ query: GET_ACCOUNTS,
+ variables,
+ });
+ const accounts = result.data?.uiapi?.query?.Account?.edges ?? [];
+ if (result.errors?.length) { /* ... */ }
```
Passing the generated `<GetAccountsQuery>` type parameter makes `result.data` fully typed — no
`as any` cast needed.
Mapping:
| Old | New |
|-----|-----|
| `sdk.graphql?.(QUERY, vars)` | `sdk.graphql!.query({ query: QUERY, variables: vars })` |
| positional `<T, V>(query, variables)` | type params on `query<T, V>(options)` |
| `response.data` / `response.errors` | `result.data` / `result.errors` (same fields) |
| (none) | `result.subscribe(cb)` / `result.refresh()` — new reactive handle |
| (none) | `cacheControl` option — new per-call cache policy |
## 3. Mutation call
The old callable form was used for mutations too. Mutations now have their own method, and the
operation key is **`mutation`**, not `query`:
```diff
- const response = await sdk.graphql?.<CreateAccountMutation>(CREATE_ACCOUNT, { input });
+ const { data, errors } = await sdk.graphql!.mutate<CreateAccountMutation, CreateAccountMutationVariables>({
+ mutation: CREATE_ACCOUNT, // key is `mutation`, not `query`
+ variables: { input },
+ });
```
`mutate()` returns `{ data, errors }` only — **no** `subscribe`/`refresh`, and it is **never
cached**. To refresh a list after a mutation, hold the query `result` and call
`result.refresh()` (see [caching.md](caching.md)).
## 4. Optional chaining → non-null assertion
The old guidance was "always use optional chaining" (`sdk.graphql?.()`). Real consumer code now
uses the **non-null assertion** after `createDataSDK()`:
```diff
- const response = await sdk.graphql?.(QUERY, vars);
+ const result = await sdk.graphql!.query({ query: QUERY, variables: vars });
```
`sdk.graphql` may still be `undefined` on some surfaces. Use `!` when you know the surface
supports data ops; use an explicit guard if it might not:
```typescript
if (!sdk.graphql) throw new Error("GraphQL not available on this surface");
const result = await sdk.graphql.query({ query: QUERY, variables: vars });
```
## 5. Codegen types — unchanged generation, new call placement
`npm run graphql:codegen` and the generated type names (`<Op>Query` / `<Op>QueryVariables` /
`<Op>Mutation` / `<Op>MutationVariables`) are unchanged. Only **where** the type params attach
moves — from the callable to the namespace method:
```diff
- await sdk.graphql?.<GetAccountsQuery, GetAccountsQueryVariables>(GET_ACCOUNTS, variables);
+ await sdk.graphql!.query<GetAccountsQuery, GetAccountsQueryVariables>({ query: GET_ACCOUNTS, variables });
```
## 6. New capabilities to consider while migrating
The reshape also unlocked the freshness features (none required to get back to working). The one
migration-specific gotcha: **caching is now on by default** (300s, WebApp) — if any code relied
on every call hitting the network, add `cacheControl: "no-cache"` to it. The reactive
`subscribe`/`refresh` handle and the rest of `cacheControl` are in [caching.md](caching.md).
## 7. Migration checklist
- [ ] Replace every `@salesforce/sdk-data` import with `@salesforce/platform-sdk`
- [ ] Convert every `sdk.graphql?.(q, v)` query to `sdk.graphql!.query({ query: q, variables: v })`
- [ ] Convert every mutation call to `sdk.graphql!.mutate({ mutation: m, variables: v })` (key is `mutation`)
- [ ] Move generated type params onto `query<T,V>` / `mutate<T,V>`
- [ ] Rename `response``result`; `response.data`/`.errors` → `result.data`/`.errors`
- [ ] Add a `cacheControl: "no-cache"` to any call that genuinely must always hit the network
- [ ] Re-run `npm run graphql:codegen` and `npx eslint <file>`