Learn how the Apex @ReadOnly annotation lets you query up to 1,000,000 rows in read-only contexts — perfect for reporting and data exports. This post explains behavior, restrictions, and best practices for Salesforce developers.
The @ReadOnly annotation in Apex relaxes the standard SOQL row limit (50,000) and allows queries that return up to 1,000,000 rows in supported read-only contexts. It’s a powerful tool when you need to process or export very large datasets without modifying records.
What @ReadOnly Does
When you annotate a method with @ReadOnly, Salesforce increases the allowed number of queried rows for that request to 1,000,000. This is ideal for heavy read operations such as reporting, analytics, or bulk exports.
Key points
- Queries can return up to 1,000,000 rows (instead of the normal 50,000 SOQL limit).
 - It only applies to supported contexts (REST/SOAP web services and Schedulable).
 - The method is strictly read-only — DML and certain system operations are blocked.
 
Restrictions and Limitations
- No DML operations: insert, update, delete, undelete are not allowed inside a @ReadOnly method.
 - No asynchronous job enqueueing: calls to System.schedule or queueable/future/Batch submission are blocked.
 - Context limited: Only available for REST and SOAP web services and classes that implement Schedulable.
 - Shared behavior: Governor limits still apply for CPU time and heap size — design queries and processing accordingly.
 
When to Use @ReadOnly
Common use cases where @ReadOnly helps:
- Reporting and analytics that require scanning large tables.
 - Data export jobs that read records for backup or external processing.
 - Read-only dashboards or scheduled reads where no record changes are needed.
 
Example
Simple Apex example that uses @ReadOnly in an AuraEnabled method to return Accounts created in the last year:
public with sharing class LargeDataQueryController {
    @AuraEnabled
    @ReadOnly
    public static List getAccounts() {
        // This query will be read-only and optimized for performance
        return [SELECT Id, Name, Industry FROM Account WHERE CreatedDate >= LAST_YEAR];
    }
}
 Best Practices
- Use selective WHERE clauses and indexed fields to avoid full table scans.
 - Process records in streaming or batched server-side patterns to minimize heap usage.
 - Keep the method strictly read-only — if you need to modify data, use a separate non-@ReadOnly transaction.
 - Test with realistic data volumes in a full or sandbox-like org to validate CPU and heap usage.
 
References
Salesforce Apex documentation: ReadOnly
Why this matters: For Salesforce admins, developers, and architects, @ReadOnly unlocks scalable read operations so you can build high-volume reports and exports without hitting SOQL row limits — but you must design carefully to respect other governor limits and restrictions.








Leave a Reply