* feat: removing old webapp skills * feat: adding sync of skills from webapps to afv * feat: adding the first iteration of skills * feat: pin template deps to latest npm versions and flatten skill folders - Add pin-template-deps.js to resolve "*" deps to exact npm versions - Integrate pinning into sync-template-skills npm script - Remove check-template-skills-versions.js (no longer needed) - Simplify workflow to single sync step - Flatten skill output: one folder per skill with cleaned names Made-with: Cursor * fix: resolve skill validation errors - Move .template-versions.json from skills/ to root - Shorten skill names to meet 64-char limit: - salesforce-webapp-feature-micro-frontend-generating-micro-frontend-lwc → salesforce-webapp-micro-frontend-lwc - salesforce-webapp-feature-react-agentforce-conversation-client-integrating-agentforce-conversation-client → salesforce-webapp-agentforce-conversation-client - salesforce-webapp-feature-react-file-upload-implementing-file-upload → salesforce-webapp-react-file-upload - Expand descriptions to meet 20-word minimum with trigger context * Add webapp skills from template, sync script updates - Rename skill folders from salesforce-webapp-* to *-webapp-* convention - Update sync-template-skills.js: set SKILL.md front matter name to dest folder - Remove sync-template-skills workflow and pin-template-deps script - Add .synced-template-skills.json manifest, deploying-webapp-to-salesforce skill - Replace salesforce-webapp-designing-webapp-ui-ux with designing-webapp-ui-ux Made-with: Cursor * Align SKILL.md front matter name with folder for all webapp skills Made-with: Cursor * Fix skill validation: description length and trigger context for configuring-webapp-metadata, creating-webapp Made-with: Cursor * Rename sync script to sync-webapp-skills, drop manifest file - Rename sync-template-skills.js to sync-webapp-skills.js - Update package.json script to sync-webapp-skills - Remove .synced-template-skills.json creation and add to .gitignore Made-with: Cursor * Revert sync-react-b2e-sample and sync-react-b2x-sample to upstream version Made-with: Cursor * Sync script: pin b2e and b2x to latest, sync skills from template - Pin both template packages to latest in sync-webapp-skills.js - Update package.json / package-lock.json (b2x 1.109.0) - Sync skills: managing-webapp-agentforce-conversation-client, bar-line-chart, remove building-webapp-analytics-charts and integrating-webapp-agentforce-conversation-client - Minor skill content updates Made-with: Cursor * Remove interactive map, weather widget, and Unsplash skills (no longer in template) Made-with: Cursor --------- Co-authored-by: Hemant Singh Bisht <hsinghbisht@salesforce.com>
14 KiB
| name | description | paths | |||
|---|---|---|---|---|---|
| generating-webapp-graphql-mutation-query | Generate Salesforce GraphQL mutation queries. Use when the query to generate is a mutation query. Schema exploration must complete first — invoke exploring-graphql-schema first. |
|
Salesforce GraphQL Mutation Query Generation
Triggering conditions
- Only if the schema exploration phase completed successfully (invoke
exploring-graphql-schemafirst) - Only if the query to generate is a mutation query
Schema Access Policy
⚠️ GREP ONLY — During mutation generation you may need to verify field names, input types, or representations. All schema lookups MUST use the grep-only commands defined in the
exploring-graphql-schemaskill. Do NOT open, read, stream, or parse./schema.graphqlwith any tool other than grep.
Your Role
You are a GraphQL expert. Generate Salesforce-compatible mutation queries. Schema exploration must complete first. If the schema exploration has not been executed yet, you MUST run the full exploration workflow from the exploring-graphql-schema skill first, then return here for mutation query generation.
Mutation Queries General Information
The GraphQL engine supports Create, Update, and Delete operations. Update and Delete operate on Id-based entity identification. See the mutation query schema section.
Mutation Query Generation Workflow
Strictly follow the rules below when generating the GraphQL mutation query:
- Input Fields Validation - Validate that the set of fields validate input field constraints. Verify every field name and type against grep output from the schema — do NOT guess or assume
- Output Fields Validation - Validate that the set of fields used in the select part of the query validate the output fields constraints
- Type Consistency - Make sure variables used as query arguments and their related fields share the same GraphQL type. Verify types via grep lookup — do NOT assume types
- Report Phase - Use the Mutation Query Report Template below to report on the previous validation phases
- Input Arguments -
inputis the default name for the argument, unless otherwise specified - Output Field - For
CreateandUpdateoperations, the output field is always namedRecord, and is of type EntityName - Field Name Validation - Every field name in the generated mutation MUST match a field confirmed via grep lookup in the schema. Do NOT guess or assume field names exist
- Query Generation - Use the mutation query template and adjust it based on the selected operation
- Output Format - Use the standalone
- Lint Validation - After writing the mutation to a file, run
npx eslint <file>from the webapp dir to validate it against the schema. Fix any reported errors before proceeding. See Lint Validation for details - Test the Query - Use the Generated Mutation Query Testing workflow to test the generated query
- Report First - Always output the generated mutation in the proper output format BEFORE initiating any test
Mutation Query Schema
Important: In the schema fragments below, replace EntityName occurrences by the real entity name (i.e. Account, Case...).
Important: Delete operations all share the same generic Record entity name for both input and payload, only exposing the standard Id field.
input EntityNameCreateRepresentation {
# Subset of EntityName fields here
}
input EntityNameCreateInput { EntityName: EntityNameCreateRepresentation! }
type EntityNameCreatePayload { Record: EntityName! }
input EntityNameUpdateRepresentation {
# Subset of EntityName fields here
}
input EntityNameUpdateInput { Id: IdOrRef! EntityName: EntityNameUpdateRepresentation! }
type EntityNameUpdatePayload { Record: EntityName! }
input RecordDeleteInput { Id: IdOrRef! }
type RecordDeletePayload { Id: ID }
type UIAPIMutations {
EntityNameCreate(input: EntityNameCreateInput!): EntityNameCreatePayload
EntityNameDelete(input: RecordDeleteInput!): RecordDeletePayload
EntityNameUpdate(input: EntityNameUpdateInput!): EntityNameUpdatePayload
}
Mutation Queries Input Field Constraints
CreateMutation Queries:- MUST include all required fields
- MUST only include createable fields
- Child relationships can't be set and MUST be excluded
- Fields with type
REFERENCEcan only be assigned IDs through theirApiNamename
UpdateMutation Queries:- MUST include the id of the entity to update
- MUST only include updateable fields
- Child relationships can't be set and MUST be excluded
- Fields with type
REFERENCEcan only be assigned IDs through theirApiNamename
DeleteMutation Queries:- MUST include the id of the entity to delete
Mutation Queries Output Field Constraints
CreateandUpdateMutation Queries:- MUST exclude all child relationships
- MUST exclude all
REFERENCEfields, unless accessed through theirApiNamemember (no navigation to referenced entity) - Inaccessible fields will be reported as part of the
errorsattribute in the returned payload - Child relationships CAN'T be queried as part of a mutation
- Fields with type
REFERENCEcan only be queried through theirApiName(no referenced entities navigation, no sub fields)
DeleteMutation Queries:- MUST only include the
Idfield
- MUST only include the
Mutation Query Report Template
Input arguments:
- Required fields: FieldName1 (Type1), FieldName2 (Type2)...
- Other fields: FieldName3 (Type3)... Output fields: FieldNameA (TypeA), FieldNameB (TypeB)...
Mutation Query Templates
mutation mutateEntityName(
# arguments
) {
uiapi {
EntityNameOperation(input: {
# the following is for `Create` and `Update` operations only
EntityName: {
# Input fields
}
# the following is for `Update` and `Delete` operations only
Id: ... # id here
}) {
# the following is for `Create` and `Update` operations only
Record {
# Output fields
}
# the following is for `Delete` operations only
Id: ... # id here
}
}
}
Mutation Standalone (Default) Output Format - CLEAN CODE ONLY
import { gql } from '@salesforce/sdk-data';
const QUERY_NAME = gql`
mutation mutateEntity($input: EntityNameOperationInput!) {
uiapi {
EntityNameOperation(input: $input) {
# select output fields here depending on operation type
}
}
}
`;
const QUERY_VARIABLES = {
input: {
// The following is for `Create` and `Update` operations only
EntityName: {
// variables here
},
// The following is for `Update` and `Delete` operations only
Id: ... // id here
}
};
❌ FORBIDDEN — Do NOT include any of the following:
- Explanatory comments about the query (inline or surrounding)
- Field descriptions or annotations
- Additional text about what the query does
- Workflow step descriptions or summaries
- Comments like
// fetches...,// creates...,/* ... */
✅ ONLY output:
- The raw query string constant (using
gqltagged template) - The variables object constant
- Nothing else — no extra imports, no exports, no wrapper functions
Lint Validation
After writing the generated mutation into a source file, validate it against the schema using the project's GraphQL ESLint setup:
# Run from webapp dir (force-app/main/default/webapplications/<app-name>/)
npx eslint <path-to-file-containing-mutation>
How it works: The ESLint config uses @graphql-eslint/eslint-plugin with its processor, which extracts GraphQL operations from gql template literals in .ts/.tsx files and validates the extracted .graphql virtual files against schema.graphql.
Rules enforced: no-anonymous-operations, no-duplicate-fields, known-fragment-names, no-undefined-variables, no-unused-variables
On failure: Fix the reported issues, re-run npx eslint <file> until clean, then proceed to testing.
⚠️ Prerequisites: The
schema.graphqlfile must exist (invokeexploring-graphql-schemafirst) and project dependencies must be installed (npm install).
Generated Mutation Query Testing
Triggering conditions — ALL conditions must be true:
- The Mutation Query Generation Workflow completed with status
SUCCESSand you have a generated query - The query is a mutation query
- A non-manual method was used during schema exploration to retrieve introspection data
Workflow
- Report Step - State the exact method you will use to test (e.g.,
sf api request graphqlfrom the project root, Connect API, etc.) — this MUST match the method used during schema exploration - Interactive Step - Ask the user whether they want you to test the query using the proposed method
- STOP and WAIT for the user's answer. Do NOT proceed until the user responds. Do NOT assume consent.
- Input Arguments - You MUST ask the user for the input argument values to use in the test
- STOP and WAIT for the user's answer. Do NOT proceed until the user provides values. Do NOT fabricate test data.
- Test Query - Only if the user explicitly agrees and has provided input values:
- Execute the mutation using the reported method (e.g.,
sf api request restto POST the query and variables to the GraphQL endpoint):sf api request rest /services/data/v65.0/graphql \ --method POST \ --body '{"query":"mutation mutateEntity($input: EntityNameOperationInput!) { uiapi { EntityNameOperation(input: $input) { Record { Id } } } }","variables":{"input":{"EntityName":{"Field":"Value"}}}}' - Replace
v65.0with the API version of the target org - Replace the
queryvalue with the generated mutation query string - Replace the
variablesvalue with the user-provided input arguments
- Execute the mutation using the reported method (e.g.,
- Result Analysis - Retrieve the
dataanderrorsattributes from the returned payload, and report the result of the test as one of the following options:PARTIALifdatais not an empty object, buterrorsis not an empty list - Explanation: some of the queried fields are not accessible on mutationsFAILEDifdatais an empty object - Explanation: the query is not validSUCCESSiferrorsis an empty list
- Remediation Step - If status is not
SUCCESS, use theFAILEDorPARTIALstatus handling workflows
FAILED Status Handling Workflow
The query is invalid:
- Error Analysis - Parse and categorize the specific error messages
- Root Cause Identification - Use error message to identify the root cause:
- Execution - Error contains
invalid cross reference idorentity is deleted - Syntax - Error contains
invalid syntax - Validation - Error contains
validation error - Type - Error contains
VariableTypeMismatchorUnknownType - Navigation - Error contains
is not currently available in mutation results - API Version - Query deals with updates, you're testing with Connect API and error contains
Cannot invoke JsonElement.isJsonObject()
- Execution - Error contains
- Targeted Resolution - Depending on the root cause categorization
- Execution - You're trying to update or delete an unknown/no longer available entity: either create an entity first, if you have generated the related query, or ask for a valid entity id to use. STOP and WAIT for the user to provide a valid Id
- Syntax - Update the query using the error message information to fix the syntax errors
- Validation - The field name is most probably invalid. Re-run the relevant grep command from the
exploring-graphql-schemaskill to verify the correct field name. If still unclear, ask the user for clarification and STOP and WAIT for their answer - Type - Use the error details and re-verify the type via grep lookup in the schema. Correct the argument type and adjust variables accordingly
- Navigation - Use the
PARTIALstatus handling workflow below - API Version -
Recordselection is only available with API version 64 and higher, report the issue, and try again with API version 64
- Test Again - Resume the query testing workflow with the updated query (increment and track attempt counter)
- Escalation Path - If targeted resolution fails after 2 attempts, ask for additional details and restart the entire GraphQL workflow from the
exploring-graphql-schemaskill
PARTIAL Status Handling Workflow
The query can be improved:
- Report the fields mentioned in the
errorslist - Explain that these fields can't be queried as part of a mutation query
- Explain that the query might be considered as failing, as it will report errors
- Offer to remove the offending fields
- STOP and WAIT for the user's answer. Do NOT remove fields without explicit consent.
- If they are OK with removing the fields restart the generation workflow with the new field list
Related Skills
- Schema exploration:
exploring-graphql-schema(must complete first) - Read query generation:
generating-graphql-read-query