Apex is Salesforce’s strongly typed, object-oriented programming language for implementing business logic on the Salesforce Platform. Its syntax is similar to Java, but Apex is designed to work directly with Salesforce records, transactions, security controls, and platform services.
This Learn Apex tutorial introduces the language from a beginner’s perspective. It covers the Apex execution model, variables, collections, classes, database operations, triggers, exception handling, testing, governor limits, and a practical learning path.
What Apex Is Used for in Salesforce
Salesforce administrators can implement many requirements with declarative tools such as Flow, validation rules, approval processes, formulas, and record configuration. Apex is used when a requirement needs custom server-side logic that cannot be implemented clearly or completely with those tools.
- Run custom logic when Salesforce records are inserted, updated, deleted, or restored.
- Create reusable services that work with records and business processes.
- Implement custom controllers for Lightning components or Visualforce pages.
- Expose or consume REST and SOAP web services.
- Process large workloads asynchronously with future methods, queueable Apex, batch Apex, or scheduled Apex.
- Perform database operations using SOQL, SOSL, and Data Manipulation Language statements.
How Apex Runs on the Salesforce Platform
Apex runs on Salesforce servers rather than in a user’s browser. Code is compiled, stored as metadata, and executed within a Salesforce transaction. A transaction may contain database queries, record updates, trigger execution, automation, and other operations.
Because Salesforce is a multitenant platform, Apex execution is controlled by governor limits. These limits restrict resources such as the number of database queries, DML statements, retrieved rows, CPU time, and heap usage available to a transaction. Apex code must therefore be designed to process records efficiently and in bulk.
Prerequisites for Learning Apex
You can begin learning Apex without previous Salesforce development experience, but the following knowledge makes the process easier:
- Basic programming concepts such as variables, conditions, loops, methods, classes, and exceptions.
- Salesforce objects, fields, records, and relationships.
- Standard and custom objects.
- Basic Salesforce security, including profiles, permission sets, object access, and field-level security.
- The difference between declarative automation and programmatic logic.
A free Salesforce Developer Edition or a Trailhead Playground can be used for practice. The Developer Console and Salesforce Extensions for Visual Studio Code provide tools for writing, executing, debugging, and testing Apex.
Your First Apex Program
The following statement writes a message to the Salesforce debug log:
System.debug('Hello from Apex');
You can execute this statement as anonymous Apex from the Developer Console. Open Debug, select Open Execute Anonymous Window, enter the statement, and run it. Select the option to open the log if you want to inspect the output immediately.
Apex Variables and Data Types
Apex is strongly typed, so each variable must have a declared data type. Common primitive types include Integer, Long, Decimal, Double, Boolean, String, Date, Datetime, Time, ID, and Blob.
String customerName = 'Asha';
Integer orderCount = 3;
Decimal orderTotal = 1499.50;
Boolean activeCustomer = true;
Date orderDate = Date.today();
Apex also supports sObjects, which represent Salesforce records. For example, an Account variable can hold an Account record.
Account customer = new Account();
customer.Name = 'Example Industries';
customer.Phone = '555-0100';
Apex Operators and Conditional Statements
Apex provides arithmetic, assignment, comparison, and logical operators. Conditional statements select which block of code should run.
Decimal orderAmount = 1200;
if (orderAmount >= 1000) {
System.debug('Large order');
} else if (orderAmount > 0) {
System.debug('Standard order');
} else {
System.debug('No order value');
}
Apex also supports switch statements for selecting an action based on a value.
Apex Loops for Processing Records
Loops repeat a block of code. Apex supports for, enhanced for, while, and do-while loops.
List<String> cities = new List<String>{
'Hyderabad',
'Mumbai',
'Chennai'
};
for (String city : cities) {
System.debug(city);
}
When processing Salesforce records, prefer collection-based loops and avoid placing SOQL queries or DML statements inside a loop. This helps the code remain within governor limits.
Apex Collections: List, Set, and Map
Collections are central to bulk-safe Apex development. They allow one transaction to process multiple values or records efficiently.
| Collection | Purpose | Typical Apex use |
|---|---|---|
| List | Ordered collection that can contain duplicate values | Store records returned by a SOQL query |
| Set | Unordered collection of unique values | Collect unique record IDs for a query |
| Map | Key-value collection | Find records quickly by ID or another unique key |
List<String> departments = new List<String>{'Sales', 'Support'};
Set<Id> accountIds = new Set<Id>();
Map<Id, Account> accountsById = new Map<Id, Account>();
Apex Classes, Methods, and Access Modifiers
An Apex class groups data and behavior. Methods inside a class perform specific tasks. Access modifiers such as private, public, and global control where a class, property, or method can be accessed.
public class DiscountCalculator {
public static Decimal calculate(Decimal amount, Decimal percentage) {
if (amount == null || percentage == null) {
return 0;
}
return amount * percentage / 100;
}
}
The method can be called without creating an instance because it is declared as static.
Decimal discount = DiscountCalculator.calculate(2000, 10);
System.debug(discount);
200
Querying Salesforce Records with SOQL
Salesforce Object Query Language, or SOQL, retrieves records from Salesforce objects. A SOQL query identifies the fields to return, the object to query, and optional filtering, ordering, grouping, or row limits.
List<Account> accounts = [
SELECT Id, Name, Industry
FROM Account
WHERE Industry = 'Technology'
ORDER BY Name
LIMIT 100
];
Use bind variables to include Apex values safely in a SOQL condition.
String selectedIndustry = 'Banking';
List<Account> bankingAccounts = [
SELECT Id, Name
FROM Account
WHERE Industry = :selectedIndustry
];
SOQL returns object records and supports relationship queries. SOSL is a separate search language used when text must be searched across multiple objects or fields.
Creating and Updating Records with Apex DML
Data Manipulation Language statements create, modify, or remove Salesforce records. Apex provides insert, update, upsert, delete, undelete, and merge operations.
Account accountRecord = new Account(
Name = 'Northwind Services',
Industry = 'Consulting'
);
insert accountRecord;
accountRecord.Phone = '555-0135';
update accountRecord;
To process multiple records efficiently, collect them in a list and perform one DML operation on the complete collection.
List<Contact> contactsToInsert = new List<Contact>();
contactsToInsert.add(new Contact(LastName = 'Rao'));
contactsToInsert.add(new Contact(LastName = 'Sharma'));
insert contactsToInsert;
Apex Triggers and Bulk-Safe Record Processing
An Apex trigger runs before or after a record event. Trigger events include insert, update, delete, and undelete. Trigger context variables provide access to the affected records and information about the current operation.
trigger AccountNameTrigger on Account (before insert, before update) {
for (Account accountRecord : Trigger.new) {
if (accountRecord.Name != null) {
accountRecord.Name = accountRecord.Name.trim();
}
}
}
A trigger may receive one record or many records in the same execution. It should therefore process the entire Trigger.new or Trigger.old collection instead of assuming that only one record is present.
For larger implementations, keep the trigger small and move business logic into an Apex handler or service class. This makes the code easier to test and maintain.
Apex Exception Handling and Transaction Control
Exceptions indicate that an operation could not be completed. Apex supports try, catch, and finally blocks for handling errors.
try {
Account accountRecord = new Account();
insert accountRecord;
} catch (DmlException exceptionRecord) {
System.debug('Insert failed: ' + exceptionRecord.getMessage());
}
An unhandled exception normally causes the transaction to roll back. Savepoints and explicit rollback operations can be used when code needs controlled transaction recovery.
Savepoint savepointRecord = Database.setSavepoint();
try {
insert new Account(Name = 'Example Account');
insert new Contact();
} catch (Exception exceptionRecord) {
Database.rollback(savepointRecord);
}
Governor Limits and Efficient Apex Design
Governor limits apply to each Apex transaction. Exact limits can depend on the execution context, so developers should use Salesforce’s current documentation when checking a particular limit.
The following practices reduce common limit-related problems:
- Do not place SOQL queries inside loops.
- Do not perform one DML statement for every record in a loop.
- Use lists, sets, and maps to process records in groups.
- Query only the fields and records the operation requires.
- Use aggregate queries when the database can perform the calculation more efficiently.
- Move appropriate long-running work to asynchronous Apex.
- Review recursive trigger behavior and prevent unintended repeated processing.
Synchronous and Asynchronous Apex
Synchronous Apex runs immediately and normally completes before control returns to the caller. Asynchronous Apex places work into a queue for later execution. It is useful for operations that do not need to finish within the original transaction.
| Apex mechanism | Typical purpose |
|---|---|
| Future method | Run a simple asynchronous static method, including certain callout scenarios |
| Queueable Apex | Run a job with structured data and support job chaining |
| Batch Apex | Process a large record set in manageable batches |
| Scheduled Apex | Start an Apex job at a configured time |
Queueable Apex is generally more flexible than a future method for new asynchronous implementations. Batch Apex should be reserved for workloads that require batch-style processing rather than used for every background task.
Writing Apex Test Classes
Apex tests verify expected behavior and help prevent regressions. Tests should create their own records, execute the code being tested, and assert the expected result. A test should check behavior rather than merely execute lines for coverage.
@IsTest
private class DiscountCalculatorTest {
@IsTest
static void calculatesTenPercentDiscount() {
Decimal result = DiscountCalculator.calculate(2000, 10);
System.assertEquals(200, result);
}
@IsTest
static void returnsZeroForNullAmount() {
Decimal result = DiscountCalculator.calculate(null, 10);
System.assertEquals(0, result);
}
}
Useful Apex testing practices include:
- Create test data inside the test class instead of depending on organization data.
- Test positive, negative, null, and bulk-record scenarios.
- Use
Test.startTest()andTest.stopTest()around the main operation when appropriate. - Use assertions to verify field values, record counts, exceptions, and side effects.
- Test asynchronous jobs by placing their execution between
Test.startTest()andTest.stopTest(). - Use callout mocks when code communicates with an external service.
Apex Security: Sharing and User Permissions
Apex often runs with elevated access compared with the current user’s interface permissions. Developers must deliberately account for record sharing, object permissions, and field-level security.
- Use an appropriate class sharing declaration such as
with sharing,without sharing, orinherited sharing. - Do not assume that record sharing automatically enforces object and field permissions.
- Use supported platform features to enforce user-mode operations or remove inaccessible fields where the use case requires it.
- Validate untrusted input and use bind variables rather than building unsafe dynamic queries.
- Review security separately for user-interface code, integrations, and background processing.
A Practical Salesforce Apex Learning Path
A structured learning sequence helps connect the language syntax with the Salesforce data model and execution environment.
- Learn Salesforce objects, fields, records, relationships, and transactions.
- Practice Apex variables, operators, conditions, loops, methods, and classes.
- Learn List, Set, and Map collections before writing trigger logic.
- Practice SOQL, relationship queries, SOSL, and DML statements.
- Write bulk-safe triggers with logic separated into classes.
- Learn exceptions, savepoints, and partial-success database methods.
- Study governor limits and inspect consumption with the
Limitsclass. - Write test classes with meaningful assertions and bulk scenarios.
- Learn record sharing, object permissions, and field-level security.
- Continue with queueable, batch, scheduled, integration, and Lightning-related Apex.
The Salesforce Trailhead module Apex Basics and Database provides guided exercises for working with Apex and Salesforce data.
Beginner Apex Practice Exercises
Use small exercises to practice one platform concept at a time:
- Write a method that calculates a percentage discount and test null, zero, and positive values.
- Query Accounts from a selected industry and store them in a map keyed by record ID.
- Create several Contact records with one list-based insert operation.
- Write a before-insert trigger that normalizes a text field without performing DML.
- Write an after-update handler that collects related record IDs and queries them once.
- Create a queueable class that updates a group of records.
- Write tests for single-record, multi-record, and invalid-input scenarios.
Common Apex Mistakes to Avoid
- Querying inside a loop: collect IDs first and run a single query outside the loop.
- Running DML for each record: add changed records to a list and update the list once.
- Writing triggers for one record only: test every trigger with a collection of records.
- Ignoring null values: validate method arguments and optional fields before using them.
- Using broad exception handling: catch exceptions that can be handled meaningfully and preserve useful diagnostic information.
- Chasing only code coverage: assert business outcomes and failure behavior.
- Ignoring security: review sharing, object access, and field access for the code’s actual execution context.
- Implementing everything in Apex: use declarative Salesforce features when they provide a clearer and maintainable solution.
Frequently Asked Questions About Learning Apex
Is Apex the same as Java?
No. Apex uses syntax and object-oriented concepts that are familiar to Java developers, but it is a separate language designed for the Salesforce Platform. It includes native support for Salesforce records, SOQL, DML, triggers, transactions, and platform governor limits.
Can a beginner learn Salesforce Apex without Java?
Yes. Previous programming experience is useful but not required. A beginner should first learn basic programming concepts and the Salesforce data model, then progress to collections, SOQL, DML, classes, triggers, and tests.
Should I learn Salesforce Flow before Apex?
It is useful to understand Flow and other declarative tools before deciding that a requirement needs Apex. Salesforce solutions often combine configuration, Flow, and Apex. Knowing their respective strengths helps avoid unnecessary custom code.
Why must Apex triggers support multiple records?
Salesforce can process many records in one transaction through imports, integrations, APIs, automation, and bulk user actions. A trigger that assumes only one record may produce incorrect results or exceed governor limits.
Where can I practice Apex for free?
You can practice in a Salesforce Trailhead Playground or Developer Edition organization. Trailhead provides guided Apex modules, while the Developer Console or Salesforce Extensions for Visual Studio Code can be used to write and run code.
Learn Apex Editorial QA Checklist
- Confirm that each SOQL example selects only the fields used by the code.
- Verify that no example places a SOQL query or DML statement inside a loop.
- Check that trigger examples process all records in the trigger context.
- Confirm that test examples contain meaningful assertions rather than coverage-only execution.
- Review Apex examples for null handling and appropriate collection use.
- Verify that security guidance distinguishes record sharing from object and field permissions.
- Check current Salesforce documentation before publishing exact governor-limit values.
- Confirm that newly added WordPress code blocks use the correct PrismJS language or output class.
TutorialKart.com