SfSkills Domains Docs GitHub

Home / integration / graphql-api-patterns

graphql-api-patterns

Use when designing or reviewing Salesforce GraphQL API usage, especially endpoint selection, field shaping, connection-based pagination, LWC wire adapters, and GraphQL vs REST tradeoffs. Triggers: 'GraphQL API'. NOT for building a custom GraphQL server or for generic REST integration design with no GraphQL component — use integration/rest-api-patterns.

Open the skill folder on GitHub


Graphql Api Patterns

Use this skill when the integration question is really about query shape and client efficiency, not just about calling another endpoint. Salesforce GraphQL is strongest when a consumer needs flexible field selection from a single endpoint and wants to avoid a chain of UI API or REST requests.


Before Starting

Gather this context before working on anything in this domain:

Questions to Ask Before Configuring

QuestionWhy it mattersWhat a good answer addsWhat proper configuration adds over just doing it
"Is the client an LWC, a mobile offline app, or a server integration?"LWC uses lightning/graphql (v2) by default; Mobile Offline must use lightning/uiGraphQLApi (v1); servers POST to /services/data/vXX.X/graphqlThe adapter or endpoint per clientNo v1-only code where v2 features are needed, and no broken offline priming
"Which API version will the client pin?"Mutations are GA from v66.0 (beta v59.0 to v65.0); upper-bound pagination needs v59.0, switching to it mid-query v60.0; five-level parent traversal needs v58.0The minimum version for each feature usedFeatures that exist in the pinned version instead of runtime schema errors
"How many records per view, and does the UI need totals?"Default page is 10; up to 2,000 records per subquery; without upperBound relay paging stops at 4,000; totalCount can be expensiveThe first, after, and upperBound planPredictable payloads and no silent truncation at 4,000 rows
"Will the query write data, and must related writes succeed together?"Mutations default to allOrNone: true; with false, failed operations and their dependents roll back while others commit; creating a record with child relationships is not supportedThe allOrNone choice and the write orderWrites that fail the way the business expects
"Which users run it, including guest users?"The schema honors each user's object and field access, so two users see different schemas; guest access is controlled in API Access ControlsA test user per personaQueries that work for every audience, not just the developer
"How will the client treat a 200 response that carries errors?"GraphQL returns 200 with an errors array for syntax, validation, and fetch errors; 503 signals rate limitingAn error-handling contractPartial failures that surface instead of rendering empty components

What a proper configuration adds over "just writing a query": the adapter and API version match the features used, paging is planned past the 4,000-row relay limit, writes have explicit transaction behavior, and partial errors reach the user.


Core Concepts

GraphQL Is A Single Endpoint With Client-Shaped Responses

Salesforce GraphQL requests go to /services/data/vXX.X/graphql. The client controls the selection set, so payload size and nesting discipline matter more than they do with fixed REST resources.

Variables Beat String-Built Query Text

Keep the query document stable and pass runtime values through GraphQL variables. This improves safety, readability, and cache behavior. String-building query text with user input is the wrong default.

Adapter Choice Matters In LWC

For most new LWC use cases, prefer lightning/graphql. Use lightning/uiGraphQLApi only when Mobile Offline compatibility is the actual requirement. Treat adapter selection as an architectural decision, not just an import statement.

Capability (LWC Developer Guide, GraphQL API Wire Adapter Comparison)lightning/uiGraphQLApi (v1)lightning/graphql (v2)
Mobile OfflineSupportedNot currently supported
Optional fieldsNot supportedSupported
Dynamic query construction, including ${} interpolation in gql (for example composing a fragment from another component)Not supportedSupported
Variables in directives such as @skip and @includeNot supportedListed as supported in the LWC guide; the GraphQL API guide's wire adapter limitations page says it is not (UNVERIFIED which is current)
RecommendationOnly for Mobile Offline"We recommend that you use lightning/graphql (v2) where possible"

gql is not reactive: runtime values belong in variables, exposed through a getter, so the wire re-runs when they change.

Limits That Shape the Query

