GraphQL Queries in Data SDK

Use a GraphQL query to retrieve Salesforce data, such as from an Account or Contact record.

To access Salesforce data via GraphQL:

  1. Define TypeScript interfaces for the response shape.
  2. Write the GraphQL query using uiapi syntax.
  3. Use createDataSDK() and dataSdk.graphql?.() with proper typing.
  4. Extract and return the data from the response.
1import { createDataSDK, gql } from "@salesforce/platform-sdk/data";
2
3const dataSdk = await createDataSDK();
4
5// For queries: use dataSdk.graphql?.query() or dataSdk.fetch?() for API calls
6const result = await dataSdk.graphql?.query<ResponseType, VariablesType>({
7    query: QUERY_STRING,
8    variables: { ... },
9    operationName?: "...",
10    cacheControl?: "no-cache" | "only-if-cached" | { type: "max-age", maxAge: 60 }
11  });
12const QUERY_STRING = gql`
13 query MyQuery {
14   uiapi {
15     ...
16   }
17 }
18`;

For more information about GraphQL usage, see the GraphQL API Developer Guide.

Use one of these patterns for defining GraphQL queries.

Inline gql Tag 

This pattern is useful for simple queries without variables or fragments.

  • Use the gql template tag (never plain template literals)
  • Ensure the query operation name matches what codegen expects
  • Import generated types from graphql-operations-types.ts
1import { createDataSDK, gql } from "@salesforce/platform-sdk/data";
2import type { CurrentUserQuery } from "../graphql-operations-types";
3const CURRENT_USER_QUERY = gql`
4  query CurrentUser {
5    uiapi {
6      currentUser {
7        Id
8        Name {
9          value
10        }
11      }
12    }
13  }
14`;
15
16interface User {
17  id: string;
18  name: string;
19}
20
21export async function getCurrentUser(): Promise<User | null> {
22  const dataSdk = await createDataSDK();
23  const result =
24    (await dataSdk.graphql?.query) <
25    CurrentUserQuery >
26    {
27      query: CURRENT_USER_QUERY,
28    };
29
30  if (result?.errors?.length) {
31    throw new Error(result.errors.map((e) => e.message).join("; "));
32  }
33
34  const userData = result?.data?.uiapi.currentUser;
35  if (!userData) {
36    throw new Error("No user data found");
37  }
38
39  return {
40    id: userData.Id,
41    name: userData.Name?.value || "User",
42  };
43}

External .graphql File 

This pattern is useful for complex queries with variables, fragments, or queries shared across files.

  • Import .graphql files with the ?raw suffix
  • Import generated types from graphql-operations-types.ts
  • Provide response and variables type generics to dataSdk.graphql?.query<T, V>()
  • Use optional chaining for nested response data

To implement a query using a .graphql external file:

  1. Create a .graphql file under src/api/utils/query/
1# src/api/utils/query/myQuery.graphql
2query GetMyData($myVariable: String) {
3  uiapi {
4    query {
5      MyObject(first: 10, where: { Field: { eq: $myVariable } }) {
6        edges {
7          node {
8            Id
9            Name {
10              value
11            }
12          }
13        }
14      }
15    }
16  }
17}
  1. Generate the types using npm run graphql:codegen. The command generates the types in src/api/graphql-operations-types.ts. For example:
    1. Response type: GetMyDataQuery
    2. Variables type: GetMyDataQueryVariables
  2. Implement the data access function.
1import { createDataSDK, type NodeOfConnection } from "@salesforce/platform-sdk/data";
2import MY_QUERY from "./query/myQuery.graphql?raw";
3import type { GetMyDataQuery, GetMyDataQueryVariables } from "../graphql-operations-types";
4
5type MyNode = NodeOfConnection<GetMyDataQuery["uiapi"]["query"]["MyObject"]>;
6
7export async function getMyData(variables: GetMyDataQueryVariables): Promise<MyNode[]> {
8  const dataSdk = await createDataSDK();
9  const result = await dataSdk.graphql?.query<GetMyDataQuery, GetMyDataQueryVariables>({
10    query: MY_QUERY,
11    variables,
12  });
13
14  if (result?.errors?.length) {
15    const errorMessages = result.errors.map((e) => e.message).join("; ");
16    throw new Error(`GraphQL Error: ${errorMessages}`);
17  }
18
19  return result?.data?.uiapi?.query?.MyObject?.edges?.map((edge) => edge?.node) || [];
20}

Export the GraphQL Return Type 

To provide type safety for GraphQL operations, define your return type. These TypeScript interfaces define the shape of data flowing in (query variables) and out (query results) of your GraphQL operations.

