Salesforce Apex Code Examples: Snippets Every Developer Should Know

Hey friends,

These are the nine Apex code examples I reach for most: a SOQL query with a bind variable, a query that runs once instead of inside a loop, a trigger that blocks a bad save, a changed-field check, a bulk insert that survives bad rows, a DML error handler, an HTTP callout, a test class and a limits check. Copy any of them and change the object and field names. Each one comes with what it does and the trap it avoids.

Two quick definitions before the code. Apex is Salesforce's programming language, and DML (data manipulation language) means the statements that change records: insert, update, delete and undelete. Apex runs under governor limits, which are per-transaction caps Salesforce enforces so one org's code can't hog shared resources. Most good Apex habits exist to stay under them. The limit numbers in this post come from Salesforce's Apex Developer Guide, checked on October 28, 2026.

How to run these examples

The queries, the DML and the limits check are plain statements, so paste them into the Execute Anonymous window in the Developer Console and run them. I wrote a short post on the Execute Anonymous window if you haven't used it. The trigger, the class and the test class are saved code, so create each one as its own trigger or class. Use a free Developer Edition org, not production, so a mistake costs you nothing.

Step: in the Developer Console, click Debug, then Open Execute Anonymous Window, enter your code, leave Open Log ticked and click Execute (1). This capture shows the last lines of a small demo snippet that is not one of the nine examples below. The window is narrow, so the end of line 7 is cut off at its right edge.

Developer Console Enter Apex Code window with the last lines of a small Apex snippet, and the Open Log box and Execute button highlighted

Developer Console > Debug > Open Execute Anonymous Window.

Step: when the log opens, tick Debug Only to hide the system lines and keep your own. Each USER_DEBUG row holds the text of one System.debug call, with its line number in brackets.

Developer Console execution log with three USER_DEBUG rows showing Fruits: (Apple, Banana, Cherry), a counts map and Sum of 1..5 = 15

Execution Log with Debug Only ticked, for the same demo snippet.

1. Query records with SOQL and a bind variable

SOQL (Salesforce Object Query Language) reads records. The colon in :stage is a bind variable: it drops the value of an Apex variable straight into the query, which is safer and cleaner than gluing strings together.

StageQuery.apex
1String stage = 'Prospecting';
2List<Opportunity> opps = [
3    SELECT Id, Name, Amount
4    FROM Opportunity
5    WHERE StageName = :stage
6    AND Amount > 10000
7    ORDER BY Amount DESC
8    LIMIT 10
9];
10for (Opportunity o : opps) {
11    System.debug(o.Name + ': ' + o.Amount);
12}

The query returns a list of Opportunity records, and the loop prints each one. For joins across objects, see my SOQL relationship queries cheat sheet.

2. Query once, not inside a loop

This is one of the most common Apex mistakes. A query inside a for loop runs once per record, and a synchronous transaction gets only 100 SOQL queries. The fix is to bulkify the code, which just means handling a whole list of records at once. Collect the IDs in a set, run one query, and keep the results in a map so you can look each record up by ID. Here contacts is a list of Contact records you already have, such as Trigger.new.

BulkifiedQuery.apex
1Set<Id> accountIds = new Set<Id>();
2for (Contact c : contacts) {
3    accountIds.add(c.AccountId);
4}
5Map<Id, Account> accountsById = new Map<Id, Account>(
6    [SELECT Id, Name FROM Account WHERE Id IN :accountIds]
7);
8for (Contact c : contacts) {
9    Account a = accountsById.get(c.AccountId);
10    if (a != null) {
11        c.Description = 'Works at ' + a.Name;
12    }
13}

A trigger receives up to 200 records at a time, so a query per record would break the 100-query limit quickly. The pattern above uses one query for any number of records. It is the same pattern Salesforce teaches in its guide to common bulk trigger idioms. For more on sets, lists and maps, see my Apex collections cheat sheet.

3. Block a bad save with a before trigger

A trigger is Apex that runs when records are inserted, updated or deleted. A before trigger runs before the record is saved, so you can reject it with addError. The user sees your message and nothing is saved.

AccountTrigger.apex
1trigger AccountTrigger on Account (before insert, before update) {
2    for (Account a : Trigger.new) {
3        if (a.Name == null || a.Name.length() < 3) {
4            a.addError('Account name needs at least 3 characters.');
5        }
6    }
7}

Put the loop over Trigger.new, never a single record, because triggers always arrive in batches. For one-trigger-per-object structure and the full list of context variables, read my Apex trigger best practices post.

4. Do something only when a field changed

In an update trigger, Trigger.oldMap holds the records as they were before the change, keyed by ID. Compare the new value with the old one and act only when they differ. Without this check, your code runs on every edit to the record.

OpportunityTrigger.apex
1trigger OpportunityTrigger on Opportunity (before update) {
2    for (Opportunity o : Trigger.new) {
3        Opportunity oldOpp = Trigger.oldMap.get(o.Id);
4        if (o.StageName != oldOpp.StageName) {
5            o.Description = 'Stage moved from ' + oldOpp.StageName;
6        }
7    }
8}

Trigger.oldMap only exists for update and delete triggers. In an insert trigger there is no old version, so using it there fails.

5. Insert many records and keep going when one fails

A plain insert statement is all-or-nothing: one bad row and the whole statement fails. Database.insert with the second argument set to false saves the good rows and reports on the bad ones. Each row gets a SaveResult back, in the same order as your list.

