Salesforce Apex Tutorial: Complete Programming Guide | SalesforceTutorial

Written by Prasanth Kumar Published on Updated on

Salesforce Apex Tutorial: Complete Programming Guide | SalesforceTutorial

This comprehensive Salesforce Apex tutorial covers everything from basic syntax to advanced programming concepts. Learn Apex programming language fundamentals, best practices, and real-world implementation examples for building robust Salesforce applications.

Apex is Salesforce’s proprietary programming language that runs on the Salesforce Platform. Unlike traditional programming languages, Apex executes in a multi-tenant environment with strict governor limits and security controls. This tutorial provides practical examples and production-ready code patterns for Salesforce developers.

Salesforce Apex tutorial programming guide overview

What is Salesforce Apex Programming Language?

Apex is a strongly-typed, object-oriented programming language that allows developers to execute flow and transaction control statements on the Salesforce Platform. It uses Java-like syntax but runs entirely on Salesforce servers, providing seamless integration with Salesforce data and metadata.

Key Features of Apex Programming

  1. Cloud-based execution: Runs on Salesforce’s multi-tenant architecture
  2. Integrated with Salesforce data: Direct access to sObjects and database operations
  3. Governor limits: Built-in resource management prevents runaway code
  4. Automatic upgrades: Platform updates don’t break existing code

When to Use Apex

  1. Complex business logic that exceeds declarative tools
  2. Custom web services and API integrations
  3. Batch processing of large data volumes
  4. Advanced trigger logic and automation

Apex Tutorial: Basic Syntax and Data Types

Apex syntax closely resembles Java, making it familiar to developers with object-oriented programming experience. Here are the fundamental concepts every Apex developer must understand.

Variable Declaration and Data Types

// Primitive data types
Integer count = 10;
String accountName = 'Acme Corporation';
Boolean isActive = true;
Decimal revenue = 1000000.50;
Date startDate = Date.today();
DateTime createdTime = DateTime.now();

// sObject variables
Account acc = new Account();
Contact con = new Contact(FirstName='John', LastName='Doe');

Collections in Apex

// List (most commonly used)
List<Account> accountList = new List<Account>();
List<String> nameList = new List<String>{'John', 'Jane', 'Bob'};

// Set (unique values only)
Set<Id> accountIds = new Set<Id>();

// Map (key-value pairs)
Map<Id, Account> accountMap = new Map<Id, Account>();

Apex Programming Tutorial: Control Structures

Control structures in Apex follow standard programming patterns with some platform-specific considerations for governor limits.

Conditional Statements

// If-else statements
if (account.AnnualRevenue > 1000000) {
    account.Rating = 'Hot';
} else if (account.AnnualRevenue > 100000) {
    account.Rating = 'Warm';
} else {
    account.Rating = 'Cold';
}

// Switch statement (API v54.0+)
switch on account.Industry {
    when 'Technology' {
        account.Priority__c = 'High';
    }
    when 'Healthcare', 'Finance' {
        account.Priority__c = 'Medium';
    }
    when else {
        account.Priority__c = 'Standard';
    }
}

Loops and Iteration

// For loop with list
for (Account acc : accountList) {
    acc.Description = 'Updated via Apex';
}

// Traditional for loop
for (Integer i = 0; i < accountList.size(); i++) {
    accountList[i].NumberOfEmployees = i + 1;
}

// While loop
Integer counter = 0;
while (counter < 10) {
    System.debug('Counter: ' + counter);
    counter++;
}

Salesforce Apex Tutorial: Working with sObjects

sObjects are the foundation of Salesforce data manipulation in Apex. Understanding how to create, query, and modify sObjects is essential for effective Apex programming.

Creating and Modifying sObjects

// Create new Account
Account newAccount = new Account(
    Name = 'Tech Solutions Inc',
    Industry = 'Technology',
    AnnualRevenue = 5000000
);

// Insert the record
insert newAccount;
System.debug('New Account ID: ' + newAccount.Id);

// Update existing record
newAccount.Phone = '(555) 123-4567';
update newAccount;

SOQL Queries in Apex

// Basic SOQL query
List<Account> accounts = [SELECT Id, Name, Industry FROM Account WHERE Industry = 'Technology'];

// Query with relationships
List<Contact> contacts = [
    SELECT Id, Name, Account.Name, Account.Industry 
    FROM Contact 
    WHERE Account.Industry = 'Technology'
    LIMIT 100
];

// Dynamic SOQL
String industry = 'Healthcare';
String query = 'SELECT Id, Name FROM Account WHERE Industry = :industry';
List<Account> dynamicResults = Database.query(query);

Final Keyword in Apex: Constants and Immutability

The final keyword in Apex creates constants and prevents reassignment of variables. This is crucial for maintaining code integrity and following best practices.

Using Final Variables

// Final primitive variable
final String COMPANY_NAME = 'Salesforce';
final Integer MAX_RECORDS = 200;

// Final collections
final List<String> VALID_INDUSTRIES = new List<String>{
    'Technology', 'Healthcare', 'Finance', 'Manufacturing'
};

// Final in method parameters
public void processAccount(final Account acc) {
    // acc cannot be reassigned, but its fields can be modified
    acc.Description = 'Processed';
    // acc = new Account(); // This would cause compilation error
}

Final Methods and Classes

// Final method cannot be overridden
public virtual class BaseProcessor {
    public final void validateInput(String input) {
        if (String.isBlank(input)) {
            throw new IllegalArgumentException('Input cannot be blank');
        }
    }
}

// Final class cannot be extended
public final class UtilityHelper {
    public static String formatPhoneNumber(String phone) {
        // Implementation here
        return phone;
    }
}

