To work with record data in a React app, use the Data SDK to run a GraphQL query or mutation operation.
Agentforce Vibes automatically generates GraphQL data access when your prompt contains one of these intents.
- Fetch data from Salesforce
- Query or update a record
- Add a new GraphQL operation (query or mutation)
- Add data access for a Salesforce object (Account, Contact, Opportunity, etc.)
The base React app provides all GraphQL dependencies and tooling:
@salesforce/platform-sdk/data: Runtime SDK forcreateDataSDKandgql@graphql-codegen/cliand plugins: Type generation from .graphqlfiles and inlinegqlqueries@graphql-eslint/eslint-plugin: Linting for .graphqlfiles andgqltemplate literalsgraphql: Shared by codegen, ESLint, and schema introspection
GraphQL Tooling
If the generated code doesn't use GraphQL and the Data SDK to access data, make sure that:
- The
@salesforce/platform-sdk/datapackage is available for thecreateDataSDK,gql, andNodeOfConnectionimports. - A
schema.graphqlfile exists at the project root. If it's missing, generate the file by runningnpm run graphql:schema. This command downloads the full GraphQL schema from a connected Salesforce org via introspection and generates theschema.graphqlfile to the project root.
To generate TypeScript types from .graphql files and inline gql queries, run this command:
npm run graphql:codegen
The TypeScript types are generated at src/api/graphql-operations-types.ts.
GraphQL Workflow
Before writing a GraphQL query or mutation operation, Agentforce Vibes explores the schema first before determining the query pattern.
1. Explore the schema
The actions for exploring the schema includes:
- Search the
schema.graphqlfile fortype <ObjectName> implements Recordto find available fields - Search for
input <ObjectName>_Filterfor filter options - Search for
input <ObjectName>_OrderByfor sorting options
For mutations, Agentforce Vibes searches for input <ObjectName>CreateInput or <ObjectName>UpdateInput.
2. Choose the query pattern
For complex queries, Agentforce Vibes generates an external .graphql file. Complex queries are those that include variables, fragments, or those that are shared across files. The external file also enables full codegen type generation.
For simpler queries, an inline gql tag is used. Simpler queries don't use variables and are colocated with usage code.
import { createDataSDK, gql } from "@salesforce/platform-sdk/data";
import type {
GetPropertiesQuery,
GetPropertiesQueryVariables,
} from "../graphql-operations-types";
const GET_PROPERTIES = gql`
query GetProperties($first: Int) {
uiapi {
query {
Property__c(first: $first) {
edges {
node {
Id
Name {
value
}
Address__c {
value
}
Status__c {
value
}
Monthly_Rent__c {
value
}
Hero_Image__c {
value
}
}
}
}
}
}
}
`;
const dataSdk = await createDataSDK();
const result = await dataSdk.graphql?.query<
GetPropertiesQuery,
GetPropertiesQueryVariables
>({
query: GET_PROPERTIES,
variables: { first: 50 }
});
3. Write the query
For complex queries, the .graphql file is generated under src/api/utils/query. It follows the UI API structure:
query GetObjectName {
uiapi {
query {
ObjectName(...) {
edges {
node { ... }
}
}
}
}
}
For read queries and mutation queries, Agentforce Vibes dynamically queries the target org, generates the query using the query template, and tests the query.
Read Query Generation and Testing
When generating the read query, Agentforce Vibes uses this template.
query QueryName {
uiapi {
query {
EntityName(
# conditions here
) {
edges {
node {
# Direct fields
FieldName { value }
# Non-polymorphic reference (single type)
RelationshipName {
Id
Name { value }
}
# Polymorphic reference (multiple types)
PolymorphicRelationshipName {
...TypeAInfo
...TypeBInfo
}
# Child relationship (subquery)
RelationshipName(
# conditions here
) {
edges {
node {
# fields
}
}
}
}
}
}
}
}
}
fragment TypeAInfo on TypeA {
Id
SpecificFieldA { value }
}
fragment TypeBInfo on TypeB {
Id
SpecificFieldB { value }
}
When a read query is successfully generated, the workflow moves into the testing phase. Agentforce Vibes asks you if you want it to test the query using the same method that was used during introspection. If you approve, the query testing starts.
If the query testing fails, Agentforce Vibes identifies the root cause and updates the query accordingly. It resumes testing with the updated query. If the query testing fails after 2 attempts, it asks for additional details and restarts the entire GraphQL workflow from the introspection phase.
Mutation Operation Generation and Testing
When generating the mutation operation, Agentforce Vibes uses this template.
mutation MutationName($input: EntityNameOperationInput!) {
uiapi {
EntityNameOperation(input: $input) {
# Create/Update operations return Record
Record {
# Always include Id
Id
# Field values
FieldName { value }
}
# Delete operations return only Id
Id
}
}
}
# Example: Create mutation
mutation CreateAccount($input: AccountCreateInput!) {
uiapi {
AccountCreate(input: $input) {
Record {
Id
Name { value }
Industry { value }
Phone { value }
}
}
}
}
# Example: Update mutation
mutation UpdateAccount($input: AccountUpdateInput!) {
uiapi {
AccountUpdate(input: $input) {
Record {
Id
Name { value }
AnnualRevenue { value }
}
}
}
}
# Example: Delete mutation
mutation DeleteAccount($input: AccountDeleteInput!) {
uiapi {
AccountDelete(input: $input) {
Id
}
}
}
When a mutation operation is successfully generated, the workflow moves into the testing phase. Agentforce Vibes asks you if you want it to test the query using the same method that was used during introspection. If you approve, the query testing starts. It then asks you if there are input arguments to use. Agentforce Vibes reports the result of the test based on the data and errors attributes in the response, as one of these options:
- Partial: Both data and errors aren't empty
- Failed: data is empty
- Success: There are no errors in the response
note: When a GraphQL response contains both data and errors (partial success), the Data SDK preserves the partial data. Partial responses aren't cached; subsequent queries re-fetch data from the network. This allows your app to render fetched data while handling field-level errors.
For partially successful query tests, Agentforce Vibes reports the fields that are mentioned in the errors list and explain the cause. For example, the fields can't be queried as part of a mutation operation. It then offers to remove the offending fields. If you agree, Agentforce Vibes restarts the generation workflow with the new field list.
For simpler queries, you get a query inline using the gql template tag and a query name that matches what codegen expects. For more information, see the GraphQL API Dev Guide.
4. Generate Types
After the query is written, codegen generates or updates the src/api/graphql-operations-types.ts file with these types:
<OperationName>Queryor<OperationName>Mutation— response type<OperationName>QueryVariablesor<OperationName>MutationVariables— input variables type
5. Implement the Data Access Function
For complex queries with a generated .graphql file, the data access function follows this pattern.
import { createDataSDK, type NodeOfConnection } from "@salesforce/platform-sdk/data";
import MY_QUERY from "./query/myQuery.graphql?raw";
import type { GetMyDataQuery, GetMyDataQueryVariables } from "../graphql-operations-types";
type MyNode = NodeOfConnection<GetMyDataQuery["uiapi"]["query"]["MyObject"]>;
export async function getMyData(variables: GetMyDataQueryVariables): Promise<MyNode[]> {
const dataSdk = await createDataSDK();
const result = await dataSdk.graphql?.query<GetMyDataQuery, GetMyDataQueryVariables>({
query: MY_QUERY,
variables,
});
if (result?.errors?.length) {
const errorMessages = result.errors.map((e) => e.message).join("; ");
throw new Error(`GraphQL Error: ${errorMessages}`);
}
return result?.data?.uiapi?.query?.MyObject?.edges?.map((edge) => edge?.node) || [];
}
For simpler inline queries, the data access function follows this pattern.
<!-- prettier-ignore -->import { createDataSDK, gql } from "@salesforce/platform-sdk/data";
import type { MySimpleQuery } from "../graphql-operations-types";
const MY_QUERY = gql`
query MySimple {
uiapi { ... }
}
`;
export async function getSimpleData(): Promise<SomeType> {
const dataSdk = await createDataSDK();
const result = await dataSdk.graphql?.query<MySimpleQuery>({ query: MY_QUERY });
// check result.errors, then extract result.data
}