BulkInsert.apex
1List<Account> accounts = new List<Account>{
2    new Account(Name = 'Acme'),
3    new Account()
4};
5List<Database.SaveResult> results = Database.insert(accounts, false);
6for (Integer i = 0; i < results.size(); i++) {
7    if (!results[i].isSuccess()) {
8        for (Database.Error err : results[i].getErrors()) {
9            System.debug('Row ' + i + ': ' + err.getMessage());
10        }
11    }
12}

Here the second account has no name, so it fails and the first one saves. A single transaction can process up to 10,000 records through DML, and 150 DML statements. I compare the two styles in DML statements vs Database methods.

6. Catch a DML error

When you use a plain DML statement, wrap it in try and catch so a failed save becomes a message you control instead of an unhandled error. A Contact needs a last name, so the insert below fails and lands in the catch block.

DmlErrorHandling.apex
1try {
2    insert new Contact(FirstName = 'Ada');
3} catch (DmlException e) {
4    System.debug('DML failed: ' + e.getMessage());
5}

One trap: this only catches errors your code can recover from. Salesforce's guide says that when code exceeds a governor limit, the runtime throws an exception that can't be handled. A catch block won't save you from 101 queries.

7. Call an external API

A callout is an HTTP request from Apex to another system. Use a Named Credential, which stores the endpoint and login in Setup so your code carries no secrets. In this example, Weather_API is a Named Credential I made up, so create your own or change the name. Check the status code before you trust the body.

WeatherService.apex
1public with sharing class WeatherService {
2    public static Map<String, Object> getForecast() {
3        HttpRequest req = new HttpRequest();
4        req.setEndpoint('callout:Weather_API/forecast');
5        req.setMethod('GET');
6        HttpResponse res = new Http().send(req);
7        if (res.getStatusCode() != 200) {
8            throw new CalloutException('Status ' + res.getStatusCode());
9        }
10        return (Map<String, Object>) JSON.deserializeUntyped(res.getBody());
11    }
12}

Per the governor limits page, one transaction can make 100 callouts, with a combined timeout of 120 seconds, and each callout times out after 10 seconds by default. The response body counts toward heap size, and callout requests and responses are capped at 6 MB for synchronous Apex.

8. Test that the trigger works

A test class proves your code does what you think and keeps working after the next change. This one checks the trigger from example 3 by trying to save a one-letter name and asserting that the save was rejected with the right message. Without that check, a test can pass while proving nothing.

AccountTriggerTest.apex
1@IsTest
2private class AccountTriggerTest {
3    @IsTest
4    static void rejectsShortNames() {
5        Test.startTest();
6        Database.SaveResult result = Database.insert(new Account(Name = 'A'), false);
7        Test.stopTest();
8        Assert.isFalse(result.isSuccess());
9        Assert.areEqual(
10            'Account name needs at least 3 characters.',
11            result.getErrors()[0].getMessage()
12        );
13    }
14}

Test.startTest() and Test.stopTest() bracket the code you are testing. For coverage requirements and what a good test looks like, see my post on Apex test class code coverage.

9. Check how close you are to the limits

The Limits class tells you how much of each limit the current transaction has used. Run it at the end of a piece of code you are tuning, or log it in a trigger while you are debugging.

LimitsCheck.apex
1System.debug('SOQL: ' + Limits.getQueries() + ' of ' + Limits.getLimitQueries());
2System.debug('DML: ' + Limits.getDmlStatements() + ' of ' + Limits.getLimitDmlStatements());
3System.debug('CPU: ' + Limits.getCpuTime() + ' of ' + Limits.getLimitCpuTime());

Each line prints the amount used and the maximum, for example SOQL: 2 of 100. The output shows up in the debug log, not on the screen.

The limits behind these examples

These are the numbers from the Execution Governors and Limits page that shape the examples above. Where the two differ, the table shows synchronous first, then asynchronous (Batch Apex and future methods).

Limit per transactionSynchronous / asynchronous
SOQL queries issued100 / 200
Records retrieved by SOQL50,000 / 50,000
DML statements150 / 150
Records processed by DML10,000 / 10,000
CPU time on Salesforce servers10,000 ms / 60,000 ms
Heap size10 MB / 25 MB
Callouts100 / 100
Trigger batch size200 records

Limits are checked per transaction, not per org, and Salesforce can change them, so check the page before you design around a number.

Cert Lab by Nick Frates

Learning Apex for the exam?

The Platform Developer Cert Lab has lessons weighted like the exam, practice questions that explain every option, flashcards and timed practice exams, so you can see which Apex topics you still need to work on.

Start the Cert Lab, $9.99 →

Frequently asked questions

What are the most common Apex code examples?

The ones every developer writes early: a SOQL query, a bulkified query that runs once for many records, a before trigger that validates a save, DML with error handling, an HTTP callout and a test class.

How do I run Apex code in Salesforce?

For a quick statement, use the Execute Anonymous window in the Developer Console. For triggers and classes, save them in the Developer Console, Visual Studio Code or another editor connected to your org.

Why shouldn't I put a SOQL query inside a for loop?

The loop runs the query once per record, and a synchronous transaction allows only 100 SOQL queries. Query once before the loop and use a map to look records up.

What is the difference between insert and Database.insert?

A plain insert fails the whole statement if one record fails. Database.insert with allOrNone set to false saves the valid records and returns a result for each one, so you can handle the failures.

Can I catch a governor limit exception?

No. Salesforce's guide says the exception raised when code exceeds a limit can't be handled, so you have to stay under the limits by design.

Where can I practice Apex for free?

In a free Salesforce Developer Edition org. I show how to sign up in my Developer Edition guide.

Hope this helps!

Best,

Nick

Keep reading

See all posts →

Salesforce Saturdays

Join 1,033 Salesforce pros getting one 10-minute tip on Salesforce tech or career growth every Saturday.