Limit (GraphQL API Developer Guide, Query Limitations and Pagination)Value
Subqueries per GraphQL query10, each counted as one request for rate limiting
Records per subqueryUp to 2,000; default page size is 10
Relay pagination without upperBoundUp to 4,000 records in total
upperBound paginationAPI v59.0+; first must be 200 to 2,000; switching to it in a later request needs v60.0
Child-to-parent relationshipsUp to 55 per query; up to 5 levels (v58.0+), 2 levels in v57.0 and earlier
Parent-to-child relationshipsUp to 20 per query; 1 level
ObjectsOnly those User Interface API supports
Rate limitingShares Connect API limits; exceeding them returns 503

Partial Data And Pagination Need Intentional Handling

GraphQL can return data and errors together, and the HTTP status is usually 200 even when the request contains an invalid object or field name (ValidationError), a syntax error (InvalidSyntax), or a fetch failure (DataFetchingException). Connection-style pagination and cursor handling should be part of the design, not an afterthought added once result sets get large.

Mutations Exist and Have Transaction Rules

Mutations are beta in v59.0 to v65.0 and generally available from v66.0; the LWC wire adapter supports them from v66.0. The uiapi mutation field takes allOrNone (default true). Creating a record together with child relationships is not supported, and update and delete requests cannot return queried fields. An earlier version of this skill's anti-patterns file called the API read-only; that is out of date.


Common Patterns

LWC Query Adapter Pattern

When to use: An LWC needs flexible reads with fewer round trips than separate wire adapters or Apex endpoints.

How it works: Define a static gql document, pass runtime values through variables, and keep the selection set intentionally small.

Why not the alternative: Raw fetch() calls to the endpoint duplicate logic the platform adapter already handles.

GraphQL For View Models, REST For Operational Actions

When to use: The UI needs shaped read data, but writes or side effects are clearer in REST or Apex services.

How it works: Use GraphQL to gather the read model and keep operational commands on dedicated APIs.

Cursor-Based Pagination

When to use: Result sets can exceed a single page and the client needs stable incremental loading.

How it works: Use the connection model and track cursors instead of assuming offset-style paging.


Decision Guidance

SituationRecommended ApproachReason
Client needs flexible read shape from one endpointGraphQLReduces over-fetching and request fan-out
Team needs explicit endpoint contracts for operational actionsREST or Apex RESTBetter fit for command-style APIs
LWC needs GraphQL and offline supportlightning/uiGraphQLApiAdapter exists for that narrower requirement
LWC needs GraphQL without offline requirementlightning/graphqlPreferred default adapter for most new work

  1. Identify the client and pin the API version; check the version-gated features (mutations, upperBound, relationship depth) against it.
  2. Choose the surface: lightning/graphql for LWC, lightning/uiGraphQLApi only for Mobile Offline, POST /services/data/vXX.X/graphql for servers.
  3. Write a named operation (query accountsByIndustry) with a minimal selection set and runtime values in variables; keep gql documents static except for fragment composition in v2.
  4. Plan paging: first plus after cursors for small sets, upperBound for more than 200 rows and anything that may exceed 4,000.
  5. Handle both data and errors on every response, including 200 responses with errors, and back off on 503.
  6. Test with each persona, because each user's schema reflects their object and field access. A deployable component is in 0.
  7. Run python3 skills/integration/graphql-api-patterns/scripts/check_graphql_api_patterns.py --manifest-dir force-app/main/default and resolve every finding.

Review Checklist

Run through these before marking work in this area complete:


Salesforce-Specific Gotchas

One-line summaries; the full entries are in 0.

GotchaShort form
Over-fetchingOne endpoint does not make large nested payloads free; totalCount costs a full lookup
Adapter choicelightning/graphql is the default; uiGraphQLApi only for Mobile Offline
Errors on 200Clients that check only the HTTP status miss validation and fetch errors
4,000-row ceilingRelay paging without upperBound stops at 4,000 records
MutationsGA from v66.0; allOrNone defaults to true; no child-relationship creates
Per-user schemaFields a user cannot access are not in that user's schema

Output Artifacts

ArtifactDescription
API choice reviewRecommendation for GraphQL versus REST or Composite
Query design reviewFindings on variables, selection sets, pagination, and adapter use
GraphQL request scaffoldStable query document and variables pattern for the chosen client

Package files