Skip to Content
Technical Articles

SAP Cloud Platform Backend service: Tutorial [18]: API: called: from: external: node.js

This blog is part of a series of tutorials explaining the usage of SAP Cloud Platform Backend service in detail.

Quicklinks:
Code

You can use Backend service to easily creat…..
Oh, no, thanks, I have enough APIs, guess I’ll search for another tutorial seri…
Hey, stop, just tell what you’re looking for
Guidance how to write node.js app to call APIs from exter…
But that’s what I was about to to say…
Ah, cool, just say

Assuming you have created enough APIs in the Backend service cockpit
Assuming you have learned how to access and test your APIs from external REST client
Maybe now you wish to take a next step and programmatically call the API from outside the cloud.
Could be necessary e.g. for automatically testing your APIS with local test script.
Such script might be running in a Jenkins build server, whatever.
And I wish example code
A wish which I fully understand
And also I wish to support such wish.
So here it comes:
Example node.js code to call Backend service API

Note:
I think that you should be able to follow this blog, even if you’re completely new to node.js

Prerequisites

Although in this tutorial we just write a small basic node.js application, there are quite a few prerequisites and configuration steps to be done.

Cloud:
You’ve created an API in Backend service. (see here)
You’ve gone through the description of manually calling API from REST client tool
In order to achieve that, there’s a prerequisite: configure security in Cloud account
You might also go through the OAuth 2 blog to understand a bit more about the background

node.js:
You might want to have a look at this section, where I’ve already described the necessary steps to be prepared to run node
Note:
In this present blog, no SAP-specific node modules are required, so you don’t need to configure the npm registry

Prepare it

Before writing the code, it makes sense to collect some user-specific data to be entered later:

Backend service API

You need the URL of the OData service which you want to call.
Important to mention:
To view the URL, we usually open the URL in the cockpit of Backend service.
But this URL is not really the one which we can use to call it externally.
So we need to slightly modify the URL:
It has to be prefixed with backend-service-api instead of
<yourSubaccount>-backend-service
See my example below

Cloud environment

Assuming, you have prepared your cloud account with creating a service instance of xsuaa and creating a service key
Now you can go there and take a note of the required properties
Furthermore, take a note of your OData service which you want to call, i.e. the API created on Backend service

These are my example values:

API https://backend-service-api.<…>/odatav2/DEFAULT/PRODUCTSERVICE;v=1/Products
clientid
sb-xsuaaName!t12345
secret
VZz5this37dJ6eTVmightbeKhfGf56lw=
Uri https://<subaccount>.authentication.eu10.hana.ondemand.com/oauth/token
user myDummyUser@myMail.com
pwd theUsual123

Local environment

Prepare your local environment for node.js development:
Experienced users may skip this section

All we need is a dedicated folder
Create a folder
e.g. C:\tmp_bsnode
Open command shell and navigate to that folder

Now we can install the required dependencies.
Run the following command:

npm install client-oauth2

like here:

This command installs a node module which helps us dealing with the OAuth2 flow
Similarly execute the following 2 commands

npm install request

and

npm install request-promise

Result:
In our file system, the folder node_modules has been created in our working directory.
It contains all required dependencies

Note:
I think it is easier for the moment to live without package.json file
Reason is that it isn’t possible to attach files or archives to the blog.
So I think it is easier to follow the blog, if there’s only one file

Code it

Now it is time for our application code.
Inside our working folder, we create a javascript file
e.g. app.js

Note:
Before you go ahead:
The next step is to paste the code and afterwards to run it BUT:
BUT there’s a BUT:
Don’t forget it:
B U T before running the app, you need to read a NOTE.
Don’t forget it
Otherwise it won’t work.
And you’ll complain.
BUT: I’ve warned you early enough

OK
OK, open the file with an editor of your choice, e.g. Notepad

Note:
A good choice is Visual Studio Code
Yes, I installed it when following the CDS modelling blog
Fantastic

Paste the following code:

'use strict';

const oauthClient = require('client-oauth2');
const request = require('request-promise');

// OData service, created in Backend service
const SERVICE_URL = 'https://backend-service-api.cfapps.eu10.hana.ondemand.com/odatav2/DEFAULT/PRODUCTSERVICE;v=1/Products';

// helper method which uses client-oauth2 library to get access token from XSUAA 
const _getAccessToken = function() {
    return new Promise((resolve, reject) => {
        const oautClient = new oauthClient({
            accessTokenUri: 'https://bssubaccount.authentication.eu10.hana.ondemand.com/oauth/token',
            clientId: 'sb-XsuaaForAuthCode!t13020',
            clientSecret: 'VZz539437dJ6eTVmigKhfGG89lw=',
            scopes: []
        });

        oautClient.owner.getToken('yourUser@yourMail.com', 'theUsual123')
        .then(result => {
            resolve({accessToken: result.accessToken});
        })
        .catch(error => {
            reject({message: 'Error: failed to get access token.', error: error}); 
        });
    });
}

 // helper method for calling Backend service API, using the previously fetched token
 const _doQUERY = function (serviceUrl, accessToken){
    return new Promise (function(resolve, reject){
        const options = {
            url: serviceUrl,
            resolveWithFullResponse: true ,
            headers: { 
                Authorization: 'Bearer ' + accessToken, 
                Accept : 'application/json'
            }
        };
        
        request(options)
        .then(response => {
            if(response && response.statusCode == 200){
                resolve({responseBody: response.body});
            }
            reject({ message: 'Error while calling OData service'});
        })  
        .catch(error => {
            reject({ message: 'Error occurred while calling OData service', error: error });
        });
    });
 };

