# @salesforce/sdk-data (Beta)

Runtime SDK for accessing Salesforce record data via GraphQL and REST from React web apps.

> **Beta:** This is a pilot or beta service subject to the
> [Beta Services Terms](https://www.salesforce.com/company/legal/agreements/) or a written
> Unified Pilot Agreement, and applicable terms in the
> [Product Terms Directory](https://ptd.salesforce.com/).

## Installation

```sh
npm install @salesforce/sdk-data
```

## Quick Start

```ts
import { createDataSDK, gql } from "@salesforce/sdk-data";

const MY_QUERY = gql`
  query GetAccounts {
    uiapi {
      query {
        Account(first: 10) {
          edges {
            node {
              Id
              Name {
                value
              }
              Industry {
                value
              }
            }
          }
        }
      }
    }
  }
`;

const dataSdk = await createDataSDK();
const response = await dataSdk.graphql?.<GetAccountsQuery>(MY_QUERY);
```

## API Reference

### `createDataSDK(options?)`

Factory that creates a `DataSDK` instance. Detects the runtime surface automatically and returns the appropriate implementation.

```ts
async function createDataSDK(options?: DataSDKOptions): Promise<DataSDK>;
```

**`DataSDKOptions`**

| Property  | Type                   | Description                          |
| :-------- | :--------------------- | :----------------------------------- |
| `surface` | `string`               | Override automatic surface detection |
| `webapp`  | `WebAppDataSDKOptions` | Options for the web app surface      |

**`WebAppDataSDKOptions`**

| Property   | Type                                      | Description                                   |
| :--------- | :---------------------------------------- | :-------------------------------------------- |
| `basePath` | `string`                                  | Base URL prefix for Salesforce API calls      |
| `onStatus` | `Partial<Record<number, StatusCallback>>` | HTTP status code callback map (e.g. 401, 403) |

### `DataSDK` Interface

```ts
interface DataSDK {
  graphql?: <T, V = Record<string, unknown>>(
    query: string,
    variables?: V,
  ) => Promise<GraphQLResponse<T>>;
  fetch?: typeof fetch;
}
```

Both methods are optional — availability depends on the runtime environment. Always use optional chaining when calling them.

#### `graphql<T, V>(query, variables?)`

Sends a GraphQL request to the Salesforce UI API endpoint.

| Parameter   | Type     | Description                                |
| :---------- | :------- | :----------------------------------------- |
| `query`     | `string` | GraphQL query or mutation operation        |
| `variables` | `V`      | Optional variables referenced by the query |

Returns `Promise<GraphQLResponse<T>>`:

```ts
interface GraphQLResponse<T> {
  data: T;
  errors?: Array<{
    message: string;
    locations?: Array<{ line: number; column: number }>;
    path?: string[];
  }>;
}
```

#### `fetch(input, init?)`

Fetch wrapper for Salesforce REST endpoints not covered by GraphQL. Handles CSRF token management and base path resolution.

Supported endpoints include `/services/apexrest/*`, `/services/data/v{version}/ui-api/*`, `/services/data/v{version}/chatter/*`, and others.

### `gql` Template Tag

Identity template literal tag for inline GraphQL queries. Enables editor syntax highlighting and codegen detection without any runtime transformation.

```ts
import { gql } from "@salesforce/sdk-data";

const MY_QUERY = gql`
  query MyQuery {
    uiapi { ... }
  }
`;
```

### `NodeOfConnection<T>`

Utility type that extracts the node type from a UI API GraphQL connection response (the `edges`/`node` pattern).

```ts
import { type NodeOfConnection } from "@salesforce/sdk-data";
import type { GetAccountsQuery } from "../graphql-operations-types";

type AccountNode = NodeOfConnection<GetAccountsQuery["uiapi"]["query"]["Account"]>;
```

Use this when your query returns the connection shape and you need a clean type for individual records. If your query uses simple flat types, you don't need `NodeOfConnection`.

## Usage Patterns

### Inline Queries with `gql`

Best for simple queries without variables that are colocated with the usage code.

```ts
import { createDataSDK, gql } from "@salesforce/sdk-data";
import type { CurrentUserQuery } from "../graphql-operations-types";

const CURRENT_USER = gql`
  query CurrentUser {
    uiapi {
      currentUser {
        Id
        Name {
          value
        }
      }
    }
  }
`;

export async function getCurrentUser() {
  const dataSdk = await createDataSDK();
  const response = await dataSdk.graphql?.<CurrentUserQuery>(CURRENT_USER);

  if (response?.errors?.length) {
    throw new Error(response.errors.map((e) => e.message).join("; "));
  }

  return response?.data?.uiapi.currentUser ?? null;
}
```

### External `.graphql` Files

Best for complex queries with variables, fragments, or queries shared across files. Import with the `?raw` suffix.

**`src/api/utils/query/getProperties.graphql`**

```graphql
query GetProperties($first: Int) {
  uiapi {
    query {
      Property__c(first: $first) {
        edges {
          node {
            Id
            Name {
              value
            }
            Address__c {
              value
            }
            Status__c {
              value
            }
          }
        }
      }
    }
  }
}
```

**`src/api/getProperties.ts`**

```ts
import { createDataSDK, type NodeOfConnection } from "@salesforce/sdk-data";
import GET_PROPERTIES from "./query/getProperties.graphql?raw";
import type { GetPropertiesQuery, GetPropertiesQueryVariables } from "../graphql-operations-types";

type PropertyNode = NodeOfConnection<GetPropertiesQuery["uiapi"]["query"]["Property__c"]>;

export async function getProperties(first = 50): Promise<PropertyNode[]> {
  const dataSdk = await createDataSDK();
  const response = await dataSdk.graphql?.<GetPropertiesQuery, GetPropertiesQueryVariables>(
    GET_PROPERTIES,
    { first },
  );

  if (response?.errors?.length) {
    throw new Error(response.errors.map((e) => e.message).join("; "));
  }

  return response?.data?.uiapi?.query?.Property__c?.edges?.map((e) => e?.node) || [];
}
```

### REST via `fetch`

Use `dataSdk.fetch?.()` for Salesforce REST endpoints not covered by GraphQL, such as Apex REST or Connect API.

```ts
import { createDataSDK } from "@salesforce/sdk-data";

const dataSdk = await createDataSDK();
const response = await dataSdk.fetch?.("/services/apexrest/property/listings");
const listings = await response?.json();
```

## Data Access Guidelines

1. **Prefer GraphQL** via `dataSdk.graphql?.()` for record data access.
2. **Use `dataSdk.fetch?.()`** for REST endpoints not covered by GraphQL (Apex REST, UI API, Connect API).
3. **Always use optional chaining** (`graphql?.()`, `fetch?.()`) — both methods may be unavailable depending on the runtime surface.
4. **Never call `fetch()` or `axios` directly** for Salesforce endpoints. The SDK handles authentication and CSRF tokens.
5. **Use the `gql` tag or `.graphql` files** — never plain string literals for queries.
6. **Provide type generics** to `graphql?.()` for type-safe responses.

```ts
// Prefer this
await dataSdk.graphql?.<GetAccountsQuery>(query);

// Not this
await dataSdk.graphql?.(query);
```

## See Also

- [GraphQL API Developer Guide](https://developer.salesforce.com/docs/platform/graphql/guide/)
- [UI API Schema Conventions](https://developer.salesforce.com/docs/platform/graphql/guide/schema-conventions.html)
- [React App Recipes](https://github.com/trailheadapps/multiframework-recipes)