Apex Programming Language Best Practices

Following best practices ensures your Apex code is maintainable, efficient, and compliant with Salesforce governor limits.

Governor Limits and Bulkification

// BAD: SOQL in loop (governor limit violation)
for (Account acc : accountList) {
    List<Contact> contacts = [SELECT Id FROM Contact WHERE AccountId = :acc.Id];
    // Process contacts
}

// GOOD: Bulkified approach
Set<Id> accountIds = new Set<Id>();
for (Account acc : accountList) {
    accountIds.add(acc.Id);
}
List<Contact> allContacts = [SELECT Id, AccountId FROM Contact WHERE AccountId IN :accountIds];

// Group contacts by account
Map<Id, List<Contact>> contactsByAccount = new Map<Id, List<Contact>>();
for (Contact con : allContacts) {
    if (!contactsByAccount.containsKey(con.AccountId)) {
        contactsByAccount.put(con.AccountId, new List<Contact>());
    }
    contactsByAccount.get(con.AccountId).add(con);
}

Error Handling and Exception Management

try {
    List<Account> accountsToInsert = new List<Account>();
    // Populate list
    insert accountsToInsert;
} catch (DmlException e) {
    System.debug('DML Error: ' + e.getMessage());
    // Handle specific DML errors
    for (Integer i = 0; i < e.getNumDml(); i++) {
        System.debug('Error on record ' + i + ': ' + e.getDmlMessage(i));
    }
} catch (Exception e) {
    System.debug('General Error: ' + e.getMessage());
    // Log error or send notification
}

Advanced Apex Programming Concepts

Once you master the basics, these advanced concepts will help you build sophisticated Salesforce applications.

Apex Triggers

trigger AccountTrigger on Account (before insert, before update, after insert, after update) {
    if (Trigger.isBefore) {
        if (Trigger.isInsert || Trigger.isUpdate) {
            AccountTriggerHandler.validateAccountData(Trigger.new);
        }
    }
    
    if (Trigger.isAfter) {
        if (Trigger.isInsert) {
            AccountTriggerHandler.createDefaultContacts(Trigger.new);
        }
        if (Trigger.isUpdate) {
            AccountTriggerHandler.updateRelatedRecords(Trigger.new, Trigger.oldMap);
        }
    }
}

Batch Apex for Large Data Processing

public class AccountUpdateBatch implements Database.Batchable<sObject> {
    
    public Database.QueryLocator start(Database.BatchableContext bc) {
        return Database.getQueryLocator(
            'SELECT Id, Name, Industry FROM Account WHERE Industry = null'
        );
    }
    
    public void execute(Database.BatchableContext bc, List<Account> accounts) {
        for (Account acc : accounts) {
            acc.Industry = 'Other';
        }
        update accounts;
    }
    
    public void finish(Database.BatchableContext bc) {
        System.debug('Batch job completed');
    }
}

Testing Your Apex Code

Apex requires 75% code coverage for deployment to production. Writing comprehensive test classes ensures your code works correctly and meets coverage requirements.

Test Class Example

@isTest
public class AccountTriggerTest {
    
    @testSetup
    static void setupTestData() {
        List<Account> testAccounts = new List<Account>();
        for (Integer i = 0; i < 10; i++) {
            testAccounts.add(new Account(
                Name = 'Test Account ' + i,
                Industry = 'Technology'
            ));
        }
        insert testAccounts;
    }
    
    @isTest
    static void testAccountInsert() {
        Test.startTest();
        Account newAccount = new Account(
            Name = 'New Test Account',
            Industry = 'Healthcare'
        );
        insert newAccount;
        Test.stopTest();
        
        Account insertedAccount = [SELECT Id, Name, Industry FROM Account WHERE Id = :newAccount.Id];
        System.assertEquals('New Test Account', insertedAccount.Name);
        System.assertEquals('Healthcare', insertedAccount.Industry);
    }
}

Frequently Asked Questions

What is the difference between Apex and other programming languages?

Apex runs exclusively on Salesforce’s multi-tenant platform with built-in governor limits, automatic database integration, and cloud-based execution. Unlike traditional languages, Apex doesn’t require server management and provides direct access to Salesforce data and metadata.

How do I start learning Salesforce Apex programming?

Begin with Salesforce Trailhead‘s Apex modules, set up a Developer Edition org for practice, and start with simple trigger examples. Focus on understanding sObjects, SOQL queries, and governor limits before moving to advanced concepts like batch processing.

What are Apex governor limits and why do they matter?

Governor limits prevent any single tenant from monopolizing shared resources in Salesforce’s multi-tenant environment. Key limits include 100 SOQL queries per transaction, 50,000 records per query, and 6MB heap size. Bulkifying code is essential to avoid hitting these limits.

When should I use the final keyword in Apex?

Use final for constants that shouldn’t change, method parameters that shouldn’t be reassigned, methods that shouldn’t be overridden, and classes that shouldn’t be extended. This improves code safety and clearly communicates intent to other developers.

How is Apex different from Java?

While Apex syntax resembles Java, it runs only on Salesforce servers, has built-in database operations, enforces governor limits, and includes Salesforce-specific features like sObjects and SOQL. Apex also handles automatic memory management and doesn’t support threading.


Next Steps in Your Apex Learning Journey

This Apex tutorial covers the fundamentals, but mastering Salesforce development requires hands-on practice and continuous learning. Start by building simple triggers and classes in a Developer Edition org, then progress to more complex scenarios like batch processing and integration patterns.

Focus on understanding governor limits, writing test classes, and following Salesforce best practices. The platform evolves with each release, so stay current with new features and capabilities through official Salesforce documentation and Trailhead modules.