// The script calling Backend service API
console.log('Calling Backend service-API ...');
_getAccessToken()
.then(result => {
    console.log('Successfully fetched OAuth access token: ' +  result.accessToken.substring(0,16));
    return _doQUERY(SERVICE_URL, result.accessToken);
})
.then(result => {
    console.log('Successfully called OData service. Response body: ' + result.responseBody.substring(0,111));
    // do anything like:  JSON.parse(result.responseBody).d.results;
})
.catch(error => {
    console.log(error.message + ' Reason: ' + error.error);
});

Node:
Now it comes, the node…
What?

Note:
Sorry, a typo, I don’t mean a node, I mean a note

The Note:
You have to change the code:
Make sure to enter your personal data, which you prepared in the preparation section above
Line 7: Enter the URL of your personal Backend service API, it should be a collection
Line 13: Enter the URL which comes from your xsuaa instance, it should end with /oauth/token
Line 14: Enter the clientid of your xsuaa instance
Line 15: Enter the clientsecret of your xsuaa instance
Line 19: Enter your personal cloud user
Line 19: Enter the password of your personal cloud user

Note:
That’s it, now you can run it

Run it

To run the node.js application, change to your command prompt and make sure you’re in the same working directory as usual
Type the following command and press enter

node app.js

As a result, the application is executed.
It prints a few success messages.
What success, what messages?
No?
It prints an error message: Error: failed to get access token ???
I cannot believe it….Did you read my note…
Which note?
aaaaarghhh…
See here how to adapt the sample code:

OK
After adapting the code to your personal data, you’ll get the 2 success messages.
Hopefully…

Note:
The following section explains a little bit what we’ve been doing

Understand it

Note:
First of all, my apologies for the code which probably could be better, but I’m not a pro and I’ve tried to keep it short
Yes, it looks awful
OK
What is done in that ugly code?

The application does 2 things
1. use the node module client-oauth2 to do the OAuth 2 flow in order to get the access token
2. use the node module request-promise to call  the OData service (with token)

Both are encapsulated in helper methods (line 10 and line 29)
Both helper methods are called subsequently (with Promises), starting in the “Script” section at line 54

Note:
For calling OData services, there are other node packages, but today I wanted to be simple.

What is interesting in the code?
We use the “Resource Owner Password Credentials” grant type of OAuth 2, therefore we use the “owner” to fetch the token.

oautClient.owner.getToken('user', 'pwd')

Like that, it is quite easy to get the required token
Once we have it, we use it as header in the request which we send to the OData service:

Authorization: 'Bearer ' + accessToken,

We’re also setting another header:

Accept : 'application/json'

This is done to ensure that the OData service sends its response payload in JSON format.
Like that, it is easy to process further

However, today we don’t process it further…
Oh, already the end?
Yes,but I’ll be back
Hasta la vista Baby

Troubleshooting

What to do if you happily deploy everything and try testing and get….. Forbidden ???
See this blog for a Troubleshooting guide

Summary

In this blog we’ve learned how to call a Backend service API from an external node application.
Honestly, there’s nothing special in this blog.
We’ve

used 2 standard node modules to do 2 standard calls:
Get OAuth token
Call OData service

However, I believe it is always good to have sample code, to be ready to easily use Backend service:
With SAP Cloud Platform Backend service you can easily create APIs and with the help of this blog you can easily consume it with external node.js application

You’ve made it

Glad that I’ve finally concluded my sentence

Advertisements

Little node.js tutorial series:
Call API from external node application: this blog
Call API from node app, deployed to Cloud Foundry, everything done manually
Call API from node app inside CF using XSUAA service binding
Call API from node app inside CF using XSUAA login screen
Call API from node app inside CF using Destination service binding

Call API from external app, means local REST client

Preparation info:
Configure xsuaa for usage from external app/tool
Configure BETA subaccount to enable xsuaa configuration
OAuth intro:
Explaining OAuth 2 mechanism for Backend service, security mechanism
Explaining OAuth “Authorization Code” grant type, also supported by Backend service

All Blogs and tutorials: overview of blog series

Links

https://nodejs.org/en/

client-oauth2

request-promise

 

7 Comments
You must be Logged on to comment or reply to a post.
  • Hello Carlos Roggan ,

    One more issue I am facing while passing the token and accessing the service, I get this below response.

    <html>
    <head>
    <link rel="shortcut icon" href="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" /><script>
    document.cookie="fragmentAfterLogin="+encodeURIComponent(location.hash)+";
    path=/";
    location="https://testola.authentication.eu10.hana.ondemand.com/oauth/authorize?response_type=code&client_id=sb-Backend-service!t6131&redirect_uri=https%3A%2F%2Ftestola-backend-service.cfapps.eu10.hana.ondemand.com%2Flogin%2Fcallback"
    </script>
    </head>
    </html>

    It is happening both from rest client(postman) and from the node as well. It works till the token is fetched after that step the above response will come.

    BR,
    Mahesh