User Interface · Topic 4 of 4
Apex for Lightning Components, Flow and Agentforce
Calling Apex from Lightning Web Components, Flow and Agentforce: AuraEnabled methods, wire versus imperative calls, refreshApex and invocable actions.
- 4 min
@AuraEnabledmethods for LWC are static;cacheable=truefor wire, no DML allowed then.- Wire is reactive and cached; imperative returns a Promise and can do DML.
refreshApex()refreshes a wired result after data changes.
What it is
When Lightning Data Service is not enough, a component calls Apex for data or actions. The same business logic can also serve Flow (as an invocable action) and Agentforce agents (as an agent action).
Each caller needs a different annotation, but all of them share one rule: the Apex must be bulk-safe, secure and return clear errors.
How it works
Apex for Lightning Web Components
public with sharing class CaseController {
@AuraEnabled(cacheable=true)
public static List<Case> getOpenCases(Id accountId) {
return [SELECT Id, Subject, Status FROM Case
WHERE AccountId = :accountId AND IsClosed = false WITH USER_MODE];
}
@AuraEnabled
public static void closeCases(List<Id> caseIds) {
List<Case> cs = new List<Case>();
for (Id i : caseIds) cs.add(new Case(Id = i, Status = 'Closed'));
update as user cs;
}
}
Rules for @AuraEnabled methods:
static, andpublicorglobal.cacheable=trueallows@wireand client caching, but the method must not perform DML.- Parameters and return types must be serializable: primitives, sObjects, collections, or classes with
@AuraEnabledproperties.
Wire versus imperative calls
import { LightningElement, api, wire } from 'lwc';
import { refreshApex } from '@salesforce/apex';
import getOpenCases from '@salesforce/apex/CaseController.getOpenCases';
import closeCases from '@salesforce/apex/CaseController.closeCases';
export default class OpenCases extends LightningElement {
@api recordId;
@wire(getOpenCases, { accountId: '$recordId' }) cases;
async handleClose() {
try {
await closeCases({ caseIds: this.selectedIds });
await refreshApex(this.cases);
} catch (error) {
this.message = error.body.message;
}
}
}
| Wire | Imperative |
|---|---|
| Runs automatically and again when reactive parameters change | Runs when your code calls it, such as on a click |
Needs cacheable=true | Any @AuraEnabled method, including DML |
Result in { data, error } | Returns a Promise |
Refresh with refreshApex(wiredResult) | Call it again |
Errors to the UI
Throw an AuraHandledException with a user-friendly message. Other exceptions may reach the client with a generic message; the component reads error.body.message.
Apex for Flow and Agentforce
| Caller | Annotation |
|---|---|
| Flow (Apex action) | @InvocableMethod with a List parameter; @InvocableVariable on input and output class fields |
| Agentforce agent action | The same @InvocableMethod, with a clear label and description on the method and each @InvocableVariable |
| Agentforce prompt templates | Can also be agent actions and can ground prompts with Apex or flows |
Agents choose actions from their descriptions, so vague descriptions cause wrong action choices or wrong inputs. Agent actions run as the agent's running user, so with sharing and user mode checks decide what the action may read or change.
@InvocableMethod(label='Check Warranty'
description='Returns warranty status and end date for the given asset serial numbers')
public static List<Result> run(List<Request> requests) { /* bulk-safe body */ }
A worked example
An LWC on the Account page shows open cases with a "Close selected" button. The list is loaded with @wire on a cacheable method. The button calls a separate, non-cacheable method imperatively, because it performs DML. After it succeeds, refreshApex() reloads the wired list so closed cases disappear. Putting the update inside the cacheable method would fail, because cacheable methods cannot do DML.
"The list does not refresh after an update" points to refreshApex() on the wired result. "Method called with @wire fails with DML" points to separating read and write methods.
How the exam asks about this
- "Call Apex when a user clicks a button." An imperative call.
- "Expose Apex as an agent action."
@InvocableMethodwith a good description. - "Show a friendly error message from Apex."
AuraHandledException.
What to remember for the exam
@AuraEnabledmethods for LWC are static;cacheable=truefor wire, no DML allowed then.- Wire is reactive and cached; imperative returns a Promise and can do DML.
refreshApex()refreshes a wired result after data changes.- Flow and agents call Apex through
@InvocableMethod; descriptions guide agents. - Enforce sharing and field access in all three cases.
A cacheable method can return stale data from the client cache. After changing records, call refreshApex() or the user sees old values.
Common mistakes
Doing DML in a cacheable method. It throws an error; use a separate non-cacheable method.
Expecting a wired method to run on a button click. Wire runs on load and on parameter changes; call imperatively for clicks.
Leaving invocable actions without descriptions for agents. The agent picks the wrong action or wrong inputs.
Check yourself
What must be true of an Apex method used with
@wire?It must be
@AuraEnabled(cacheable=true), static, and must not perform DML.How do you reload a wired list after an imperative update?
Call
refreshApex()with the wired property's value.Which annotation lets an Agentforce agent call an Apex method as an action?
@InvocableMethod.
Topic 4 of 4 in User Interface
Test yourself on User Interface
The full Salesforce Platform Developer question bank has 645 questions, with timed practice exams and an explanation for every answer.
This study guide is independent and is not affiliated with, endorsed by, or sponsored by Salesforce. Vendor names, certification names and exam codes are trademarks of their respective owners and are used for identification only.