Hi all!
In this article, we will be discussing how to implement a simple Rest API in salesforce and how to test it.
Things to notice:
2. The use of JSON.deserialize method
If you take a look at the swagger definitions of the POST and PUT methods, you would notice that the request body parameter is a json object with the field names defined as the salesforce field API names.
This allows us to use the JSON deserialize method, which can convert a json object to a salesforce object by mapping the field values based on the field API names defined in the json object.
3. Catching the DML exceptions and rethrowing BadRequestExceptions
Exception handling plays an important role in any development project and same goes for our API. In the business logic, exceptions can be anticipated and I have mapped the error message in the response as a Bad Request.
In this article, we will be discussing how to implement a simple Rest API in salesforce and how to test it.
Swagger Definition
I have defined a simple API with the basic GET, POST and PUT methods in this yaml file. You can use the swagger editor to view the definitions interactively.
- GET /payment - Retrieve all payment records
- GET /payment/{id} - Retrieve single payment records
- POST /payment - Create a new payment record
- PUT /payment - Update a new payment record
Classes
We will be using the following classes:
- PaymentAPI - This class will capture the API calls
- PaymentService - This class implements the business logic of the endpoints
- Util - The util class to include commonly used methods
- BadRequestException - Custom exception class to throw 400 HTTP error response
- PaymentAPITest - The test class to include test methods
PaymentAPI class
As you can see from the above implementation, PaymentAPI is the API interface that captures the API calls and then retrieves the respective response by calling the business logic class.Things to notice:
- Use of the annotation @RestResource with the URL mapping
- Use of annotations @HttpGet, @HttpPost and @HttpPut
- The use of Util methods to get and set request and response body parameters
PaymentService class
PaymentService includes the business logic class that controls the responses sent back to the client. For this example, I have omitted the use of complex logic but once you understand how the inputs and outputs are handled, any logical processing could be achieved.
Things to notice:
1. The use of field set to define the response parameters
If you take a look at the swagger definitions of the GET calls, you would notice that the responses include a payment object with the fields "Id", "Name" and "Amount__c".
If you take a look at the PaymentService implementation of the GET methods, in the SOQL query, I have not manually defined the fields of the results. Instead, I use salesforce field sets to define the fields I wish to have in my response.
For this, first you must create a field set in the desired SObject and add the necessary fields to it.
Once the field set is in place, I created a method in the Util class that would return a comma separated list of fields:
public static String getFieldsQuery(Schema.DescribeSObjectResult obj, String fieldSetName){ Schema.FieldSet fs = obj.fieldSets.getMap().get(fieldSetName); List<Schema.FieldSetMember> fields = fs.getFields(); String query = ' '; for(Schema.FieldSetMember field: fields){ query += field.getFieldPath() + ','; } String formattedQuery = query.removeEnd(','); return formattedQuery; }
It can be called as follows:
String fields = Util.getFieldsQuery(Schema.SObjectType.Payment__c, 'Payment_Response');
Then it can be appended to the SOQL query using a String operation.
2. The use of JSON.deserialize method
If you take a look at the swagger definitions of the POST and PUT methods, you would notice that the request body parameter is a json object with the field names defined as the salesforce field API names.
This allows us to use the JSON deserialize method, which can convert a json object to a salesforce object by mapping the field values based on the field API names defined in the json object.
3. Catching the DML exceptions and rethrowing BadRequestExceptions
Exception handling plays an important role in any development project and same goes for our API. In the business logic, exceptions can be anticipated and I have mapped the error message in the response as a Bad Request.
PaymentAPITest class
This is the test class that would cover test scenarios for the API class. Depending on the design, This class could be used to accomplish the test coverage of both the PaymentAPI as well as the PaymentService classes.
For this example, I have only defined three test methods that cover the important points I wanted to highlight:
1. The use of JSON.deserializeUntyped to convert response body into a map of objects
In the previous class, we used JSON.deserialize to convert a json object to a salesforce object. In this scenario, we used a similar method to convert a json object to a map of primitive data types.
2. The use of JSON.serialize to convert a map to json object
In the POST and PUT requests, we need to define a request body in the form of a json object and I have used the JSON.serialize method to convert a map of objects into a json object.
3. Negative test paths
When testing any code, it is important to test both positive and negative scenarios. In the third test method, I have tested the payment creation, by deliberately setting an ID field in the request body. This would throw a DMLException when inserting and would ideally convert it to a bad request with the 400 status code.
Testing the API using Postman
You can expose your APIs for external use by using connected apps.
1. Connected App
In order to call your endpoints, you need to have a Connected App.
- In your setup, Quick Search for "App Manager" and click on "New Connected App"
- Define the required fields and click on "Enable OAuth Settings" checkbox
- In the new fields, define a callback URL and select desired scopes
When the settings are saved, app details page will be displayed and you need to note down the "Consumer Key" and "Consumer Secret" for the next step.
2. Access token
Once you have the app credentials, you can use salesforce token endpoint to retrieve an access token.
In order to retrieve a token using the password grant type, you need to define a combination of your user password and your user security token. In your profile settings, click on "Reset My Security Token" which would email you your security token.
If your user password is "abcd" and your security token is "1234", the password for the token call would be "abcd1234".
Use the following parameters in the token call:
- grant_type - "password"
- username - Your salesforce username
- password - The combination of your password and security token
- client_id - The Consumer Key of the App
- client_secret - The Consumer Secret of the App
The token endpoint for production environments would be "https://login.salesforce.com/services/oauth2/token" and for sandbox, the token endpoint domain would be "test.salesforce.com".