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. However, in general one needs to authenticate the endpoint for accessing the content.
This article provides is to give you a demonstration of combining RESTful webservices and Site.com to provide an URL which can be publicly accessed without need of any Authentication. For anyone who wants to expose their public data using a simple URL, this would be helpful.
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:
- Determine the current customer and their email.
- Create a support case and assign it to the support person responsible for the state that the customer is based.
- Find the attachment associated with the most recent Support Library object associated to the type of case being submitted.
- 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:
[sourcecode language=”java”]
@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;
}
}
[/sourcecode]
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.
Combining the REST Class and Site.com
In my example below the purpose is to expose all the ‘Special Event’ object records as JSON so that the client can use that information and display those events in their website.
For this purpose I need Site.com Enabled with the Site.com Profile having access to the ‘Special Event’object.
Example of Utilizing GET Request:
[sourcecode language=”java”]
@RestResource(urlMapping=’/calEventsLinvio’)
global class calEventsLinvioRestService {
@HttpGet
global static void doGetCalEvents() {
RestContext.response.addHeader(‘Content-Type’, ’Application/JSON’);
String name = RestContext.request.params.get(‘name’);
//- //DO NOT include any events that have expired.//-//
//-//Include ONLY& nbsp;events that& nbsp;have a& nbsp;"Status& amp;quot; of& nbsp;& quot;Published& quot; and a checkmark& nbsp;next to " Include& nbsp; in Calendar& amp;quot; and starting later than today with Status of ‘Published’ and with ‘Include in calendar’checkbox checked.//-//
list<evt__Special_Event__c >& nbsp;calEvnts& nbsp;= new list& lt;evt__Special_Event__c >();
//Record types - Conference,Quick& nbsp;Event,Seminar,Specialty& nbsp;Event, Web Meeting
list<evt__Special_Event__c > LstQuickEvnts= [select name, evt__City__c,& nbsp;evt__Country__c,& nbsp;evt__Short_Description__c,& nbsp;Registration_URL__c,& nbsp;Quick_Event_URL__c& nbsp;, evt__Event_Type__c,& nbsp;evt__Start__c, Location__c from evt__Special_Event__c WHERE evt__Start__c & amp;gt;=LAST_N_DAYS:0 AND evt__Status__c=’Published’ AND evt__Include_in_Calendar__c& nbsp;= True AND RecordType.name=’Quick Event’];
list<evt__Special_Event__c& nbsp;& amp;gt; LstConferenceEvnts= [select name, evt__City__c, evt__Country__c, evt__Short_Description__c,& nbsp;Registration_URL__c,& nbsp;Conference_Site_URL__c& nbsp;, evt__Event_Type__c,& nbsp;evt__Start__c,& nbsp;Location__c& nbsp; from& nbsp;evt__Special_Event__c WHERE evt__Start__c & gt;=LAST_N_DAYS:0 AND& nbsp;evt__Status__c=’Published’& nbsp;AND evt__Include_in_Calendar__c& nbsp;= True AND RecordType.name=’Conference’];
list<evt__Special_Event__c > LstSeminarEvnts= [select name, evt__City__c,& nbsp;evt__Country__c,& nbsp;evt__Short_Description__c, Registration_URL__c, Seminar_Event_URL__c, evt__Event_Type__c, evt__Start__c, Location__c from evt__Special_Event__c& nbsp;WHERE evt__Start__c& nbsp;>=LAST_N_DAYS:0 AND evt__Status__c=’Published’& nbsp;AND evt__Include_in_Calendar__c& nbsp;= True AND RecordType.name=’Seminar’];
list<evt__Special_Event__c > LstSpecialityEvnts= [select name,& nbsp;evt__City__c,& nbsp;evt__Country__c,& nbsp;evt__Short_Description__c,& nbsp;Registration_URL__c, evt__Event_Landing_Page__c,& nbsp;evt__Event_Type__c, evt__Start__c, Location__c& nbsp; from evt__Special_Event__c WHERE evt__Start__c& nbsp;>=LAST_N_DAYS:0& nbsp;AND& nbsp;evt__Status__c=’Published’& nbsp;AND evt__Include_in_Calendar__c& nbsp;= True& nbsp;AND RecordType.name=’Specialty Event’];
list<evt__Special_Event__c > LstWebMeetingEvnts= [select name, evt__City__c, evt__Country__c, evt__Short_Description__c,& nbsp;Registration_URL__c,& nbsp;Web_Meeting_Event_URL__c,& nbsp;evt__Event_Type__c,& nbsp;evt__Start__c, Location__c from evt__Special_Event__c& nbsp;WHERE evt__Start__c& nbsp;>=LAST_N_DAYS:0& nbsp;AND evt__Status__c=’Published’& nbsp;AND evt__Include_in_Calendar__c& nbsp;= True AND RecordType.name=’Web Meeting’];
for(evt__Special_Event__c QuickEvent: LstQuickEvnts){
calEvnts.add(QuickEvent);
}
for(evt__Special_Event__c ConferenceEvent: LstConferenceEvnts){
calEvnts.add(ConferenceEvent);
}
for(evt__Special_Event__c SeminarEvent: LstSeminarEvnts){
calEvnts.add(SeminarEvent);
}
for(evt__Special_Event__c SpecialityEvent: LstSpecialityEvnts){
calEvnts.add(SpecialityEvent);
}
for(evt__Special_Event__c WebMeetingEvent: LstWebMeetingEvnts){
calEvnts.add(WebMeetingEvent);
}
String EventsJSON = JSON.serialize(calEvnts);
RestContext.response.responseBody = Blob.valueof(EventsJSON);
}
}
[/sourcecode]
Now by utilizing my Site.com URL- and appending services/apexrest/URLMapping to the Site.com URL . We will have our desired Public Endpoint which exposes required Special Event Records without need of any Authentication.
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.