Fire Platform Events from Batch Apex Classes


With winter 19 release you can able to fire the platform events from the batch apex.Batch Apex classes can opt in to fire platform events when encountering an error or exception. Clients listening on an event can obtain actionable information, such as how often the event failed and which records were in scope at the time of failure. Events are also fired for Salesforce Platform internal errors and other uncatchable Apex exceptions such as LimitExceptions, which are caused by reaching governor limits. An event record provides more granular error tracking than the Apex Jobs UI. It includes the record IDs being processed, exception type, exception message, and stack trace. You can also incorporate custom handling and retry logic for failures. You can invoke custom Apex logic from any trigger on this type of event, so Apex developers can build functionality like custom logging or automated retry handling. To fire a platform event, a batch Apex class declaration must implement the Database.RaisesPlatformEvents interface.

Step 1:  Create a Platform event

Here is the simple platform event object created for this example 

Step 2: Subscribe to platform event

I just created a simple trigger to subscribe for the platform events. During this beta release, Process Builder and flows do not support subscribing to these events.

// Trigger for listening to Cloud_News events.
trigger CloudNewsTrigger on Cloud_News__e (after insert) {    
    // List to hold all cases to be created.
    List<Case> cases = new List<Case>();
    // Iterate through each notification.
    for (Cloud_News__e event : Trigger.New) {
        if (event.Urgent__c == true) {
            // Create Case to dispatch new team.
            Case cs = new Case();
            cs.Priority = 'High';
            cs.Subject = 'News team dispatch to ' +event.Location__c;
    // Insert all cases corresponding to events received.
    insert cases;

Step 3: Raise Platform events

now you can raise the platform event from the batch is the simple batch apex that will raise the platform evens. After raising platform events all the subscribed channel will be receiving the events.

global with sharing class PlatformEventRaise implements Database.Batchable<SObject>, Database.RaisesPlatformEvents{
    // class implementation
    global Database.QueryLocator start(Database.BatchableContext BC){
        return Database.getQueryLocator('Select Id ,Name,Rating,Industry, BillingAddress,BillingStreet,BillingCity, BillingCountry, BillingPostalCode,BillingState,Phone from Account  where BillingStreet!=NULL');
    global void execute(Database.BatchableContext BC, List<sObject> scope){
        List<Account> accs =(List<Account>) scope ; 
        List<Cloud_News__e> cnewList = new List<Cloud_News__e>();
        for(Account a : accs){
            // Create an instance of the event and store it in the newsEvent variable
            Cloud_News__e newsEvent = new Cloud_News__e(
            cnewList.add(newsEvent) ;
    global void finish(Database.BatchableContext BC){

Go and execute batch apex

After raising the platform events, the subscribed trigger will create a case as shown below


Event Error Handling

The BatchApexErrorEvent object represents a platform event associated with a batch Apex class. This example creates a trigger to determine which accounts failed in the batch transaction. Custom field Dirty__c indicates that the account was one of a failing batch and ExceptionType__c indicates the exception that was encountered. JobScope and ExceptionType are fields in the BatchApexErrorEvent object.

trigger MarkDirtyIfFail on BatchApexErrorEvent (after insert) {
    Set<Id> asyncApexJobIds = new Set<Id>();
    Map<Id,AsyncApexJob> jobs = new Map<Id,AsyncApexJob>(
        [SELECT id, ApexClass.Name FROM AsyncApexJob WHERE Id IN :asyncApexJobIds]
    List<Account> records = new List<Account>();
        //only handle events for the job(s) we care about
        if(jobs.get(evt.AsyncApexJobId).ApexClass.Name == 'PlatformEventRaise'){
            for (String item : evt.JobScope.split(',')) {
                Account a = new Account(
                    Id = (Id)item,
                    ExceptionType__c = evt.ExceptionType,
                    Dirty__c = true
    update records;