Change Data Capture (CDC) Receive near-real-time changes of Salesforce records, and synchronize corresponding records in an external data store. Change Data Capture publishes change events, which represent changes to Salesforce records. Changes include creation of a new record, updates to an existing record, deletion of a record, and undeletion of a record. We can subscribe this in LWC using Emp Api.
Automate With Change Data Capture using Emp Api
Business asked to create a task whenever user manually change the status to Closed Won. Architect does not wants you to write apex trigger or create record triggered flows. This can be achieved by using Change Data Capture events and generic LWC placed on opportunity lightning record page.
How to enable CDC?
- Go to setup
- Search Change Data Capture
- Add Entities

Sample Response of CDC
This demonstrate the sample JSON response from Change Data Capture event paylaod.
{
"data": {
"schema": "F6bowHIr-cOJQX8LrZSSOQ",
"payload": {
"LastModifiedDate": "2023-01-18T09:19:02Z",
"ForecastCategory": "Closed",
"IsClosed": true,
"ExpectedRevenue": 15000,
"IsWon": true,
"StageName": "Closed Won",
"Probability": 100,
"LastStageChangeDate": "2023-01-18T09:19:02Z",
"ChangeEventHeader": {
"commitNumber": 10932624199415,
"commitUser": "005B0000008JWlBIAW",
"sequenceNumber": 1,
"entityName": "Opportunity",
"changeType": "UPDATE",
"changedFields": [
"StageName",
"Probability",
"ExpectedRevenue",
"IsClosed",
"IsWon",
"ForecastCategory",
"ForecastCategoryName",
"LastModifiedDate",
"LastStageChangeDate"
],
"changeOrigin": "com/salesforce/api/soap/57.0;client=SfdcInternalAPI/",
"transactionKey": "00184570-30a2-a0f5-a9f3-d3c89a97026e",
"commitTimestamp": 1674033542000,
"recordIds": [
"006B0000008v9DaIAI"
]
},
"ForecastCategoryName": "Closed"
},
"event": {
"replayId": 65738599
}
},
"channel": "/data/OpportunityChangeEvent"
}
Subscribe to CDC using Emp API in LWC
Let’s create a LWC with name changeDataCaptureSubscriber. This is very simple component that can be placed on any record page. We will be using it on Opportunity. As we are going to create task, We will use an apex controller named changeDataCaptureController.
changeDataCaptureController.cls
AssignTask is a simple apex method to insert a task and assign it to opportunity owner.
public with sharing class changeDataCaptureController {
@AuraEnabled
public static void AssignTask(String recordid){
try {
Opportunity opp = [select ownerId from Opportunity where id=:recordid];
Task tsk = new Task();
tsk.ownerId = opp.ownerId;
tsk.WhatId = recordid;
tsk.Subject = 'Complete Deal Closing Formalities';
tsk.Status = 'Not Started';
tsk.Priority = 'High';
insert tsk;
} catch (Exception e) {
throw new AuraHandledException(e.getMessage());
}
}
}
changeDataCaptureSubscriber.html
This component does not have any UI.
<template>
</template>
changeDataCaptureSubscriber.js
The lightning/empApi
module provides access to methods for subscribing to a streaming channel and listening to event messages. All streaming channels are supported, including channels for platform events, PushTopic events, generic events, and Change Data Capture events. This component requires API version 44.0 or later.
import { LightningElement, api } from 'lwc';
import { subscribe, unsubscribe, onError } from 'lightning/empApi';
import AssignTask from '@salesforce/apex/changeDataCaptureController.AssignTask';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';
export default class ChangeDataCaptureSubscriber extends LightningElement {
channelName = '/data/OpportunityChangeEvent';
subscription = {};
@api recordId;
subscribed;
// Tracks changes to channelName text field
handleChannelName(event) {
this.channelName = event.target.value;
}
renderedCallback() {
if (!this.subscribed) {
this.handleSubscribe();
this.subscribed = true;
}
}
// Initializes the component
connectedCallback() {
// Register error listener
this.registerErrorListener();
}
// Handles subscribe button click
handleSubscribe() {
// Callback invoked whenever a new event message is received
const messageCallback = (response) => {
console.log('New message received: ', JSON.stringify(response));
// Response contains the payload of the new message received
this.handleMessage(response);
};
// Invoke subscribe method of empApi. Pass reference to messageCallback
subscribe(this.channelName, -1, messageCallback).then(response => {
// Response contains the subscription information on subscribe call
console.log('Subscription request sent to: ', JSON.stringify(response.channel));
this.subscription = response;
});
}
// Handles unsubscribe button click
handleUnsubscribe() {
// Invoke unsubscribe method of empApi
unsubscribe(this.subscription, (response) => {
console.log('unsubscribe() response: ', JSON.stringify(response));
// Response is true for successful unsubscribe
});
}
registerErrorListener() {
// Invoke onError empApi method
onError((error) => {
console.log('Received error from server: ', JSON.stringify(error));
// Error contains the server-side error
});
}
handleMessage(response) {
if (response) {
if (response.hasOwnProperty('data')) {
let responsePayload = response.data.payload;
if (responsePayload.hasOwnProperty('StageName') && responsePayload.hasOwnProperty('ChangeEventHeader')) {
if (responsePayload.ChangeEventHeader.hasOwnProperty('recordIds') && responsePayload.StageName == 'Closed Won') {
let currentRecordId = responsePayload.ChangeEventHeader.recordIds.find(element => element == this.recordId);
console.log('currentRecordId', currentRecordId);
if (currentRecordId) {
AssignTask({
recordid: this.recordId
}).then(result => {
const event = new ShowToastEvent({
title: 'Task Assigned',
message: 'A task has been assigned to you, please complete them.',
variant: 'success'
});
this.dispatchEvent(event);
}).catch(error => {
console.log(error);
})
}
}
}
}
}
}
}
changeDataCaptureSubscriber.js-meta.xml
We are enabling the component to be used only with record page.
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>56.0</apiVersion>
<isExposed>true</isExposed>
<targets>
<target>lightning__RecordPage</target>
</targets>
</LightningComponentBundle>
Demo – CDC In LWC
Place your component on opportunity record page and change the stage to closed won. A task will be automatically created without having any apex trigger or record trigger flows.
Do you need help?
Are you stuck while working on this requirement? Do you want to get review of your solution? Now, you can book dedicated 1:1 with me on Lightning Web Component completely free.
GET IN TOUCH
Schedule a 1:1 Meeting with me
Also check out https://salesforcediaries.com/category/lightning-web-compont