# 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?.(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({ + query: GET_ACCOUNTS, + variables, + }); + const accounts = result.data?.uiapi?.query?.Account?.edges ?? []; + if (result.errors?.length) { /* ... */ } ``` Passing the generated `` 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 `(query, variables)` | type params on `query(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?.(CREATE_ACCOUNT, { input }); + const { data, errors } = await sdk.graphql!.mutate({ + 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 (`Query` / `QueryVariables` / `Mutation` / `MutationVariables`) are unchanged. Only **where** the type params attach moves — from the callable to the namespace method: ```diff - await sdk.graphql?.(GET_ACCOUNTS, variables); + await sdk.graphql!.query({ 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` / `mutate` - [ ] 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 `