
INTRODUCTION
Lightning Web Components got GraphQL reads with @wire(graphql) last year—a powerful, reactive way to fetch data. But mutations (create, update, delete) still required Apex. Developers had to write backend controllers for basic CRUD operations, adding layers of boilerplate and complexity.
Spring ’26 changes this fundamentally: executeMutation from lightning/graphql brings native GraphQL mutations to LWC. Now developers can create, update, and delete records directly from components without backend Apex. Fully typed inputs. Controlled output fields. Modern developer experience.
This post explores what GraphQL mutations in LWC enable, how to implement them, and how they change component architecture.
THE BEFORE SCENARIO: APEX AS INTERMEDIARY
Why GraphQL Mutations in LWC Matter
The Old Pattern: Apex Required
Architecture (Before Spring ’26):
LWC Component
↓
Calls Apex Method
↓
Apex creates/updates/deletes record
↓
Returns result to LWC
↓
LWC processes result
Code Complexity (Before):
LWC Component:
javascript
import { LightningElement } from ‘lwc’;
import createContact from ‘@salesforce/apex/ContactController.createContact’;
export default class ContactCreator extends LightningElement {
async handleCreate() {
try {
const result = await createContact({
firstName: ‘Sam’,
lastName: ‘Smith’
});
console.log(‘Contact created:’, result);
} catch (error) {
console.error(‘Error:’, error);
}
}
}
Apex Controller (Required):
public class ContactController {
@AuraEnabled
public static Contact createContact(String firstName, String lastName) {
Contact c = new Contact(FirstName = firstName, LastName = lastName);
insert c;
return c;
}
}
Problems with This Pattern:
Problem 1: Extra Backend Layer
- Simple CRUD operations required Apex
- Boilerplate code for every operation
- Maintenance burden increases
- More code to test
Problem 2: No Type Safety for Inputs
- Apex method receives strings
- Hard to validate inputs
- Easy to miss required fields
- No IDE autocomplete for input shape
Problem 3: Over-fetching or Under-fetching
- Apex returns entire record
- Or must specify fields to return
- Hard to control exactly what comes back
- Wasted data transfer
Problem 4: Developer Experience
- Feels dated (REST, not GraphQL)
- Requires switching between LWC and Apex
- No reactive mutations
- More complex testing

COMPARISON: BEFORE VS AFTER
Side-by-Side Comparison
| Aspect | Before (Apex) | After (GraphQL) |
| Backend Required | Yes (Apex controller) | No |
| Type Safety (Inputs) | Strings, manual validation | Full types, IDE validation |
| Type Safety (Outputs) | Depends on Apex | Full GraphQL types |
| Over-fetching | Returns full record | Control exact fields |
| Code Location | Split (LWC + Apex) | LWC only |
| Testing | Need Apex tests | Just LWC tests |
| Developer Experience | Traditional, multi-file | Modern, single location |
| Learning Curve | Apex knowledge required | GraphQL knowledge required |
| Performance | Baseline | Optimized (less data) |
| Reactivity | Manual refresh | Can integrate with reactive reads |
| Maintenance | Apex + LWC sync | Single source of truth |
WHEN TO USE GRAPHQL MUTATIONS VS APEX
Making the Right Choice
Use GraphQL Mutations When:
Simple CRUD Operations
- Create, update, delete records
- No complex logic
- Straightforward data flow
Performance Matters
- Control over returned fields
- Reduce over-fetching
- Bandwidth efficiency
Type Safety Important
- Strongly typed inputs needed
- IDE validation wanted
- Compile-time checks preferred
Rapid Development
- Quick prototyping
- Minimal boilerplate
- Fast iteration
Single Component Operations
- Mutations only used in one or few components
- No shared backend logic
- Component-specific needs
Still Use Apex When:
Complex Business Logic
- Multi-step processes
- Cross-object validations
- Business rule enforcement
- Custom algorithms
Shared Logic
- Multiple components use same operation
- Centralized business rules
- DRY principle
Governor Limits Matter
- Bulk operations
- Complex data processing
- Need to optimize CPU/query time
Security Concerns
- Field-level access control critical
- Need additional validation
- Custom permission checks
External Integrations
- Callouts to external systems
- Complex data transformation
- Multi-system coordination
IMPACT ON ARCHITECTURE
How This Changes Component Design
From Three Layers to Two
Before:
LWC Component ↔ Apex Controller ↔ Database
After:
LWC Component ↔ GraphQL ↔ Database
Impact:
- Fewer files to maintain
- Less boilerplate
- Faster development
- Simpler testing
New Component Architecture
Component Responsibilities:
- UI rendering
- User interaction
- Data mutations (directly)
- Error handling
- State management
What’s Removed:
- Need for backend controller
- Backend data mapping
- Cross-layer communication
Result:
- Self-contained components
- Clearer responsibility
- Easier to understand
- Faster to develop
FINAL THOUGHTS
GraphQL mutations in LWC represent a significant evolution in component development. For years, LWC felt like a partial solution—great for UI, but you still needed Apex for anything beyond simple reads. That limitation is now gone.
executeMutation brings full CRUD capabilities to components without requiring backend code. For simple operations—which are the vast majority—you no longer need an Apex controller. That’s massive in terms of development speed and code simplicity.
The type safety is genuine. Inputs are typed. Outputs are typed. The IDE knows your data structure. Mistakes are caught at development time, not runtime.
Combined with @wire(graphql) for reactive reads, LWC now has a complete, modern data access pattern. Reads are reactive and declarative. Writes are imperative and type-safe. The combination is powerful.
This is particularly valuable for rapid development, prototyping, and components with simple operations. Not every mutation needs Apex. For many, GraphQL is the better choice: simpler, faster, cleaner.
The catch: you need to understand GraphQL syntax. But that’s becoming table-stakes for modern Salesforce development anyway.
Spring ’26 significantly raises the ceiling for what’s possible in LWC without backend code. That’s a meaningful step forward.