Simple Inbound REST API Using Apex
There are multiple options to create REST APIs to expose Salesforce data and processes, some of the popular ones being -
- Directly use Object APIs
- Use composite APIs: Tie multiple APIs together in one call. See this post
- Invoke Salesforce Flows from external systems. See this post
- Use Apex to create REST APIs
While the Apex option is frowned upon as customization, it is by far the more flexible and powerful of the lot (IMO - your mileage may vary).
Let us create a simple REST API that supports GET & POST methods using Apex.
1. Create new Apex class for REST #
Create a new Apex class AccountSimpleRest
using your favourite editor. Before we write any further code, we will use a simple decorator to support REST APIs.
@RestResource(urlMapping='/account-simple/*')
global with sharing class AccountSimpleRest {
}
The statement urlMapping='/account-simple/*'
indicates that the salesforce instance should reserve /account-simple
URI to invoke the Apex class.
You can invoke this Apex class using the below URL -
<your_instance>/services/apexrest/account-simple
The simplest way to test the API is by using Workbench REST Explorer. Just navigate to https://workbench.developerforce.com/restExplorer.php, login using Salesforce, provide the service URI as /services/apexrest/account-simple/
, and you should be testing away to glory.
Now that we have the basic structure, let us create a GET method.
2. GET Method #
Write the below code in the Apex class to fetch and return account.
@RestResource(urlMapping='/account-simple/*')
global with sharing class AccountSimpleRest {
@HttpGet
global static List<Account> getAccount() {
List<Account> lstAccount;
try {
System.debug('Invoked REST getAccount');
lstAccount = [SELECT Name FROM Account LIMIT 10];
System.debug(lstAccount);
}
catch(Exception e) {
System.debug(e.getMessage());
}
return lstAccount;
}
Invoke the REST API from Workbench to see a list of accounts.
Let us improvise the code a bit to accept one or more characters from the URL, and filter the results.
@RestResource(urlMapping='/account-simple/*')
global with sharing class AccountSimpleRest {
@HttpGet
global static List<Account> getAccount() {
List<Account> lstAccount;
try {
System.debug('Invoked REST getAccount');
RestRequest req = RestContext.request;
String acctNameStr = req.requestURI.substring(req.requestURI.lastIndexOf('/')+1) + '%';
lstAccount = [SELECT Name FROM Account where Name like :acctNameStr LIMIT 10];
System.debug(lstAccount);
}
catch(Exception e) {
System.debug(e.getMessage());
}
return lstAccount;
}
}
You will now be able to provide a URL like so -
/services/apexrest/account-simple/b
.. to fetch all accounts starting with b
(case does not matter). The filter is applied by the SOQL and it is as simple as in “normal” Apex.
You may also have noted that RestRequest
exposes the request object and can be used to fetch URI, message header / body information, and more.
3. POST Method #
Next, write some code to support POST method that enables creating a new account.
@HttpPost
global static Account getAccount(String name, String accountNumber) {
Account acct = new Account();
try {
acct.name = Name;
acct.accountNumber = AccountNumber;
insert acct;
}
return acct;
}
Send data through Workbench API invocation to insert a new account
- URL
/services/apexrest/account-simple
- this is same as the URL used by GET method - Select
POST
as the method - Use the below
Request Body
{ "name": "abc 202212 02", "accountNumber" : "202212 02" }
The above actions should create a new account. Note that we directly return a response that has the Account
type.
Let us improve the code a bit to handle errors better.
public class RestException extends Exception {}
@HttpPost
global static void postAccount(String name, String accountNumber) {
Account acct = new Account();
RestResponse res = RestContext.response;
try {
acct.Name = name;
acct.AccountNumber = accountNumber;
System.debug('Input Name: ' + name);
if (name == null || accountNumber == null) {
throw new RestException('Required fields missing. Revalidate input and try again.');
}
insert acct;
res.responseBody = Blob.valueOf(JSON.serialize(acct));
res.statusCode = 200;
}
catch(RestException e) {
System.debug(e.getMessage());
res.statusCode = 500;
res.responseBody = Blob.valueOf(String.valueOf(e));
}
We made the following changes -
- Changed the return to
void
- Control the return code and description of the error
- Create a custom exception object and use that to return custom errors
The class AccountSimpleRest.cls
should look like so..
@RestResource(urlMapping='/account-simple/*')
global with sharing class AccountSimpleRest {
public class RestException extends Exception {}
@HttpGet
global static List<Account> getAccount() {
List<Account> lstAccount;
try {
System.debug('Invoked REST getAccount');
RestRequest req = RestContext.request;
String acctNameStr = req.requestURI.substring(req.requestURI.lastIndexOf('/')+1) + '%';
lstAccount = [SELECT Name FROM Account where Name like :acctNameStr LIMIT 10];
System.debug(lstAccount);
}
catch(Exception e) {
System.debug(e.getMessage());
}
return lstAccount;
}
@HttpPost
global static void postAccount(String name, String accountNumber) {
Account acct = new Account();
RestResponse res = RestContext.response;
try {
acct.Name = name;
acct.AccountNumber = accountNumber;
System.debug('Input Name: ' + name);
if (name == null || accountNumber == null) {
throw new RestException('Required fields missing. Revalidate input and try again.');
}
insert acct;
res.responseBody = Blob.valueOf(JSON.serialize(acct));
res.statusCode = 200;
}
catch(RestException e) {
System.debug(e.getMessage());
res.statusCode = 500;
res.responseBody = Blob.valueOf(String.valueOf(e));
}
}
}
Conclusion #
Apex provides an easy-to-develop approach to create REST APIs. You should probably prefer Composite APIs as the first choice for creating REST APIs, but there are use cases that need Apex -
- Accept JSON/XML messages that are not in salesforce-defined format
- Need for transformation of messages within salesforce
- Custom validations and automations which are too complex or too verbose when using other options
Use the above illustration as a spring-board to learn possibilities of REST APIs in Apex, but you are better off using a framework or standardized approach for more robust code.