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
In a nutshell
  • @AuraEnabled methods for LWC are static; cacheable=true for 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.

LWCAuraEnabledApex methodDataFlow or AgentInvocableMethodApex methodData

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, and public or global.
  • cacheable=true allows @wire and client caching, but the method must not perform DML.
  • Parameters and return types must be serializable: primitives, sObjects, collections, or classes with @AuraEnabled properties.

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;
        }
    }
}
WireImperative
Runs automatically and again when reactive parameters changeRuns when your code calls it, such as on a click
Needs cacheable=trueAny @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

CallerAnnotation
Flow (Apex action)@InvocableMethod with a List parameter; @InvocableVariable on input and output class fields
Agentforce agent actionThe same @InvocableMethod, with a clear label and description on the method and each @InvocableVariable
Agentforce prompt templatesCan 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.

Exam tip

"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." @InvocableMethod with a good description.
  • "Show a friendly error message from Apex." AuraHandledException.

What to remember for the exam

  • @AuraEnabled methods for LWC are static; cacheable=true for 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.
Watch out

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.