1export interface Account {
2  id: string;
3  name: string;
4  industry: string | null;
5  type: string | null;
6  annualRevenue: number | null;
7  numberOfEmployees: number | null;
8  billingCity: string | null;
9  billingState: string | null;
10  billingCountry?: string | null;
11  phone: string | null;
12  website: string | null;
13  description?: string | null;
14}
15
16export interface AccountsResult {
17  accounts: Account[];
18  pageInfo: {
19    hasNextPage: boolean;
20    endCursor: string | null;
21  };
22}
23export interface GetAccountsQueryVariables {
24  first?: number | null;
25  after?: string | null;
26}

Export the interfaces so they can be imported and reused across components. You can also use graphql-codegen to generate the interfaces for the interfaces to stay in sync with your schema. Use the return types in your methods and variables.

1export async function getAccounts(first = 20, after?: string): Promise<AccountsResult> {
2 const variables: GetAccountsQueryVariables = { first };
3 if (after) variables.after = after;
4
5 const dataSdk = await createDataSDK();
6 const result = await dataSdk.graphql?.query<GetAccountsQuery, GetAccountsQueryVariables>({
7   query: GET_ACCOUNTS,
8   variables,
9 });
10
11 if (result?.errors?.length) {
12   throw new Error(result.errors.map((e) => e.message).join("; "));
13 }

GraphQL Fragments 

GraphQL fragments are reusable units of query logic that are used to define a set of fields.
This example uses GraphQL fragments and directives to keep one query reusable while conditionally including fields.

  • FinancialFields and ContactFields are reusable field groups for Account.
  • In the main query, each fragment is attached with @include(if: $...):
    • ...FinancialFields @include(if: $includeFinancials)
    • ...ContactFields @include(if: $includeContacts)
  • At runtime, variables (includeFinancials, includeContacts) decide whether those field blocks are fetched.
1query GetAccountDetails($id: ID!, $includeFinancials: Boolean!, $includeContacts: Boolean!) {
2  uiapi {
3    query {
4      Account(where: { Id: { eq: $id } }) {
5        edges {
6          node {
7            Id
8            Name {
9              value
10            }
11            ...FinancialFields @include(if: $includeFinancials)
12            ...ContactFields @include(if: $includeContacts)
13          }
14        }
15      }
16    }
17  }
18}
19
20fragment FinancialFields on Account {
21  AnnualRevenue {
22    value
23  }
24  NumberOfEmployees {
25    value
26  }
27}
28
29fragment ContactFields on Account {
30  Phone {
31    value
32  }
33  Website {
34    value
35  }
36}

dataSdk.graphql?.query<Response, Variables>({ query, variables }) executes the query with:

  • explicit response type (GetAccountDetailsQuery)
  • explicit variables type (GetAccountDetailsQueryVariables)
  • runtime variables object (id, includeFinancials, includeContacts)
1import { createDataSDK } from "@salesforce/platform-sdk/data";
2import QUERY from "./query/getAccountDetails.graphql?raw";
3import type {
4  GetAccountDetailsQuery,
5  GetAccountDetailsQueryVariables,
6} from "../graphql-operations-types";
7
8const dataSdk = await createDataSDK();
9const result = await dataSdk.graphql?.query<
10  GetAccountDetailsQuery,
11  GetAccountDetailsQueryVariables
12>({
13  query: QUERY,
14  variables: {
15    id: accountId,
16    includeFinancials: userWantsFinancials,
17    includeContacts: userWantsContacts,
18  },
19});

GraphQL Usage Considerations 

When using GraphQL via the Data SDK, consider these usage patterns.

Direct API calls aren’t supported. Always use the Data SDK to make your API calls.

1// Don't do this: Direct axios/fetch for Salesforce APIs
2const result = await axios.post("/graphql", { query });
3const result = await fetch("/services/data/v{version}/...");
4// Do this: Use the DataSDK
5const dataSdk = await createDataSDK();
6const result = await dataSdk.graphql?.query<ResponseType>({ query, variables });
7const result = await dataSdk.fetch?.("/services/data/v{version}/...");

Provide the response type instead of making untyped GraphQL calls.

1// Don't do this: Untyped GraphQL calls
2await dataSdk.graphql?.query({ query });
3// Do this: Provide response type generic
4await dataSdk.graphql?.query<GetAccountsQuery>({ query });

Use the gql template tag or an external .graphql file instead of plain string queries.

1// Don't do this: Plain string queries
2const query = `query { ... }`;
3
4// Do this: Use gql tag or .graphql file
5const query = gql`query { ... }`;
6// or do this: Import the query from an external .graphql file
7import QUERY from "./query/myQuery.graphql?raw";

Use optional chaining instead of non-null assertion on a GraphQL call.

1// Don't do this: Non-null assertion on graphql
2await dataSdk.graphql!.query<T>({ query });
3// Do this: Optional chaining
4await dataSdk.graphql?.query<T>({ query });

See Also