PushTopic in Salesforce: Real-Time Updates for Custom Apps


06/05/2024

 

 

 

What is PushTopic?

It's a tool that lets you get notified about specific changes happening to your data in Salesforce. It's secure (your data is protected) and can handle a lot of notifications at once (scalable). You tell PushTopics exactly what kind of changes you're interested in by writing a special kind of search (SOQL query). A common way to use PushTopics is to update a custom app you built within Salesforce. Whenever the data the app relies on changes, PushTopics lets the app know so it can refresh its information and show the latest updates to users. In simpler terms, PushTopics is like a real-time alert system for your Salesforce data. You tell it what to watch for, and it tells your custom apps when something important happens.

PushTopic events provide a secure and scalable way to receive notifications for changes to Salesforce data that match a SOQL query you define. A typical application of PushTopic events is refreshing the UI of a custom app from Salesforce record changes.

 

Use of push topic:

  • Receive notifications of Salesforce record changes, including create, update, delete, and undelete operations.
  • Capture changes for the fields and records that match a SOQL query.
  • Receive change notifications for only the records a user has access to based on sharing rules.
  • Limit the stream of events to only those events that match a subscription filter.

 

PushTopic events can be received by:

  • Pages in the Salesforce application.
  • Application servers outside of Salesforce.
  • Clients outside the Salesforce application.

 

Note: PushTopics is a legacy feature. Salesforce no longer adds new features or provides extensive support for it. Consider alternatives like the Streaming API for new development. Instead of PushTopic events, consider using Change Data Capture events. To learn about Change Data Capture events, see the Change Data Capture Developer Guide and the Change Data Capture Basics Trailhead module.

 

Supported PushTopic queries:

All custom objects are supported in PushTopic queries.

  • Account
  • Campaign
  • Case
  • Contact
  • ContractLineItem
  • Entitlement
  • Lead
  • LiveChatTranscript
  • MessagingSession
  • Opportunity
  • Quote
  • QuoteLineItem
  • ServiceAppointment
  • ServiceContract
  • Task
  • UserServicePresence
  • WorkOrder
  • WorkOrderLineItem

 

Unsupported PushTopic queries:

  • Queries without Id in the selected field list.
  • Semi-joins and anti-joins. for exmple- SELECT Id, Name FROM Account WHERE Id IN (SELECT AccountId FROM Contact WHERE Title = 'CEO').
  • Aggregate queries (queries that use AVG, MAX, MIN, and SUM).
  • Relationships aren’t supported, but you can reference an ID
  • Certain functionalities like ORDER BY, LIMIT, and OFFSET are not allowed.
  • Fields like LastModifiedDate and LastActivityDate are automatically updated by the system and don't trigger PushTopic notifications.

Note: Tasks that are created or updated using these methods don’t appear in task object topics in the streaming API.

  • Lead conversion
  • Entity merge
  • Mass email contacts/leads

 

Create PushTopic API notifications for the record

Execute the below code in Anonymous window

PushTopic pushTopic = new PushTopic();
pushTopic.Name = 'AccountUpdates';
pushTopic.Query ='SELECT Id, Name, Rating FROM Account';
pushTopic.ApiVersion = 46.0;
pushTopic.NotifyForFields = 'Reference';
pushTopic.NotifyForOperationCreate = true;
pushTopic.NotifyForOperationDelete = true;
pushTopic.NotifyForOperationUndelete = true;
pushTopic.NotifyForOperationUpdate = ture;
insert pushTopic;

 

See the PushTopic Channel execute the query in query editor:

Query: SELECT Id, Name, Query, ApiVersion, IsActive, NotifyForFields, NotifyForOperations, NotifyForOperationCreate, NotifyForOperationUpdate, NotifyForOperationDelete, NotifyForOperationUndelete FROM PushTopic


 

The channel name is the name of the PushTopic prefixed with “/topic/”, for example, /topic/AccountUpdates. A Bayeux client can receive streamed events on this channel. The channel name is case-sensitive when you subscribe. 

As soon as a PushTopic record is created, the system starts evaluating record creates, updates, deletes, and undeletes for matches. Whenever there’s a match, a new notification is generated. The server polls for new notifications for currently subscribed channels every second. This frequency can fluctuate depending on the overall server load.

