Salesforce REST API using Spring Web Services

There are many examples out there of using the Salesforce REST API. But I didn't come across any showing explicitly how to do it with Spring Web Services. Base Java, other Java libraries, Node.js, Ruby, curl, and so on are some of the examples you trip over. But not much with Spring Web Services. Though maybe it is out there and I missed. (The internet is infinite after all, right?) Thus, I coded up a quick example of how to do it. My goal wasn't to create another API library. My goal is to show how to. The codebase is at https://github.com/billryoung/sfdc-springws-example, but I'll walk through the key parts in this article.

Login

Everything starts with login. For my example, I coded the user/password authentication flow. For reference, the API docs for user/password login are here.

As the API docs show, the login needs a body with the user, password, and other attributes as a URL type string in the body. An easy way is to use spring's MultiValueMap. By default it will convert into that format and handle all string escaping. You do have special characters in your password, right? ;)

MultiValueMap<String,String> bodyValues = new LinkedMultiValueMap<String,String>();
bodyValues.add("grant_type", grant_type);
bodyValues.add("client_id", client_id);
bodyValues.add("client_secret", client_secret);
bodyValues.add("username", username);
bodyValues.add("password", password);

There isn't a body required for this operation, but the HTTP header must set a content type. And since we're sending a body and headers, we need to wrap both together into a HttpEntity.

HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
HttpEntity<MultiValueMap<String,String>> requestEntity = new HttpEntity<MultiValueMap<String,String>>(bodyValues, headers);

Before we can make the call, we need an object to save the result. We could save to a String to get the raw JSON, but we can make a simple Java object with get/set methods for each of the attributes that login call returns, like "access_token." There's nothing very fancy about this. See my example here.

Now we need a RestTemplate and then we can make a POST.

RestTemplate restTemplate = new RestTemplate();
String login_url = "https://login.salesforce.com/services/oauth2/token";
ResponseEntity<LoginResponse> responseEntity = restTemplate.exchange(
   login_url, //where to send to
   HttpMethod.POST, //POST for login
   requestEntity, //what to send (body and headers)
   LoginResponse.class);  //what to parse the response into

Notes on the call.

  1. Change login url to test if needed
  2. postForEntity would work here too, but I'm showing exchange as it is a bit more general and that's the whole point of my example.
  3. Exchange has an optional last optional parameter that can replace variables in the URL we are posting too.
  4. responseEntity.getBody() has your LoginResponse which has your access_token and instance_url.

See the full code here

Query Example

Now that you've logged in, you have an access token and instance url. Other calls are similar in how to use the exchange method. I'll show how to do a Query of Account as an example. And for reference, the query API docs are here.

First, we need a few plain Java objects with get/set methods for everything that gets returned. Notice from the API that there is an outer entity that has records and each record has the attributes you queried for plus an embedded attributes. See my AccountQueryRecord, AccountQueryRecords, and QueryAttributes classes here.

The only special gotcha that I noticed is that your get/set method names must match up to the fields coming back, exactly including case. That makes it a bit odd when the API gives back upper case. For instance, a Java method like "getName" is a property of lower case "name." But there's an easy annotation to use.

import com.fasterxml.jackson.annotation.JsonProperty;

...

@JsonProperty("Name")
public String getName() {
    return name;
}
@JsonProperty("Name")
public void setName(String name) {
  this.name = name;
}

You can use the same annotation if you'd like just a better Java type name. (And be careful on the JsonProperty package. There are differently libraries out there. Make sure you're using the one spring is looking for.)

As for the actual API call, we need to create a dynamic URL. It needs the instance URL and the actual query. (Tip, springws has support for replacing and escaping variables into the URL.) And we need to supply the access token in header. Thus, we use the HttpEntity again. But this time, there isn't a body, so we don't have to worry about a body. The overall code goes like this:

String query = "select Id, Name from Account limit 10";

//create the url to call
String url = loginResponse.getInstance_url()
		+"/services/data/"
		+ "v"+api_version
		+ "/query/?q={q}";
log.debug("query: url="+url);
		
//create a Map for variables in the URL
//namely the query
Map<String, String> uriVariables = new HashMap<String, String>();
uriVariables.put("q", query);

//create the headers - need to pass access token from login
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", "Bearer "+loginResponse.getAccess_token());

//create our response
//body is empty string, since there is no body for this request
HttpEntity<String> requestEntity = new HttpEntity<String>("", headers);

//need a template to request things from
RestTemplate restTemplate = new RestTemplate();

//issue the GET
ResponseEntity<AccountQueryRecords.class> responseEntity = restTemplate.exchange(
   url, //url to request
   HttpMethod.GET, //GET operation
   requestEntity, //request empty body and http headers (access token)
   AccountQueryRecords.class, //class type to parse response into
   uriVariables); //variables to replace in the url

For query API specifically, remember to check if the response has more records and do a follow up query. Also, the result class needs to match up with the specific fields you're selecting.

You can see all the code together in a full example here: My full codebase example

Overall, from there, the other operations are similar. If you look at how I did login and query, from there you could follow the same way to call any of the other API operations. I'm sure you'll need some other parts of the Spring WS API, but hopefully with these basics you can apply the tons of springws articles elsewhere on the web to build off it more.

Related links that may be helpful:

  1. Spring's guide on how to consume a simple web service with spring
  2. Salesforce's REST api docs
  3. Spring boot guide

To view or add a comment, sign in

More articles by Bill Young

  • Salesforce Business Technology Architect-a-thon

    What was my next challenge after running a marathon? My day job is Enterprise Architecture, so I helped organize an…

    2 Comments
  • Salesforce SFDX Plugin - REST API Access

    The Salesforce SFDX plugin provides a library of some pre-fab commands, like run a SOQL query. The sample hi command…

  • SFDX Plugin for Record Type Ids in JSON Files

    SFDX has a great feature (data:tree:export and import) where you can export data from a Salesforce instance to use as…

    5 Comments
  • Creating A Salesforce SFDX Plugin

    It was a rainy evening outside. That meant the new roof on my shed had to wait.

  • Adventures With Java URL Handler

    Have you ever wanted the native URL Java object to support your personal protocol? Well me neither, until I came across…

  • Creating Java KeyStore Programmatically

    Challenge: A Java application at runtime that will receive a public X509 certificate and a RSA private key will create…

    2 Comments
  • Sharing Scratch Orgs In Salesforce DX

    I like that Salesforce DX enables Scratch Orgs to be per individual. But, it can still be handy to share a single…

    1 Comment

Explore content categories