Creating REST APIs using Apex REST

Abstract

The Force.com REST API lets you use a simple and lightweight API to access Force.com data, using standard OAuth, together with a choice of data flavors – XML and JSON. The REST API is configured out of the box to suit the basic CRUD (create, read, update and delete) operations of data management.

Apex REST augments this functionality and makes it possible for developers to create your own REST-based web services using Apex. It has all of the advantages of the REST architecture, provides the ability to define custom logic and includes automatic argument/object mapping.

This article provides an introduction and overview of the Apex REST functionality, showing you how to take your first steps in creating REST-based web services in Apex. As Apex REST builds on the Force.com REST implementation itself, the Getting Started with the Force.com REST API article is recommended reading.

Example: Assigning Case By Location

Let’s assume a company has agents in the field that will be submitting cases back to their Force.com application. It’s a small to medium sized company, but large enough that they have one team out on the road creating the case and then specific employees responsible for handling the cases by region.

For simplicity’s sake, we’ll have the support team assigned to areas by state – but the road warriors of the company may be anywhere in the country when they submit the case back to the company.

To make this example sufficiently complex that it couldn’t be simply solved by workflow rules – let us also assume that the first action in any support case is to email out an attachment that must always be the most recent in the support team’s library to the customer.

So the logic would need to work in the following manner:

  1. Determine the current customer and their email.
  2. Create a support case and assign it to the support person responsible for the state that the customer is based.
  3. Find the attachment associated with the most recent Support Library object associated to the type of case being submitted.
  4. Email the customer with the attachment and notify them that their case has been entered into the system.

Solving this without Apex REST would be a multistep process. If the customer data wasn’t stored on the mobile device somewhere, the application would need to hit the query end point to make sure they have the correct information. It would also need to find the correct support person before hitting the create endpoint. It would need to probably rely on a trigger to correctly handle if not 3, certainly 4. While acceptable, this process might take longer, be hard to maintain and may not provide the field agent with the response information they need right away.

Or the developer could create a single Apex REST endpoint. The code would look something like this:

@RestResource(urlMapping='/FieldCase/*')
global with sharing class RESTCaseController {

@HttpPost	
  global static String createNewCase(String companyName, String caseType) {
     System.debug('COMPANY: '+companyName);
     System.debug('CASE TYPE: '+caseType);
     
     List<Account> company = [Select ID, Name, Email__c, BillingState from Account where Name = :companyName];
	 List<Support_Member__c> support;
	 
	 if(company.size() > 0) {
	 	support = [SELECT ID, User__c, Name, User__r.Email from Support_Member__c WHERE Support_State__c = :company[0].BillingState LIMIT 1];
	 }
     List<Support_Documentation__c> doc = [SELECT ID from Support_Documentation__c WHERE Type__c = :caseType ORDER BY CreatedDate ASC LIMIT 1];
     
     if(company.size() == 0 || support.size() == 0 || doc.size() == 0) {
     	return 'No support data exists for this problem';
     }
     
     Case c = new Case();
     c.OwnerId = support[0].User__c;
     c.AccountId = company[0].Id;
     c.Subject = caseType + ' for '+companyName;
     c.Status = 'Open';
     insert c;
	 
     sendEmail(companyName, company[0].Email__c, doc[0].Id, support[0].Name, support[0].User__r.Email);
          
     return 'Submitted case to '+support[0].Name+' for '+companyName+'.  Confirmation sent to '+company[0].Email__c;
  }
}

With a second “sendEmail” function, which would handle getting the correct attachment and then send that out to the customer. Some of this might be, depending on the use case, be better handled by a trigger or workflow still – but this example shows you can simplify a more complicated business process down to a single endpoint and not have to code a multiple step procedure which will perform slower and be more difficult to maintain. Apex developers don't need to use a different structure to set their code up for use with an Apex REST endpoint with the exception of the @HttpPost (or related HTTP method) annotation. By utilizing this custom class, you can automate several tasks within Apex itself simplified down to a single endpoint.


Setting up an Apex REST endpoint

To declare a class as a custom endpoint, you only need to annotate a global class with “@RestResource” and define the name of the endpoint.

In the above example, we start the class with the following lines:

