The Blog - Expert Thoughts & Opinions

Salesforce Rest API + AngularJS

In this post, I want to explain the integration of Salesforce REST API and AngularJS application. For serving static data and for holding Salesforce application data, I am using a simple proxy server written on Node.

Firstly, we need to define Connected App. To do this, go to your Salesforce organization. Next, go to Setup -> Build -> Create ->  Apps and create new Connected App.

New Connected App

New Connected App part 2

In callback URL section, we need provide at least one URL. You can write several URLs, for example:

 http://localhost:3000#oauth2/callback

 and for production:

 https://sfexpress.herokuapp.com#/oauth2/callback

 Then click 'Save' and we can see that Salesforce provides a Consumer Key and Secret Key.

Consumer Key

The second step is to add CORS to our proxy servers. For this go Setup -> Administer -> Security Controls -> CORS and click 'New'.

CORS

Now our Salesforce environment is ready.

In the next step, we need to create a simple server. For this, we will be using expresjs and salesforce-oauth2 node libraries. The App structure looks like this:

expressjs

The core of our proxy-server is express js:

var express = require('express');
var app = express();

We also should store our Salesforce app data. For this, we are using environment variables:

var consumerKey = process.env.CONSUMER_KEY;
var secretKey = process.env.CONSUMER_SECRET;
var callbackURL = process.env.CALLBACK_URL;

For OAuth2 in Salesforce, we will be using salesforce-oauth2 node library:

var oauth2 = require('salesforce-oauth2');

For oauth2 authentication, we need to add 2 URL handlers:

  1. for fetching authorization code
  2. for fetching the access token

In this handler, express redirects to Salesforce authentication URL.

app.get('/oauth2/auth', function (req, res) {
    var uri = oauth2.getAuthorizationUrl({
        redirect_uri: callbackURL,
        client_id: consumerKey,
        scope: 'api'
    });
    return res.redirect(uri);
})

After successful authentication, SF redirects us by URL which we provided and adds the new parameter code.

This code we will be accepted in our Angular app and proxy:

app.controller("CallbackController", function(LoginService, $location, localStorageService, $window, $http) {
		var absUrl = $location.absUrl();
		var regExp = /\?code=(\S+)#/g;
		var res = regExp.exec(absUrl);
		if(res) {
			var code = res[1]
			LoginService.getAccessToken({code: code})
				.then(function(response) {
					//handle success
				}, function(response) {
					$location.path('/login')
				})
		} else {
			console.error('Error during log in');
		}
	})

We take the code and add to proxy via our custom LoginService. And within the proxy, we make the call to Salesforce to fetch the access token with this code:

app.post('/RestTest/oauth/callback', function(req, res) {
  	var code = req.body.code;
  	code = code.replace(/%3D/g, '=')
  	oauth2.authenticate({
        redirect_uri: callbackURL,
        client_id: consumerKey,
        client_secret: secretKey,
        code: code
    }, function(error, payload) {
    	if(error) {
    		res.send(error)
    	} else {
		console.log(payload)
    		res.send(payload)
    	}
    })
})

Now we have an access token and can request data directly from the browser. After fetching the token, save it in local storage and add to default header to $http service:

localStorageService.set('instanceUrl', response.data.instance_url);
localStorageService.set('accessToken', response.data.access_token);
$http.defaults.headers.common.Authorization = 'Bearer ' + response.data.access_token;

Now for making a request, we should have an HTTP header with the access token.

After reloading page $http service, of course, will lose the default Authorization header. To avoid this, we should set this default header in run block from local storage:

    .run(function(localStorageService, $http) {
		let accessToken='Bearer '+ localStorageService.get('accessToken');
           $http.defaults.headers.common.Authorization = accessToken
    })

<

For serving local storage, we are using localStorageService. Since access token has an expiration time, we should handle and redirect to the login page. For this, we create an HTTP interceptor :

.factory('SalesforceResponseObserver', ($location, $q)=>{
&#9;return {
&#9;&#9;'responseError': function(errorResponse) {
&#9;&#9;&#9;let status = errorResponse.statu
&#9;&#9;&#9;if(status == '-1' || status == 401 
                    || status == 403) {
&#9;&#9;&#9;&#9;$location.path('/login')
&#9;&#9;&#9;}
&#9;&#9;&#9;&#9;return $q.reject(errorResponse)
&#9;&#9;&#9;}
&#9;&#9;}
&#9;})

and add to interceptors array:

.config(($httpProvider) => {
    $httpProvider.interceptors.push('SalesforceResponseObserver');
})

For fetching data from Salesforce REST API, we should define several services:

 Here we defined 2 simple methods. First for fetching data by sobject name and second for getting all available sobjects:

.factory('SalesforceRecordService', ($http, localStorageService) => {
&#9;var instanceUrl =
&#9;&#9;localStorageService.get('instanceUrl');
&#9;return {
&#9;&#9;getRecords: function(recordName) {
&#9;&#9;&#9;var url = instanceUrl +
&#9;&#9;&#9;    '/services/data/v20.0/sobjects/' +
&#9;&#9;&#9;    recordName;
&#9;&#9;&#9;return $http.get(url);
&#9;&#9;},

&#9;&#9;getListAvailableSobjects: function() {
&#9;&#9;&#9;var url = instanceUrl +
&#9;&#9;&#9;&#9;'/services/data/v39.0/sobjects';
&#9;&#9;&#9;return $http.get(url);
&#9;&#9;},
&#9;}
})

And in controller, we use this service:

.controller('RecordsController',($scope, SalesforceRecordService,  $routeParams) => {
&#9;var recordName = 'Account';
     SalesforceRecordService.getRecords(recordName).then((response) => {
&#9;&#9;$scope.data = JSON.stringify(response.data)
&#9;}, (response) => {
&#9;&#9;console.error('Erorr in SalesforceRecordService => ', response);
&#9;})
})

and here's the simple HTML template:

 

 

 ng-bind="data">

 

And the result will be something like this:

You can find all the code here:

sfexpress