A practical reference for Apex development on Salesforce Service Cloud: queries, triggers, HTTP callouts, scheduled jobs, and deployment utilities. This page covers the patterns that come up most in backend and integration work.

Preparation

⚠️ All external endpoints must be whitelisted in Setup > Security > Remote Site Settings before any callout will succeed.

Walkthrough

SOQL, DML & Dates

// Query
List<Account> accounts = [SELECT Id, Name FROM Account];
System.debug('size: '+accounts.size());

// Loop
for(Account account : accounts){
  throw new IllegalArgumentException('Test'); // Exception class is abstract. Use Exceptions.SystemException, ApplicationException
}

// Insert / Update
Account a = new Account(Name='');
insert a;
a.Name = 'New name';
update a;

// Dates
datetime nowDatetime = datetime.now(); // for saleItemGeneratedId PLOCRM-21
string nowDate = nowDatetime.format('yyyyMMdd');
string nowTime = nowDatetime.format('HHmmss');

Parent-child SOQL request (and datetime)

Datetime dtFrom = DateTime.newInstance(2021, 09, 01, 0, 0, 0);
List<Sale__c> sales = [
    SELECT
	Id, Name, Locale__c,
	(
	    SELECT Id, Quantity__c, Product__c, Amount__c
	    FROM Ticket_Lines__r
	    WHERE Quantity__c>0 AND Amount__c>0
	)
    FROM Sale__c
    WHERE Purchase_Date__c > :dtTo
];
for(Sale__c sale: sales){
    System.debug(sale.Ticket_Lines__r.size()+' lines');
}

Triggers

trigger MyrosCaseTrigger on Case (
    before insert, before update, before delete,
    after insert, after update, after delete, after undelete
    ) {
    if(Trigger.Isupdate){
        MyrosCaseHelper.process(Trigger.new);
    }
}

HTTP Callouts

GET Request

HttpRequest request = new HttpRequest();
request.setEndpoint('https://jsonplaceholder.typicode.com/users');
request.setMethod('GET');
HttpResponse response = new Http().send(request);

List<Object> records = (List<Object>) JSON.deserializeUntyped(response.getBody());
for (Object record : records) {
  Map<String, Object> mapped = (Map<String, Object>) record;
  String id = (String) mapped.get('id');
}

POST to SOAP API (e.g. Adobe Campaign)

Update broadlog statuses in Adobe Campaign from Salesforce:

HttpRequest request = new HttpRequest();
request.setEndpoint('https://xxx.neolane.net/nl/jsp/soaprouter.jsp');
request.setMethod('POST');
request.setHeader('Content-Type', 'application/xml');
request.setHeader('SOAPAction', 'xtk:persist#WriteCollection');
request.setBody(
  '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:urn="urn:xtk:session">' +
    '<soapenv:Header/>' +
    '<soapenv:Body>' +
      '<urn:WriteCollection>' +
        '<urn:sessiontoken>user_name/pass_word</urn:sessiontoken>' +
        '<urn:domDoc>' +
          '<broadLogRcp-collection xtkschema="nms:broadLogRcp">' +
            '<broadLogRcp id="129106000" status="5" _operation="update" _key="@id" eventDate="2018-12-25 11:05:59"/>' +
            '<broadLogRcp id="129117000" status="4" _operation="update" _key="@id" eventDate="2018-12-25 11:05:59"/>' +
          '</broadLogRcp-collection>' +
        '</urn:domDoc>' +
      '</urn:WriteCollection>' +
    '</soapenv:Body>' +
  '</soapenv:Envelope>'
);
HttpResponse response = new Http().send(request);
System.debug(response.getBody());

POST to REST API (e.g. Marketing Cloud)

Two-step: authenticate, then call. See also Marketing Cloud REST API.

// Step 1 — Authenticate
HttpRequest authRequest = new HttpRequest();
authRequest.setEndpoint('https://xxx.auth.marketingcloudapis.com/v2/token');
authRequest.setMethod('POST');
authRequest.setHeader('Content-Type', 'application/json');
authRequest.setBody('{"grant_type":"client_credentials","client_id":"xxx","client_secret":"xxx","account_id":"xxx"}');
HttpResponse authResponse = new Http().send(authRequest);

Map<String, Object> authBody = (Map<String, Object>) JSON.deserializeUntyped(authResponse.getBody());
String accessToken = (String) authBody.get('access_token');

// Step 2 — Send transactional email
HttpRequest request = new HttpRequest();
request.setEndpoint('https://xxx.rest.marketingcloudapis.com/messaging/v1/email/messages/');
request.setMethod('POST');
request.setHeader('Authorization', 'Bearer ' + accessToken);
request.setHeader('Content-Type', 'application/json');
request.setBody(
  '{"definitionKey":"Auto_WW_DoubleOptinQRCode","recipients":[{"contactKey":"0034H00002ZiYo5QAF","to":"[email protected]","attributes":{"FirstName":"John","LastName":"Doe"}}]}'
);
HttpResponse response = new Http().send(request);
System.debug(response.getBody());

Scheduled Jobs

Find running jobs in Setup > Environments > Jobs > Scheduled Jobs.

global class MyrosSchedule implements Schedulable {
  global void execute(SchedulableContext ctx) {
    // process
  }
}

Schedule from the Anonymous Window:

// Seconds Minutes Hours Day Month Weekday Year
String cron = '0 0 1 * * ? *'; // daily at 01:00
// → https://www.freeformatter.com/cron-expression-generator-quartz.html
String jobId = System.schedule('JobName', cron, new MyrosSchedule());
System.debug(jobId);

Stop job from Setup > Scheduled Jobs > Delete or programmatically:

System.abortJob(jobId);

Validation & troubleshooting

Any Apex Class deployed to production needs to be at least 80% covered by Unit Tests.

Trigger unit test

@isTest
public with sharing class MyrosCaseTriggerTest {
    @isTest private static void test() {
    	Case c = new Case(Subject='TestCase');
	    insert c;
	    List<Case> createdCases = [SELECT Id FROM Case WHERE Subject = 'TestCase'];
        System.assertEquals(1, createdCases.size());
    }
}

Callout mock for unit tests

@isTest
global class MyrosCalloutMock implements HttpCalloutMock {
    global HTTPResponse respond(HTTPRequest request) {
        HttpResponse response = new HttpResponse();
        response.setHeader('Content-Type', 'application/json');
        response.setBody('[{"id": 1}]');
        response.setStatusCode(200);
        return response;
    }
}

Going further

Delete Apex from Production

Production orgs block direct deletion of Apex. Deploy a destructiveChanges.xml via Workbench under Migration > Deploy.

  • package.xml
<?xml version="1.0" encoding="UTF-8"?>
<package xmlns="http://soap.sforce.com/2006/04/metadata">
  <version>30.0</version>
</package>
  • destructiveChanges.xml
<?xml version="1.0" encoding="utf-8"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
  <types>
    <members>Class1</members>
    <members>Class2</members>
    <members>Class3</members>
    <name>ApexClass</name>
  </types>
  <version>30.0</version>
</Package>

Zip both files and deploy.