afv-library/skills/generating-webapp-graphql-mutation-query/SKILL.md
k-j-kim 9ba064174a
feat: syncing webapp skills sync to afv @W-21338965@ (#57)
* 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>
2026-03-19 22:46:27 +05:30

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.
**/*.ts
**/*.tsx
**/*.graphql

Salesforce GraphQL Mutation Query Generation

Triggering conditions

  1. Only if the schema exploration phase completed successfully (invoke exploring-graphql-schema first)
  2. 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-schema skill. Do NOT open, read, stream, or parse ./schema.graphql with 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:

  1. 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
  2. Output Fields Validation - Validate that the set of fields used in the select part of the query validate the output fields constraints
  3. 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
  4. Report Phase - Use the Mutation Query Report Template below to report on the previous validation phases
  5. Input Arguments - input is the default name for the argument, unless otherwise specified
  6. Output Field - For Create and Update operations, the output field is always named Record, and is of type EntityName
  7. 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
  8. Query Generation - Use the mutation query template and adjust it based on the selected operation
  9. Output Format - Use the standalone
  10. 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
  11. Test the Query - Use the Generated Mutation Query Testing workflow to test the generated query
    1. 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

  1. Create Mutation Queries:
    1. MUST include all required fields
    2. MUST only include createable fields
    3. Child relationships can't be set and MUST be excluded
    4. Fields with type REFERENCE can only be assigned IDs through their ApiName name
  2. Update Mutation Queries:
    1. MUST include the id of the entity to update
    2. MUST only include updateable fields
    3. Child relationships can't be set and MUST be excluded
    4. Fields with type REFERENCE can only be assigned IDs through their ApiName name
  3. Delete Mutation Queries:
    1. MUST include the id of the entity to delete

Mutation Queries Output Field Constraints

  1. Create and Update Mutation Queries:
    1. MUST exclude all child relationships
    2. MUST exclude all REFERENCE fields, unless accessed through their ApiName member (no navigation to referenced entity)
    3. Inaccessible fields will be reported as part of the errors attribute in the returned payload
    4. Child relationships CAN'T be queried as part of a mutation
    5. Fields with type REFERENCE can only be queried through their ApiName (no referenced entities navigation, no sub fields)
  2. Delete Mutation Queries:
    1. MUST only include the Id field

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 gql tagged 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.graphql file must exist (invoke exploring-graphql-schema first) and project dependencies must be installed (npm install).

Generated Mutation Query Testing

Triggering conditionsALL conditions must be true:

  1. The Mutation Query Generation Workflow completed with status SUCCESS and you have a generated query
  2. The query is a mutation query
  3. A non-manual method was used during schema exploration to retrieve introspection data

Workflow

  1. Report Step - State the exact method you will use to test (e.g., sf api request graphql from the project root, Connect API, etc.) — this MUST match the method used during schema exploration
  2. Interactive Step - Ask the user whether they want you to test the query using the proposed method
    1. STOP and WAIT for the user's answer. Do NOT proceed until the user responds. Do NOT assume consent.
  3. Input Arguments - You MUST ask the user for the input argument values to use in the test
    1. STOP and WAIT for the user's answer. Do NOT proceed until the user provides values. Do NOT fabricate test data.
  4. Test Query - Only if the user explicitly agrees and has provided input values:
    1. Execute the mutation using the reported method (e.g., sf api request rest to 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"}}}}'
      
    2. Replace v65.0 with the API version of the target org
    3. Replace the query value with the generated mutation query string
    4. Replace the variables value with the user-provided input arguments
  5. Result Analysis - Retrieve the data and errors attributes from the returned payload, and report the result of the test as one of the following options:
    1. PARTIAL if data is not an empty object, but errors is not an empty list - Explanation: some of the queried fields are not accessible on mutations
    2. FAILED if data is an empty object - Explanation: the query is not valid
    3. SUCCESS if errors is an empty list
  6. Remediation Step - If status is not SUCCESS, use the FAILED or PARTIAL status handling workflows

FAILED Status Handling Workflow

The query is invalid:

  1. Error Analysis - Parse and categorize the specific error messages
  2. Root Cause Identification - Use error message to identify the root cause:
    • Execution - Error contains invalid cross reference id or entity is deleted
    • Syntax - Error contains invalid syntax
    • Validation - Error contains validation error
    • Type - Error contains VariableTypeMismatch or UnknownType
    • 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()
  3. 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-schema skill 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 PARTIAL status handling workflow below
    • API Version - Record selection is only available with API version 64 and higher, report the issue, and try again with API version 64
  4. Test Again - Resume the query testing workflow with the updated query (increment and track attempt counter)
  5. Escalation Path - If targeted resolution fails after 2 attempts, ask for additional details and restart the entire GraphQL workflow from the exploring-graphql-schema skill

PARTIAL Status Handling Workflow

The query can be improved:

  1. Report the fields mentioned in the errors list
  2. Explain that these fields can't be queried as part of a mutation query
  3. Explain that the query might be considered as failing, as it will report errors
  4. Offer to remove the offending fields
  5. STOP and WAIT for the user's answer. Do NOT remove fields without explicit consent.
  6. If they are OK with removing the fields restart the generation workflow with the new field list
  • Schema exploration: exploring-graphql-schema (must complete first)
  • Read query generation: generating-graphql-read-query