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

5.1 KiB

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

- 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

- // 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:

- 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).

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():

- 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:

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:

- 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.

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 responseresult; response.data/.errorsresult.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>