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.
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.
The second step is to add CORS to our proxy servers. For this go Setup -> Administer -> Security Controls -> CORS and click 'New'.
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:
The core of our proxy-server is express js:
<span style="color: #008800; font-weight: bold;">var</span> express <span style="color: #333333;">=</span> require(<span style="background-color: #fff0f0;">'express'</span>); <span style="color: #008800; font-weight: bold;">var</span> app <span style="color: #333333;">=</span> express();
For OAuth2 in Salesforce, we will be using salesforce-oauth2 node library:
<span style="color: #008800; font-weight: bold;">var</span> oauth2 <span style="color: #333333;">=</span> require(<span style="background-color: #fff0f0;">'salesforce-oauth2'</span>);
For oauth2 authentication, we need to add 2 URL handlers:
In this handler, express redirects to Salesforce authentication URL.
app.get(<span style="background-color: #fff0f0;">'/oauth2/auth'</span>, <span style="color: #008800; font-weight: bold;">function</span> (req, res) { <span style="color: #008800; font-weight: bold;">var</span> uri <span style="color: #333333;">=</span> oauth2.getAuthorizationUrl({ redirect_uri<span style="color: #333333;">:</span> callbackURL, client_id<span style="color: #333333;">:</span> consumerKey, scope<span style="color: #333333;">:</span> <span style="background-color: #fff0f0;">'api'</span> }); <span style="color: #008800; font-weight: bold;">return</span> 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(<span style="background-color: #fff0f0;">"CallbackController"</span>, <span style="color: #008800; font-weight: bold;">function</span>(LoginService, $location, localStorageService, $window, $http) { <span style="color: #008800; font-weight: bold;">var</span> absUrl <span style="color: #333333;">=</span> $location.absUrl(); <span style="color: #008800; font-weight: bold;">var</span> regExp <span style="color: #333333;">=</span> <span style="color: #000000; background-color: #fff0ff;">/\?code=(\S+)#/g</span>; <span style="color: #008800; font-weight: bold;">var</span> res <span style="color: #333333;">=</span> regExp.exec(absUrl); <span style="color: #008800; font-weight: bold;">if</span>(res) { <span style="color: #008800; font-weight: bold;">var</span> code <span style="color: #333333;">=</span> res[<span style="color: #0000dd; font-weight: bold;">1</span>] LoginService.getAccessToken({code<span style="color: #333333;">:</span> code}) .then(<span style="color: #008800; font-weight: bold;">function</span>(response) { <span style="color: #888888;">//handle success</span> }, <span style="color: #008800; font-weight: bold;">function</span>(response) { $location.path(<span style="background-color: #fff0f0;">'/login'</span>) }) } <span style="color: #008800; font-weight: bold;">else</span> { console.error(<span style="background-color: #fff0f0;">'Error during log in'</span>); } })
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(<span style="background-color: #fff0f0;">'/RestTest/oauth/callback'</span>, <span style="color: #008800; font-weight: bold;">function</span>(req, res) { <span style="color: #008800; font-weight: bold;">var</span> code <span style="color: #333333;">=</span> req.body.code; code <span style="color: #333333;">=</span> code.replace(<span style="color: #000000; background-color: #fff0ff;">/%3D/g</span>, <span style="background-color: #fff0f0;">'='</span>) oauth2.authenticate({ redirect_uri<span style="color: #333333;">:</span> callbackURL, client_id<span style="color: #333333;">:</span> consumerKey, client_secret<span style="color: #333333;">:</span> secretKey, code<span style="color: #333333;">:</span> code }, <span style="color: #008800; font-weight: bold;">function</span>(error, payload) { <span style="color: #008800; font-weight: bold;">if</span>(error) { res.send(error) } <span style="color: #008800; font-weight: bold;">else</span> { 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(<span style="background-color: #fff0f0;">'instanceUrl'</span>, response.data.instance_url); localStorageService.set(<span style="background-color: #fff0f0;">'accessToken'</span>, response.data.access_token); $http.defaults.headers.common.Authorization <span style="color: #333333;">=</span> <span style="background-color: #fff0f0;">'Bearer '</span> <span style="color: #333333;">+</span> 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(<span style="color: #008800; font-weight: bold;">function</span>(localStorageService, $http) { <span style="color: #008800; font-weight: bold;">let</span> accessToken<span style="color: #333333;">=</span><span style="background-color: #fff0f0;">'Bearer '</span><span style="color: #333333;">+</span> localStorageService.get(<span style="background-color: #fff0f0;">'accessToken'</span>); $http.defaults.headers.common.Authorization <span style="color: #333333;">=</span> 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(<span style="background-color: #fff0f0;">'SalesforceResponseObserver'</span>, ($location, $q)<span style="color: #333333;">=></span>{ <span style="color: #008800; font-weight: bold;">return</span> { <span style="background-color: #fff0f0;">'responseError'</span><span style="color: #333333;">:</span> <span style="color: #008800; font-weight: bold;">function</span>(errorResponse) { <span style="color: #008800; font-weight: bold;">let</span> status <span style="color: #333333;">=</span> errorResponse.statu <span style="color: #008800; font-weight: bold;">if</span>(status <span style="color: #333333;">==</span> <span style="background-color: #fff0f0;">'-1'</span> <span style="color: #333333;">||</span> status <span style="color: #333333;">==</span> <span style="color: #0000dd; font-weight: bold;">401</span> <span style="color: #333333;">||</span> status <span style="color: #333333;">==</span> <span style="color: #0000dd; font-weight: bold;">403</span>) { $location.path(<span style="background-color: #fff0f0;">'/login'</span>) } <span style="color: #008800; font-weight: bold;">return</span> $q.reject(errorResponse) } } })
and add to interceptors array:
.config(($httpProvider) <span style="color: #333333;">=></span> { $httpProvider.interceptors.push(<span style="background-color: #fff0f0;">'SalesforceResponseObserver'</span>); })
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(<span style="background-color: #fff0f0;">'SalesforceRecordService'</span>, ($http, localStorageService) <span style="color: #333333;">=></span> { <span style="color: #008800; font-weight: bold;">var</span> instanceUrl <span style="color: #333333;">=</span> localStorageService.get(<span style="background-color: #fff0f0;">'instanceUrl'</span>); <span style="color: #008800; font-weight: bold;">return</span> { getRecords<span style="color: #333333;">:</span> <span style="color: #008800; font-weight: bold;">function</span>(recordName) { <span style="color: #008800; font-weight: bold;">var</span> url <span style="color: #333333;">=</span> instanceUrl <span style="color: #333333;">+</span> <span style="background-color: #fff0f0;">'/services/data/v20.0/sobjects/'</span> <span style="color: #333333;">+</span> recordName; <span style="color: #008800; font-weight: bold;">return</span> $http.get(url); }, getListAvailableSobjects<span style="color: #333333;">:</span> <span style="color: #008800; font-weight: bold;">function</span>() { <span style="color: #008800; font-weight: bold;">var</span> url <span style="color: #333333;">=</span> instanceUrl <span style="color: #333333;">+</span> <span style="background-color: #fff0f0;">'/services/data/v39.0/sobjects'</span>; <span style="color: #008800; font-weight: bold;">return</span> $http.get(url); }, } })
And in controller, we use this service:
.controller(<span style="background-color: #fff0f0;">'RecordsController'</span>,($scope, SalesforceRecordService, $routeParams) <span style="color: #333333;">=></span> { <span style="color: #008800; font-weight: bold;">var</span> recordName <span style="color: #333333;">=</span> <span style="background-color: #fff0f0;">'Account'</span>; SalesforceRecordService.getRecords(recordName).then((response) <span style="color: #333333;">=></span> { $scope.data <span style="color: #333333;">=</span> JSON.stringify(response.data) }, (response) <span style="color: #333333;">=></span> { console.error(<span style="background-color: #fff0f0;">'Erorr in SalesforceRecordService => '</span>, response); }) })
and here's the simple HTML template:
<span style="color: #007700;"><span style="color: #007700;"><p< span=""> <span style="color: #0000cc;">ng-bind=</span><span style="background-color: #fff0f0;">"data"</span><span style="color: #007700;">></span></p<></span></span>
And the result will be something like this:
You can find all the code here:
sfexpress