Read-only @AuraEnabled method is not cacheable

ID

apex.performance.server_action_cacheable

Severity

low

Remediation Complexity

trivial

Remediation Risk

medium

Remediation Effort

low

Resource

Performance

Language

Apex

Description

Reports an @AuraEnabled method that queries records and performs no DML, declared without cacheable=true.

Rationale

Without cacheable=true every component instance pays a full server round trip for data that has not changed, and Lightning Data Service cannot serve it from the client cache. On a record page with several components, or a list refreshed as the user scrolls, that is a visible delay and server work repeated for the same answer. Marking a read-only action cacheable also makes it usable with @wire, which is how a Lightning web component is meant to consume data.

The following code illustrates the pattern detected by this rule:

public with sharing class AccountListController {

    @AuraEnabled
    public static List<Account> loadAccounts() {
        // FLAGGED: Read-only @AuraEnabled method is not cacheable
        return [SELECT Id, Name FROM Account ORDER BY Name];
    }

    @AuraEnabled(cacheable=true)
    public static List<Account> loadAccountsCached() {
        return [SELECT Id, Name FROM Account ORDER BY Name];
    }

    @AuraEnabled
    global static List<Account> findAndTouch(String name) {
        List<Account> found = [SELECT Id, Name FROM Account WHERE Name = :name];
        for (Account a : found) {
            a.Description = 'seen';
        }
        Database.update(found, false);
        return found;
    }

    @AuraEnabled
    global static void purge(String name) {
        List<Account> found = [SELECT Id FROM Account WHERE Name = :name];
        delete found;
    }

    @AuraEnabled
    public static void renameAccount(Id accountId, String name) {
        Account a = [SELECT Id FROM Account WHERE Id = :accountId];
        a.Name = name;
        update a;
    }
}

Remediation

Declare the method @AuraEnabled(cacheable=true). The platform then rejects DML inside it, so keep the writes in a separate, non-cacheable action - which is the split the component should have anyway.

Configuration

This detector does not need any configuration.

© 2026 Xygeni. All rights reserved