@RestResource(urlMapping='/FieldCase/*')
global with sharing class RESTCaseController {

This will define “FieldCase” as an accessible endpoint and the full URI would be constructed from your instance URL concatenated with “/services/apexrest/FieldCase”. For example, something like “https://na8.salesforce.com/services/apexrest/FieldCase”.

What happens at that endpoint is completely up to the developer depending on how the class is defined. REST defines endpoints as a combination of URI and HTTP methods, the class can declare different actions depending on if the incoming request is an HTTP GET, POST, PUT or DELETE request. After this class is saved, Apex REST sets everything else up: there are no additional steps required from the developer to make the endpoint available, or trigger data decoding or anything else - the custom REST endpoint is ready to go.


Utilizing the REST Request and REST Response objects

Any Apex REST service that we define can accept a REST Request and Response objects as the first parameters if necessary. These objects will provide the developer with additional information about the REST transaction that is taking place and give them control over processing that information before the transaction is complete. If this information is not needed, as in our POST example above, the objects can be left out of the method definition.


Using GET and DELETE

HTTP GET defines data as a series of query parameters in the URI itself. For example, here’s a URI:

https://na8.salesforce.com/services/apexrest/FieldCase?companyName=GenePoint

So if we wanted to write an Apex endpoint, which delivers all the cases for a particular company, based on name, the developer can annotate a method with “@HttpGet”. In order to decode the query string, Apex REST provides access to a global RestContext variable. This example will get the companyName from the query string, and look up related cases to that company. This provides for an easy to use REST endpoint which is simpler to use than the standard query one.

@HttpGet
  global static List<Case> getOpenCases() {
    String companyName = RestContext.request.params.get('companyName');
    Account company = [ Select ID, Name, Email__c, BillingState from Account where Name = :companyName];
     
    List<Case> cases = [SELECT Id, Subject, Status, OwnerId, Owner.Name from Case WHERE AccountId =: company.Id];
    return cases;
    
  }

In the above example, the RestResponse object is included to illustrate where it would be added to the method definition.

In this instance, calling “https://na8.salesforce.com/services/apexrest/FieldCase?companyName=GenePoint” via GET will return a JSON array of cases related to the company.

The DELETE HTTP method should be reserved for removing data as opposed to retrieving data, but is otherwise structured the same. A method annotated with “@HttpDelete” is structured the same as “@HttpGet”, utilizing the RestRequest object to generate a map of parameters. Utilizing this Apex:

@HttpDelete
  global static String deleteOldCases() {
    String companyName = RestContext.request.params.get('companyName');
    Account company = [ Select ID, Name, Email__c, BillingState from Account where Name = :companyName];
     
    List<Case> cases = [SELECT Id, Subject, Status, OwnerId, Owner.Name from Case WHERE AccountId =: company.Id AND Status = 'Closed'];
    delete cases;
    
    return 'Closed Cases Deleted';
  }

with the same URI above, however with the DELETE method, will look up cases with the status of closed, delete them and then return the status string.

Using POST, PUT and PATCH

The POST HTTP method is mostly reserved for creating new records, while PUT and PATCH can be utilized to modify and update existing records. POST and similar methods submit a body of data, and Apex REST has the ability to automatically decode that data into incoming parameters on the function. So if an application sends the following post data:

{“companyName”:”GenePoint”,”caseType”:”Software”}

to the endpoint using the POST method, this example function above will pick up those values automatically. So this code:

@HttpPost 
  global static String createNewCase(String companyName, String caseType) {
     System.debug('COMPANY: '+companyName);
     System.debug('CASE TYPE: '+caseType);
     return “Done”;
   }

would display “GenePoint” and “Software” correctly in the debug logs. Just as DELETE is structurally the same as GET, PUT and PATCH are structurally the same as POST and the incoming body of data can be decoded the same way. The following example will find the case based on ID, change the status and append the description field (note that this code isn’t doing any validation on length of the description field, etc.):


@HttpPatch
  global static String updateCase(String caseId, String caseStatus, String caseNote) {
    Case companyCase = [SELECT Id, Subject, Status, Description from Case WHERE Id =: caseId];
    
    companyCase.Status = caseStatus;
    companyCase.Description += '/n/n';
    companyCase.Description += caseNote;
    update companyCase;
    
    return 'Case Updated';
}


Accepting Binary Data from RESTRequest Object

As an example of why a developer might need direct access to the RESTRequest object, here is an instance of using the PUT method to attached an image to an existing Account record:

  @HttpPut
  global static String updateCase(RestRequest req) {
      String companyName = req.params.get('companyName');
      Account company = [ Select ID, Name, Type, BillingState from Account where Name = :companyName];
    
      Attachment a = new Attachment();
      a.ParentId = company.Id;
      a.Name = 'test.png';
      a.Body = req.requestBody;
      
      insert a;
      
      return 'Attachment added';
  }

Both the query parameters and the binary attachment are available directly from the endpoint. Base64 encoding of the data should not be required, unlike SOAP based enpoints.

@HttpGet methods need to access querystring parameters manually via RestContext.request. When returning sObjects they are automatically serialized to JSON:

@HttpGet
global static sObject doGet() {
    RestRequest req = RestContext.request;
    Id AccountID = req.params.get('id');
    try {
        Account acc = [SELECT Id, Name, Type FROM Account WHERE Id = :AccountId LIMIT 1];
        return acc;
    } catch (exception e) {
        return null;
    }
}

Summary

By utilizing custom Apex REST endpoints, developers can tailor the REST API to suit the business needs of their application. An endpoint can be defined with an Apex Class using the @RestResource annotation, and the method annotations allow the endpoint to behave specifically for creating, updating, deleting and querying data within the instance. In areas where the REST API has already proven successful, i.e. third party web integrations and mobile applications – the ability to use Apex REST will make those applications even more versatile and powerful.

More Information


About the Author

Josh Birk is a Developer Evangelist at Salesforce.com and spends time unequally wrangling code as well as cats. Along with Apex, he also writes about Node, Flex, HTML5 and other topics - usually either at the Force.com Blog or @joshbirk on twitter.