Fields Details
ApiVersion Required Field. This is double type. It must be an API version greater than 20.0. If your query applies to a custom object from a package, this value must match the package's ApiVersion.
Description This is string type. Description of the PushTopic. Limit: 400 characters
isActive This is a boolean type. Indicates whether the record currently counts towards the organization's allocation.
Name This is string type. Required field. Descriptive name of the PushTopic, such as AccountUpdates. Limit: 25 characters. This value identifies the channel and must be unique.
NotifyForFields This is a picklist type. Specifies which fields are evaluated to generate a notification. There are four possible values: "All", "Referenced" (default), "Select", "Where"
NotifyForOperationCreate This is a boolean type. true if a create operation should generate a notification, otherwise, false. Defaults to "true". This field is available in API version 29.0 and later.
NotifyForOperationDelete This is a boolean type. true if a delete operation should generate a notification, otherwise, false. Defaults to "true". Clients must connect using the cometd/29.0 (or later) Streaming API endpoint to receive delete and undelete event notifications. This field is available in API version 29.0 and later.
NotifyForOperationUndelete This is a boolean type. true if an undelete operation should generate a notification, otherwise, false. Defaults to "true". Clients must connect using the cometd/29.0 (or later) Streaming API endpoint to receive delete and undelete event notifications. This field is available in API version 29.0 and later.
NotifyForOperationUpdate This is a boolean type. true if an update operation should generate a notification, otherwise, false. Defaults to true. This field is available in API version 29.0 and later.
NotifyForOperations This is a picklist type. Specifies which record events may generate a notification. In API version 29.0 and later, this field is read-only and doesn’t contain information about delete and undelete events. Use NotifyForOperationCreate, NotifyForOperationDelete, NotifyForOperationUndelete, and NotifyForOperationUpdate to specify which record events should generate a notification. There are four possible values: "All" (default), "Create", "Extended", and "Update". A value of Extended means that neither create or update operations are set to generate events.
Query This is string type. Required. The SOQL query statement determines which record changes trigger events to be sent to the channel. Limit: 1,300 characters.

 

PushTopic queries:

The PushTopic query defines which object record create, update, delete and undelete events generate notifications. This query must be valid SOQL query.

The following requirements apply to PushTopic :

  • The query SELECT clause must include Id. For example: SELECT Id, Name FROM ObjectName
  • Only one entity per query.
  • The object must be valid for the specified API version.

if your PushTopic query is SELECT SELECT Id, Name, Rating FROM Account, then the IdName and Rating fields are included in any notifications sent on that channel. Following is an example of a notification message that might appear in that channel:

{
    "data": {
      "event": {
        "createdDate": "2024-05-06T08:35:03.763Z",
        "replayId": 119,
        "type": "updated"
      },
      "sobject": {
        "Rating": "Hot",
        "Id": "0012w00002Alo5kAAB",
        "Name": "Tests"
      }
    },
    "channel": "/topic/AccountUpdates"
}
 

 

If you change a PushTopic query, those changes take effect immediately on the server. A client receives events only if they match the new SOQL query. If you change a PushTopic Name, live subscriptions are not affected. New subscriptions must use the new channel name.

 

Events:

Events that may generate a notification are the creation, update, delete, or undelete of a record. The PushTopic NotifyForOperationCreate, NotifyForOperationUpdate, NotifyForOperationDelete, and NotifyForOperationUndelete fields enable you to specify which events may generate a notification in that PushTopic channel. The fields are set as follows:

 

NotifyForOperationCreate true if a create operation should generate a notification, otherwise false.
NotifyForOperationDelete true if a delete operation should generate a notification, otherwise false.
NotifyForOperationUndelete true if an undelete operation should generate a notification, otherwise false.
NotifyForOperationUpdate true if an update operation should generate a notification, otherwise false.

 

In API version 28.0 and earlier, you use the NotifyForOperations field to specify which events generate a notification, and can only specify create or update events. The NotifyForOperations values are:

All (default) Evaluate a record to possibly generate a notification whether the record has been created or updated.
Create Evaluate a record to possibly generate a notification only if the record has been created.
Update Evaluate a record to possibly generate a notification only if the record has been updated.
Extended

A value of Extended means that neither create or update operations are set to generate events. This value is provided to allow clients written to API version 28.0 or earlier to work with Salesforce organizations configured to generate delete and undelete notifications.

 

Notifications:

Notifications might be generated when record is created, adapted against the PushTopic query. A notifications sent to the channel as result of the event. The notification is in JSON formatted.

The PushTopic field NotifyForFields specifies how the record is evaluated against the PushTopic query.

All:= Notifications are generated for all record field changes, provided the evaluated records match the criteria specified in the WHERE clause.

Referenced (default) := Changes to fields referenced in the SELECT and WHERE clauses are evaluated. Notifications are generated for the evaluated records only if they match the criteria specified in the WHERE clause.

Select:= Changes to fields referenced in the SELECT clause are evaluated. Notifications are generated for the evaluated records only if they match the criteria specified in the WHERE clause.

Where:= Changes to fields referenced in the WHERE clause are evaluated. Notifications are generated for the evaluated records only if they match the criteria specified in the WHERE clause.

 

Create Lightning Web Component

Now we need to make LWC component to subscribe PustTopic channel. To subscribe PushTopic channel we have to use lightning/empApi module which is support the streaming channel and listening to event messages. These are channels - platform events, PushTopic events, generic events, and Change Data Capture events.

