Technology Blogs by SAP
Learn how to extend and personalize SAP applications. Follow the SAP technology blog for insights into SAP BTP, ABAP, SAP Analytics Cloud, SAP HANA, and more.
cancel
Showing results for 
Search instead for 
Did you mean: 
dhem
Active Participant
3,318
As you may have seen in our announcement blog post, we have released the SAP S/4HANA Cloud SDK for JavaScript (beta)! In time for TechEd Las Vegas we bring the benefits of the SAP S/4HANA Cloud SDK also to anyone developing in JavaScript or TypeScript.



This blog post is part of a bigger series about extending SAP S/4HANA using the SAP S/4HANA Cloud SDK. You can find the full series here.

Goal of this blog post


The goal of this blog post is to enable you to build your own SAP S/4HANA side-by-side extensions application in JavaScript using the SAP S/4HANA Cloud SDK for JavaScript. In this tutorial, we will cover the following steps:

  • Prerequisites

  • Downloading the JS SDK.

  • Setting up an application using Express.js

  • Installing the SDK to your application.

  • Building an example service that retrieves data from the SAP S/4HANA system.

  • Deploying your application to SAP Cloud Platform Cloud Foundry


Please note: the SAP S/4HANA Cloud SDK is available as beta. It is not meant for productive use, and SAP does not make any guarantee about releasing a productive version. Any API and functionality may change without notice.

Prerequisites


Access to the beta requires you to be an SAP customer and sign a Test and Evaluation Agreement (TEA). We have described the process in the announcement blog post.
In order to complete this tutorial, you will need to install Node.js on your machine. If you have not used Node.js before, you can either grab the latest executable from the official Node.js website, or install it using your package manager of choice.

Furthermore, you should have access to SAP Cloud Platform Cloud Foundry and an SAP S/4HANA Cloud system. If you do not have access to Cloud Foundry, you can create a trial account. Additionally, we recommend using the command line tools for CloudFoundry (cf CLI). Installation instructions can be found in the CloudFoundry documentation.

In case you don't have access to an SAP S/4HANA Cloud system, for the scope of this tutorial you can also use our mock server, that provides an exemplary OData business partner service.

One final disclaimer: While the SDK is fully compatible with pure JavaScript, it is written in and designed for TypeScript. TypeScript is a superset of ECMAScript 6, adding an optional but powerful type system to JavaScript. If you are not familiar with TypeScript, we highly recommend checking it out!

Download the SDK


Since the SDK is a beta version, you currently cannot get it from the NPM registry. Instead, after signing in the TEA as described in the separate blog post, visit SAP's Service Marketplace and download the SDK there. Save the SDK to a directory of your choice. The downloaded file will be a .tgz, so go ahead and unzip the archive. That's all for downloading! We will come back to the SDK after setting up our application.

Setting up an Application using Express.js


Now we will setup our application. We use Express.js to build a backend application exposing RESTful APIs. In this section, we will set up the plain application as you would do with any Node.js application, still without any integration to SAP S/4HANA.

Start by creating a directory example-app. In this directory, create a package.json file with the following content:
{
"name": "example-app",
"version": "1.0.0",
"description": "Example application using the SAP S/4HANA Cloud SDK for JavaScript.",
"scripts": {
"start": "ts-node src/server.ts"
},
"dependencies": {
"express": "^4.16.3"
},
"devDependencies": {
"ts-node": "^7.0.1",
"typescript": "^3.0.3"
}
}

The package.json acts as a project descriptor used by npm, the Node.js package manager.

Proceed by entering the example-app directory in your terminal and call npm install. This will install the necessary dependencies for our application.

Additionally, since this is a TypeScript project, create another file called tsconfig.json with the following content:
{
"typeAcquisition": {
"enable": true
}
}

Now create a directory src inside your example-app and add two files: First, server.ts, that will contain the logic for starting the webserver.
import app from './application';

const port = 8080;

app.listen(port, () => {
console.log('Express server listening on port ' + port);
});

Secondly, application.ts, that contains the logic and routes of our application.
import * as bodyParser from 'body-parser';
import * as express from 'express';
import { Request, Response } from 'express';

class App {
public app: express.Application;

constructor() {
this.app = express();
this.config();
this.routes();
}

private config(): void {
this.app.use(bodyParser.json());
this.app.use(bodyParser.urlencoded({ extended: false }));
}

private routes(): void {
const router = express.Router();

router.get('/', (req: Request, res: Response) => {
res.status(200).send('Hello, World!');
});

this.app.use('/', router);
}
}

export default new App().app;

The most important part in this file is the following, where we define our first API in the routes function:
router.get('/', (req: Request, res: Response) => {
res.status(200).send('Hello, World!');
});

This instructs the router to respond to HTTP GET requests (router.get) on the root path of the application ('/', the first parameter) by calling the function provided as the second parameter. In this function, we simply send a response with status code 200 and 'Hello, World!' as body.

