Salesforce Developer Apex Rest Exposing Apex
// Salesforce -
Developer - REST - Expose Your Apex Class as a Web Service:
You can expose your Apex
class methods as a REST or SOAP web service operation.
By making your methods
callable through the web, you allow third party
applications (or your
own external applications) to access your data.
Apex REST methods can be
used in managed and unmanaged packages. When calling
Apex REST methods that
are contained in a managed package, you need to include
the managed package
namespace in the REST call URL. For example, if the class
is contained in a
managed package namespace called packageNamespace and the
Apex REST methods use a
URL mapping of /MyMethod/*, the URL used via REST to
call these methods would
be of the form
Instead of using custom
Apex code for REST and SOAP services, external
applications can
integrate with Salesforce by using Salesforce’s REST and SOAP
APIs. These APIs let you
create, update, and delete records. However, the
advantage of using Apex
web services is that Apex methods can encapsulate
complex logic. This
logic is hidden from the consuming application. Also, the
Apex class operations
can be faster than making individual API calls, because
fewer roundtrips are
performed between the client and the Salesforce servers.
With an Apex web service
call, there is only one request sent, and all
operations within the
method are performed on the server.
The security context
under which Apex web service methods run differs from the
security context of
Salesforce APIs. Unlike Salesforce APIs, Apex web service
methods run with system
privileges and don’t respect the user’s object and field
permissions. However,
Apex web service methods enforce sharing rules when
declared with the with
sharing keyword.
All sObjects, standard
or custom, come with a REST API, so we do not have to
create a REST API for
each of our objects. Apex REST is used
for defining
custom
interactions. Apex REST is good for
abstracting complex interactions
to a single call:
1. Inserting / updating
multiple objects
2. Callouts to external
systems
3. Custom search
interfaces.
All custom Apex REST
services are accessed in the same namespace.
For example,
can be something like
v1/joborders
Making your Apex class
available as a REST web service is straightforward:
1. Define your class as
global
2. Add the @RestResource
annotation to the class
@RestResource(urlMapping='/Account/*')
global with sharing class MyRestResource {
@HttpGet
global static Account getRecord() {
// Add your code
}
}
As you can see, the class is annotated with
@RestResource(urlMapping='/Account/*). The
base endpoint for Apex REST is
appended to the base endpoint to form the
endpoint for your REST service.
For example, in the class example, the REST
endpoint is
The URL mapping is case-sensitive and can
contain a wildcard character (*).
We should think about versioning your API
endpoints so that you can provide
upgrades in functionality without breaking
existing code. You could create t
wo classes specifying URL mappings of
/Cases/v1/* and /Cases/v2/* to
implement this functionality.
3. Define methods as
global static
4. Add annotations to
the class and methods.
@HttpGet: Reads or retrieves records (HTTP
VERB: GET)
@HttpPost: Create records (HTTP VERB: POST)
@HttpDelete: Deletes records (HTTP VERB:
DELETE)
@HttpPut: Update existing records or create
records. (HTTP VERB: PUT)
@HttpPatch: Update (HTTP VERB: PATCH)
@RestResource(urlMapping='/Cases/*')
global with sharing
class CaseManager {
@HttpGet
global static Case getCaseById() {
RestRequest request =
RestContext.request;
// grab the caseId from the end of the
URL
String caseId =
request.requestURI.substring(
request.requestURI.lastIndexOf('/')+1);
Case result = [SELECT
CaseNumber,Subject,Status,Origin,Priority
FROM Case
WHERE Id = :caseId];
return result;
}
@HttpPost
global static ID createCase(String subject,
String status,
String origin, String priority) {
Case thisCase = new Case(
Subject=subject,
Status=status,
Origin=origin,
Priority=priority);
insert thisCase;
return thisCase.Id;
}
@HttpDelete
global static void deleteCase() {
RestRequest request =
RestContext.request;
String caseId =
request.requestURI.substring(
request.requestURI.lastIndexOf('/')+1);
Case thisCase = [SELECT Id FROM Case
WHERE Id = :caseId];
delete thisCase;
}
@HttpPut
global static ID upsertCase(String subject,
String status,
String origin, String priority, String
id) {
Case thisCase = new Case(
Id=id,
Subject=subject,
Status=status,
Origin=origin,
Priority=priority);
// Match case by Id, if present.
// Otherwise, create new case.
upsert thisCase;
// Return the case ID.
return thisCase.Id;
}
@HttpPatch
global static ID updateCaseFields() {
RestRequest request =
RestContext.request;
String caseId =
request.requestURI.substring(
request.requestURI.lastIndexOf('/')+1);
Case thisCase = [SELECT Id FROM Case
WHERE Id = :caseId];
// Deserialize the JSON string into
name-value pairs
Map<String, Object> params = (Map<String,
Object>)JSON.deserializeUntyped(request.requestbody.tostring());
// Iterate through each parameter field
and value
for(String fieldName : params.keySet())
{
// Set the field and value on the
Case sObject
thisCase.put(fieldName,
params.get(fieldName));
}
update thisCase;
return thisCase.Id;
}
}
// Salesforce - Developer
- REST - Expose Your Apex Class as a Web Service - Testing:
Testing your Apex REST
class is similar to testing any other Apex class. Just
call the class methods
by passing in parameter values and then verify the
results. For methods
that don’t take parameters or that rely on information in
the REST request, create
a test REST request.
In general, here’s how
you test Apex REST services. To simulate a REST request,
create a RestRequest in
the test method, and then set properties on the request
as follows. You can also
add params that you “pass” in the request to simulate
URI parameters.
// Set up a test request
RestRequest request =
new RestRequest();
// Set request
properties
request.requestUri =
+ recordId;
request.httpMethod =
'GET';
// Set other properties,
such as parameters
request.params.put('status',
'Working');
// more awesome code
here....
// Finally, assign the
request to RestContext if used
RestContext.request =
request;
If the method you’re
testing accesses request values through RestContext, assign
the request to
RestContext to populate it (RestContext.request = request;).
1. In the Developer
Console, select File | New | Apex Class.
2. For the class name,
enter CaseManagerTest and then click OK.
3. Replace the
autogenerated code with the following class definition.
@IsTest
private class CaseManagerTest
{
@isTest static void testGetCaseById() {
Id recordId = createTestRecord();
// Set up a test request
RestRequest request = new
RestRequest();
request.requestUri =
+ recordId;
request.httpMethod = 'GET';
RestContext.request = request;
// Call the method to test
Case thisCase =
CaseManager.getCaseById();
// Verify results
System.assert(thisCase != null);
System.assertEquals('Test record',
thisCase.Subject);
}
@isTest static void testCreateCase() {
// Call the method to test
ID thisCaseId = CaseManager.createCase(
'Ferocious chipmunk', 'New',
'Phone', 'Low');
// Verify results
System.assert(thisCaseId != null);
Case thisCase = [SELECT Id,Subject FROM
Case WHERE Id=:thisCaseId];
System.assert(thisCase != null);
System.assertEquals(thisCase.Subject,
'Ferocious chipmunk');
}
@isTest static void testDeleteCase() {
Id recordId = createTestRecord();
// Set up a test request
RestRequest request = new
RestRequest();
request.requestUri =
+ recordId;
request.httpMethod = 'GET';
RestContext.request = request;
// Call the method to test
CaseManager.deleteCase();
// Verify record is deleted
List<Case> cases = [SELECT Id
FROM Case WHERE Id=:recordId];
System.assert(cases.size() == 0);
}
@isTest static void testUpsertCase() {
// 1. Insert new record
ID case1Id = CaseManager.upsertCase(
'Ferocious chipmunk', 'New',
'Phone', 'Low', null);
// Verify new record was created
System.assert(Case1Id != null);
Case case1 = [SELECT Id,Subject FROM
Case WHERE Id=:case1Id];
System.assert(case1 != null);
System.assertEquals(case1.Subject,
'Ferocious chipmunk');
// 2. Update status of existing record to Working
ID case2Id = CaseManager.upsertCase(
'Ferocious chipmunk',
'Working', 'Phone', 'Low', case1Id);
// Verify record was updated
System.assertEquals(case1Id, case2Id);
Case case2 = [SELECT Id,Status FROM
Case WHERE Id=:case2Id];
System.assert(case2 != null);
System.assertEquals(case2.Status,
'Working');
}
@isTest static void testUpdateCaseFields()
{
Id recordId = createTestRecord();
RestRequest request = new
RestRequest();
request.requestUri =
+ recordId;
request.httpMethod = 'PATCH';
request.addHeader('Content-Type',
'application/json');
request.requestBody =
Blob.valueOf('{"status": "Working"}');
RestContext.request = request;
// Update status of existing record to
Working
ID thisCaseId =
CaseManager.updateCaseFields();
// Verify record was updated
System.assert(thisCaseId != null);
Case thisCase = [SELECT Id,Status FROM
Case WHERE Id=:thisCaseId];
System.assert(thisCase != null);
System.assertEquals(thisCase.Status,
'Working');
}
// Helper method
static Id createTestRecord() {
// Create test record
Case caseTest = new Case(
Subject='Test record',
Status='New',
Origin='Phone',
Priority='Medium');
insert caseTest;
return caseTest.Id;
}
}
4. Press CTRL+S to save.
5. Run all the tests in
your org by selecting Test | Run All.