Salesforce has recently introduced Cursor, a powerful new feature (still in Beta) that makes working with large datasets much easier. Traditionally, when we query thousands of records, Apex tries to load everything into memory, which can lead to limits, slow processing, and performance issues.
With Apex Cursors, you no longer have to worry about that. Instead of pulling all records at once, you can fetch your query results in small, manageable chunks, and even navigate forward and backward through the data. This is incredibly useful when you’re building UI experiences that rely on large volumes of records.
If you’re working with Lightning Web Components (LWC) or planning to build solutions that need to handle big datasets smoothly, this new capability opens up some really exciting possibilities.
Also, check this: Use deleteRecord in LWC Salesforce
Key Highlights :
- Efficiently query large datasets without hitting limits
- Load records progressively for a smoother user experience
- Improve scalability and performance
- Build cleaner UI interactions powered by Cursor-based data retrieval
Code :
CursorController.cls:
public with sharing class CursorController {
@AuraEnabled
public static CursorResultWrapper processCursor(CursorResultWrapper wrapper){
try {
Database.Cursor cursorLocator;
if(wrapper.stringlocator == null){
cursorLocator = Database.getCursor('SELECT Id, Name FROM Contact');
}else{
cursorLocator = (Database.Cursor)JSON.deserializeStrict(wrapper.stringlocator, Database.Cursor.class);
}
wrapper.records = cursorLocator.fetch(wrapper.position, wrapper.batchSize);
wrapper.stringlocator = JSON.serialize(cursorLocator);
wrapper.position++;
return wrapper;
} catch (Exception e) {
throw new AuraHandledException(e.getMessage());
}
}
public class CursorResultWrapper{
@auraEnabled
public Integer position{get;set;}
@auraEnabled
public Integer batchSize{get;set;}
@auraEnabled
public List<Contact> records{get;set;}
@auraEnabled
public string stringlocator{get;set;}
}
}
cursorLWC.HTML:
<template>
<lightning-card title="Call Next Chunk" icon-name="utility:connected_apps">
<div class="slds-m-around_medium">
<lightning-button label="Next Result set" onclick={callNextChunkFromCursor}></lightning-button>
<p class="slds-m-top_medium">current cursor position : {cursorWrapper.position}</p>
<p class="slds-m-top_medium">cursor chunk size: {cursorWrapper.batchSize}</p>
<p class="slds-m-top_medium">cursor results</p>
<template lwc:if={cursorWrapper.records} for:each={cursorWrapper.records} for:item="contact">
<p key={contact.Id}>{contact.Name}</p>
</template>
</div>
</lightning-card>
</template>
cursorLWC.JS:
import { LightningElement, track } from 'lwc';
import processCursor from '@salesforce/apex/CursorController.processCursor';
export default class ProcessLargeDataset extends LightningElement {
@track cursorWrapper = {
position : 0,
batchSize : 2,
records : null,
stringlocator: null
}
connectedCallback(){
this.callNextChunkFromCursor(true);
}
async callNextChunkFromCursor(isloading) {
try {
let result = await processCursor({ wrapper: this.cursorWrapper });
this.cursorWrapper = JSON.parse(JSON.stringify(result));
} catch (error) {
console.log(error);
}
}
}
cursorLWC.JS-meta.xml:
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>63.0</apiVersion>
<isExposed>true</isExposed>
<targets>
<target>lightning__HomePage</target>
</targets>
</LightningComponentBundle>