In order to start your server, return to your terminal and execute npm start. This will in turn execute the command we have defined for start in the scripts section of our package.json.
"scripts": {
"start": "ts-node src/server.ts"
}

After calling npm start, you should see the following output in your terminal: Express server listening on port 8080. Now you can visit http://localhost:8080 in your browser, and you will be greeted with Hello, World! in response!

To stop the server, press Ctrl+C or Cmd+C.

Adding the SDK to your project


Now it's finally time to add the SDK to the project. In a previous step, we downloaded the SDK as a .tgz archive and unpacked it, which gave us a directory called s4sdk. Now we need to copy this directory into the example-app directory. Then, we can add the SDK as a dependency to our application by adding two entries to the dependencies section of our package.json so that this section looks as follows (don't forget to add a comma behind the second line):
"dependencies": {
"express": "^4.16.3",
"s4sdk-core": "file:s4sdk/s4sdk-core",
"s4sdk-vdm": "file:s4sdk/s4sdk-vdm"
}

The file: prefix instructs npm to install the dependencies from your machine instead of fetching them from the npm registry.

Your project directory should now look as follows:



Call npm install again to install the SDK to your project. In a development environment such as Visual Studio Code, this will also make available the types of the SAP S/4HANA Cloud SDK for code completion.

Now that we can use the SDK, let's write an API endpoint that fetches business partners from your SAP S/4HANA Cloud system.

To do so, we will add another route in the routes function in application.ts.
import { BusinessPartner } from 's4sdk-vdm/business-partner-service';

router.get('/businesspartners', (req: Request, res: Response) => {
BusinessPartner.requestBuilder()
.getAll()
.top(100)
.execute()
.then((businessPartners: BusinessPartner[]) => {
res.status(200).send(businessPartners);
});
});

When using a modern editor like Visual Studio Code, the correct imports should be automatically suggested to you. If this fails for whatever reason, add the following line to the import declarations:
import { BusinessPartner } from 's4sdk-vdm/business-partner-service';

Let's go through the function step by step:

First, we define a new route that matches on GET requests on /businesspartners. Then, we use the SDK's Virtual Data Model (VDM) to retrieve business partners from our SAP S/4HANA Cloud system. The VDM, originally introduced in the SAP S/4HANA Cloud SDK for Java, allows you to query OData services exposed by your SAP S/4HANA Cloud system in a type-safe, fluent and explorative way. More details can be found in this blog post introducing the VDM in the SDK for Java.

We start by creating a request builder on our desired entity, in this case by calling BusinessPartner.requestBuilder(). This in turn will offer you a function for each operation that you can perform on the respective entity. In the case of business partners, the possible operations are getAll()getByKey()create() and update(). We choose getAll(), since we want to retrieve a list of business partners. Now we can choose from the variety of options to further refine our query, such as select() and filter(). However, for now we keep it simple by only calling top(100) to restrict the query to the first 100 results. Finally, we call execute(). The SDK takes care of the low level infrastructure code of the request.

By default, any call to an SAP S/4HANA system performed using the VDM will be done asynchronously and returns a promise. We handle the promise by calling then() and providing it with a function that handles the query result. As you can see from the signature of the callback function, promises returned by the VDM are automatically typed with the respective entity, in this case we get an array of business partners (BusinessPartner[]). Now we can simply send a response with status code 200 and the business partners retrieved from the SAP S/4HANA system as response body.

Running the Application Locally


Before deploying the application to Cloud Foundry, let's test the integration locally first. To do so, we need to supply our destination configuration to designate the SAP S/4HANA system to connect to. This can be achieved by running the following command in your command line, or the equivalent for setting environment variables in your shell (the below is for the Windows command prompt):
set destinations=[{"name":"ErpQueryEndpoint", "url": "https://mys4hana.com", "username": "myuser", "password":"mypw"}]

Make sure to replace the values for url, username and password with the respective values matching your SAP S/4HANA Cloud system.

If you restart the server using npm start and navigate to http://localhost:8080/businesspartners, you should see a list of business partners retrieved from your SAP S/4HANA Cloud system.

Deploying the Application to Cloud Foundry


In order to deploy the application on Cloud Foundry, we need to provide a deployment descriptor in the form of a manifest.yml file. Add this file to the root directory of your application.
---
applications:

- name: example-app
memory: 256M
random-route: true
buildpacks:
- nodejs_buildpack
command: npm start
env:
destinations: >
[
{
"name": "ErpQueryEndpoint",
"url": "https://mys4hana.com",
"username": "<USERNAME>",
"password": "<PASSWORD>"
}
]

Pay attention to the env section. Here, we provide the destination settings for the SAP S/4HANA system we want to connect to. Simply substitute the value for each entry with the respective values for your SAP S/4HANA system.

Additionally, we need to perform one more addition to our app's package.json.
"engines": {
"node": "10.14.1"
}

