StandardSetController or the Salesforce User Interface API to pull matching records automatically.
Salesforce List Views allow business users and administrators to create custom data filters without writing code. However, developers often face challenges when trying to query those exact filtered records programmatically. Recreating complex user-configured filter logic manually inside dynamic SOQL creates maintenance overhead whenever a list view is modified. By programmatically binding to the List View ID or developer name, applications can dynamically inherit list view criteria.
1. Architectural Approaches: How Salesforce Handles List Views in Code
Salesforce provides three primary architectural mechanisms to retrieve records defined by a List View:
- Apex StandardSetController: The native programmatic approach inside Apex. By binding to an sObject collection or query locator and applying
setFilterId(listViewId), you can paginate through matching records directly. - Salesforce User Interface API (UI API): The modern standard for Lightning Web Components (LWC). Endpoints like
/ui-api/list-records/{listViewId}or the wire adaptergetListRecordsByNamefetch records, metadata, and column configs dynamically. - List View Describe REST API: Endpoints under
/services/data/vXX.X/sobjects/{sObject}/listviews/{listViewId}/describereturn the underlying SOQL query string for advanced custom execution.
- Native Apex Pattern:
ApexPages.StandardSetControllerusingsetFilterId(). - Modern UI / LWC:
lightning/uiListApi(getListRecordsByName). - Querying Metadata: Query standard object
ListViewto retrieve DeveloperName and SobjectType. - Security Standard: Always enforce
WITH USER_MODEandwith sharingto respect record-level security.
2. Implementing List View Retrieval via Apex StandardSetController
The cleanest way to pull records for a List View inside Apex without building external HTTP callouts is using the StandardSetController class.
This service class accepts an object's query locator, sets the target List View filter ID, and returns the filtered records.
public with sharing class ListViewRecordService {
/**
* @description Retrieves records filtered by a specific Salesforce List View ID
* @param sObjectType The SObject type name (e.g., 'Account')
* @param listViewId The 18-character Id of the target List View
* @param pageSize Number of records to return in the page batch
* @return List<SObject> Filtered record list
*/
public static List<SObject> getRecordsByListViewId(String sObjectType, Id listViewId, Integer pageSize) {
if (String.isBlank(sObjectType) || listViewId == null) {
throw new IllegalArgumentException('SObject Type and List View ID must not be null.');
}
// 1. Build base query locator for the target object
String baseQuery = 'SELECT Id, Name FROM ' + String.escapeSingleQuotes(sObjectType) + ' LIMIT 10000';
ApexPages.StandardSetController setController = new ApexPages.StandardSetController(
Database.getQueryLocator(baseQuery)
);
// 2. Apply the List View Filter ID
setController.setFilterId(listViewId);
// 3. Set the page chunk size
setController.setPageSize(pageSize != null && pageSize > 0 ? pageSize : 50);
// 4. Return matching records
return setController.getRecords();
}
/**
* @description Helper to find a List View ID by developer name
*/
public static Id getListViewIdByDeveloperName(String sObjectType, String devName) {
ListView lv = [
SELECT Id, DeveloperName, SobjectType
FROM ListView
WHERE SobjectType = :sObjectType
AND DeveloperName = :devName
WITH USER_MODE
LIMIT 1
];
return lv != null ? lv.Id : null;
}
}
3. Modern LWC Approach: Using User Interface API (UI API)
For modern front-end components, Salesforce provides dedicated wire adapters that fetch List View records directly in the browser without writing custom Apex:
import { LightningElement, wire } from 'lwc';
import { getListRecordsByName } from 'lightning/uiListApi';
import ACCOUNT_OBJECT from '@salesforce/schema/Account';
export default class AccountListViewViewer extends LightningElement {
@wire(getListRecordsByName, {
objectApiName: ACCOUNT_OBJECT.objectApiName,
listViewApiName: 'All_Partner_Accounts',
fields: ['Account.Id', 'Account.Name', 'Account.Type', 'Account.Industry'],
pageSize: 25
})
listViewData({ error, data }) {
if (data) {
console.log('List View Records:', data.records);
} else if (error) {
console.error('Failed to load list view:', error);
}
}
}
4. Common Traps & Platform Limitations
Writing
SELECT Query FROM ListView WHERE Id = :listViewId in standard SOQL often fails in production because the Query field is not queryable via standard Apex SOQL across all standard objects or org editions. Attempting to use a semi-join like WHERE Id IN (SELECT Id FROM ListView...) is invalid SOQL syntax. Always use StandardSetController.setFilterId() or the UI API instead of raw ListView queries.
- StandardSetController Limit:
StandardSetControllercan work with up to 10,000 records from the base query locator. - Handle List View Scope: When filtering by user-specific list views (e.g., "My Accounts"), ensure the code runs under the appropriate user context (
with sharing) so records reflect personal ownership filters. - Sanitize Dynamic Inputs: When constructing query strings for dynamic objects, sanitize all object parameters using
String.escapeSingleQuotes()to prevent injection risks.
Summary
Fetching records using List View IDs bridges the gap between user-defined declarative filters and custom programmatic business logic. By utilizing ApexPages.StandardSetController for server-side processing or modern UI API wire adapters in Lightning Web Components, developers can build scalable, responsive applications that stay synchronized with business data filters.