pushTopicStreamingApi.html
<template>
    <lightning-card>
        <div class="slds-p-around_x-small" >

            <table class="slds-table slds-table_bordered slds-table_fixed-layout slds-table_resizable-cols" role="grid">
                <thead>
                    <tr class="slds-line-height_reset">
                        <th class="" scope="col">
                            <a class="slds-th__action slds-text-link_reset" role="button" tabindex="0" name="AccountName">
                               AccountId
                            </a>
                        </th>
                        <th class="" scope="col">
                            <a class="slds-th__action slds-text-link_reset" role="button" tabindex="0" name="LastName">
                                AccountName
                            </a>
                        </th>
                        <th class="" scope="col">
                            <a class="slds-th__action slds-text-link_reset" role="button" tabindex="0" name="Rating">
                                Rating
                            </a>
                        </th>
                        <th class="" scope="col">
                            <a class="slds-th__action slds-text-link_reset" role="button" tabindex="0" name="Action">
                                Action
                            </a>
                        </th>
                    </tr>
                </thead>
                <tbody>
                    <template for:each={accountData} for:item="data">
                        <tr class="slds-hint-parent" key={data.Id}>
                            <td data-label="Account Name" scope="row">
                                <div class="slds-truncate" title={data.Id}>{data.Id}</div>
                            </td>
                            <td data-label="Name">
                                <div class="slds-truncate" title={data.Name}>{data.Name}</div>
                            </td>
                            <td data-label="Rating">
                                <div class="slds-truncate" title={data.Rating}>{data.Rating}</div>
                            </td>
                            <td data-label="Edit">
                                <div class="slds-truncate"><lightning-button onclick={editAccount} data-acc-id={data.Id} label="Edit"></lightning-button></div>
                            </td>
                        </tr>
                    </template>
                </tbody>
            </table>
        </div>
    </lightning-card>
</template>
 
pushTopicStreamingApi.js
import { LightningElement, api, track, wire } from 'lwc';
import getAccountsForPushTopic from '@salesforce/apex/DatatableController.getAccountsForPushTopic'
import { subscribe, unsubscribe, onError} from 'lightning/empApi';
import { NavigationMixin } from 'lightning/navigation';
import { refreshApex } from "@salesforce/apex";
import { ShowToastEvent } from "lightning/platformShowToastEvent";

 

export default class PushTopicStreamingApi extends NavigationMixin(LightningElement) {

 

    @track accountData;
    @api recordId;
     
    connectedCallback() {
        this.handleSubscribe();
        this.registerErrorListener();
    }

 

    accountsActivity;
    @wire(getAccountsForPushTopic)
    wiredGetAccounts(value){
        this.accountsActivity = value;
        const {data, error} = value;
        if(data) {
            this.accountData = data;
        } else if(error) {
            console.log('Error getAccountsForPushTopic:'+ error);
        }
    }

 

    channelName = '/topic/AccountUpdates';
    subscription = {};

 

    // Handles subscribe
    handleSubscribe() {
        const messageCallback = (response) => {
            this.handleChangeEventResponse(response);
        }
        subscribe(this.channelName, -1, messageCallback)
        .then((response) => {
            console.log('Subscription request sent to: ',JSON.stringify(response.channel));
            this.subscription = response;
        });
    }

 

    editAccount(event) {
        event.preventDefault();
        this[NavigationMixin.Navigate]({
            type: 'standard__recordPage',
            attributes: {
                recordId: event.target.dataset.accId,
                objectApiName: 'Account',
                actionName: 'edit'
            },
        });
    }

 

    handleChangeEventResponse(response) {
        console.log('-----handleChangeEventResponse-------');
        console.log('New message recieved: '+ JSON.stringify(response));
        if(response.data.event.type=="updated") {
            return refreshApex(this.accountsActivity);
        }
    }

 

    registerErrorListener() {
        // Invoke onError empApi method
        onError((errors) => {
            console.log('Received error from server: ', errors.error);
            this.toastMessage("Error", errors.error, "error");
        });
    }

 

    disconnectedCallback() {
        unsubscribe(this.subscription, (response) => {
            console.log('unsubscribe() response: ', JSON.stringify(response));
        });
    }

 

    toastMessage(title, message, variant) {
        this.dispatchEvent(
            new ShowToastEvent({
              title: title,
              message: message,
              variant: variant,
            }),
        );
    }
}
 
 
 
pushTopicStreamingApi.js-meta.xml
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>59.0</apiVersion>
    <isExposed>true</isExposed>
    <targets>
        <target>lightning__HomePage</target>
    </targets>
</LightningComponentBundle>

 

How to deactivate the PushTopic

Run the below code on anonymous window to deactivate the pushtopic.

PushTopic pt = new PushTopic(Id='0IF2w000000DGWrGAO', IsActive = false);
update(pt);