This tells npm which version of Node.js to use as runtime environment. Omitting this from the package.json leads to CloudFoundry defaulting to an older version of node. However, the VDM relies on some features only present in newer version of Node.js. Additionally, as in any project, it is good practice to specifiy the version of the runtime environment to protect yourself from errors introduced in unwanted version changes down the line.

The resulting package.json should look as follows:
{
"name": "example-app",
"version": "1.0.0",
"description": "Example application using the SAP S/4HANA Cloud SDK for JavaScript.",
"scripts": {
"start": "ts-node src/server.ts"
},
"dependencies": {
"express": "^4.16.3",
"s4sdk-core": "file:s4sdk/s4sdk-core",
"s4sdk-vdm": "file:s4sdk/s4sdk-vdm"
},
"devDependencies": {
"ts-node": "^7.0.1",
"typescript": "^3.0.3"
},
"engines": {
"node": "10.14.1"
}
}

Finally, you can push the application by executing cf push on your command line in the root directory of the application. This uses the Cloud Foundry command line interface (CLI), whose installation is described in this blog post. The cf CLI will automatically pick up the manifest.yml. At the end of the deployment, cf CLI will print the URL under which you can access your application. If you now visit the /businesspartners route of your application at this URL, you should see a list of business partners that have been retrieved from your SAP S/4HANA system! This requires that the URL of the system you connect to is accessible from Cloud Foundry.

This concludes our tutorial.

Give us Feedback!


Are you excited about the SAP S/4HANA Cloud SDK for JavaScript? Are there features that you would love to see in the future? Or did you have problems completing the tutorial? In any case, we would love to hear your feedback in the comments to this blog post! In case of technical questions, you can also reach out to us on StackOverflow using the tags s4sdk and javascript.

Going even further


If you have completed the tutorial up to this point, you are equipped with the basics of extending your SAP S/4HANA Cloud system with a Node.js application. However, so far we have only explored a small part of the SDK's capabilities. While the SDK for JavaScript is a Beta release and, thus, only supports a subset of the features of the SAP S/4HANA Cloud SDK for Java, we do provide the same capabilities in terms of the Java virtual data model for integrating with your SAP S/4HANA system!

Complex Queries using the VDM


Let's take a look at a more complex query:
import { BusinessPartner, Customer } from 's4sdk-vdm/business-partner-service';
import { and, or } from 's4sdk-core';

BusinessPartner.requestBuilder()
.getAll()
.select(
BusinessPartner.FIRST_NAME,
BusinessPartner.LAST_NAME,
BusinessPartner.TO_CUSTOMER.select(Customer.CUSTOMER_FULL_NAME)
)
.filter(
or(
BusinessPartner.BUSINESS_PARTNER_CATEGORY.equals('1'),
and(
BusinessPartner.FIRST_NAME.equals('Foo'),
BusinessPartner.TO_CUSTOMER.filter(Customer.CUSTOMER_NAME.notEquals('bar'))
)
)
)
.execute();

Again, we want to retrieve a list of business partners. This time, however, we added a select and a filter clause to our query. This highlights two of the VDM's advantages over building queries by hand: type-safety and discoverability.

As you can see in the filter clause, you can simply get an overview over which fields are present on the business partner by typing BusinessPartner. in your editor. The autocompletion of modern editors, such as Visual Studio Code, will then provide a list of suggestions, which you can navigate to find the fields you need, without having to refer to the service's metadata. Furthermore, we ensure that each query you build is type-safe. This means that if you try to e.g. select a field that does not exist on the respective entity, your editor will report a type mismatch, effectively preventing your from writing incorrect queries.

In this example, we restrict our selection to the business partner's first and last name. Additionally, we add Customer, a related entity, to your selection, from which we only use the full name.

The same fields can also be used for filtering. As you can see, you can build arbitrarily complex filter clauses using the and() and or() functions. Each field also provides a function for each filter operation that can be used on the respective field. Additionally, we again make sure that the values you provide in the filter clause match the type of the respective field. If, for example, you'd try to filter a string-typed field by a number (e.g. BusinessPartner.FIRST_NAME.equals(5)), a type mismatch will be reported.

Destinations and Authentication


In the examples so far, we have simply called execute() in our queries. If no parameter is provided, execute() will by default try to load a destination named "ErpQueryEndpoint" from your application's environment variables (the one we configured in the manifest.yml, remember?). You can of course provide more destinations. If you want to use a specific destination for your OData queries, you do so by passing the destination's name to the execute call, like this:
execute('MyCustomDestination')

Additionally, we provide the option to pass a destination configuration directly to the VDM.
execute({
url: "https://mys4hana.com/",
username: "MyUser",
password: "MyPassword"
})

Finally, if you want to make use of OAuth2 or other means instead of basic authentication, you can also set the Authorization header directly.
BusinessPartner.requestBuilder()
...
.withCustomHeaders({
Authorization: "Bearer <EncodedJWT>"
})
.execute()

If you provide an Authorization header using this mechanism, the VDM will ignore the username and password otherwise provided by any destination configuration.
7 Comments