Skip to main content

Execute an OData GET request using the SAP Cloud SDK for JavaScript

Overview

In this part of the tutorial, you will do the following:

  • Extend your starter NestJS application by adding a custom route.
  • Call the business partner service of SAP S/4HANA Cloud using the SAP Cloud SDK for JavaScript.
  • Manage destinations centrally during development (optional).

Add a Custom Route

Initially, the app contains a basic controller with a single route that returns the string "Hello World!". You will add another route for business-partner that will list all available business partners.

Create a new controller by executing the command:

nest g controller business-partner

This will create a folder business-partner in the src/ directory containing the controller business-partner.controller.ts.

import { Controller } from '@nestjs/common';

@Controller('business-partner')
export class BusinessPartnerController {}

The @Controller(business-partner) decorator marks the class BusinessPartnerController as a controller (i.e. a thing that handles requests). Next, add a method getBusinessPartners() with a @Get('') decorator. This will tell Nest to create a handler for this endpoint for HTTP requests.

import { Controller, Get } from '@nestjs/common';

@Controller('business-partner')
export class BusinessPartnerController {
@Get()
getBusinessPartners() {
return 'You will implement this in a minute.';
}
}

Notice that you did not add any path information in the decorator. Nest will map GET /business-partner requests to this handler.

For the controller to work, you need to include it in the controllers array within the @Module() decorator in app.module.ts. The generate command updates the app.module.ts automatically and looks like this:

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { BusinessPartnerController } from './business-partner/business-partner.controller';

@Module({
imports: [],
controllers: [AppController, BusinessPartnerController],
providers: [AppService]
})
export class AppModule {}

If you've started your application with the following command in the previous tutorial, it should detect the change and restart automatically.

npm run start:dev

If you've terminated your application, you can restart it by running the start command again. Now, calling http://localhost:8080/business-partner should return the placeholder string.

Generate Service Entities

The SAP Cloud SDK for JavaScript requires client libraries to make calls to OData services. In this tutorial, you generate the client library for the business partner service using @sap-cloud-sdk/generator. More details of the OData generator can be found in Generate an OData client for JavaScript.

Steps:

  1. Install the @sap-cloud-sdk/generator package as a local dependency.

    npm install -D @sap-cloud-sdk/generator
  2. Create a folder service-specifications at the root of the project.

  3. Download the EDMX file for the business partner service in the SAP Business Accelerator Hub.

  4. Copy the API_BUSINESS_PARTNER.edmx file into the service-specifications folder.

  5. Create a options-per-service.json file in the service-specifications folder with the following content:

    {
    "service-specifications/API_BUSINESS_PARTNER.edmx": {
    "directoryName": "business-partner-service",
    "basePath": "/sap/opu/odata/sap/API_BUSINESS_PARTNER",
    "packageName": "business-partner-service"
    }
    }
  6. Generate the BusinessPartner service.

    npx generate-odata-client --input service-specifications --outputDir services --optionsPerService service-specifications/options-per-service.json

The generated client library is in services/business-partner-service.

note

You can find a list of services exposed by SAP S/4HANA Cloud in the SAP Business Accelerator Hub. Use @sap-cloud-sdk/generator to generate OData client libraries you need, as mentioned above.

Execute an OData Request

Next, you will create a service that will be responsible for fetching the business partners. To create a service class business-partner.service.ts, execute:

nest g service business-partner

This creates a basic class inside src/business-partner folder.

import { Injectable } from '@nestjs/common';

@Injectable()
export class BusinessPartnerService {}

The service is also registered in the provider array within the @Module() decorator in app.module.ts.

@Module({
imports: [],
controllers: [AppController, BusinessPartnerController],
providers: [AppService, BusinessPartnerService],
})

To import the service function and entity exported by the client library, add the following line to the top of the service class.

import {
businessPartnerService,
BusinessPartner
} from '../../services/business-partner-service';

Create a function getAllBusinessPartners(). Get the API for the entity you want to make a call to in your application. In this tutorial, you are using the businessPartnerApi of the business partner service. Unpack the API object from the service function using JavaScript Object Destructuring.

Depending on the target system you are connecting to, the destination configuration can vary:

async getAllBusinessPartners(): Promise<BusinessPartner[]> {
const { businessPartnerApi } = businessPartnerService();
return await businessPartnerApi.requestBuilder().getAll().execute({
url: 'http://localhost:3000/',
});
}

As network requests are asynchronous by nature, the return value of this function is a Promise to a list of business partners (Promise<BusinessPartner[]>).

Now that you have a service class to retrieve business partners, use it in the BusinessPartnerController. The BusinessPartnerService is injected through the class constructor:

import { Controller, Get, HttpException } from '@nestjs/common';
import { BusinessPartner } from '../../services/business-partner-service';
import { BusinessPartnerService } from './business-partner.service';

@Controller('business-partner')
export class BusinessPartnerController {
constructor(private businessPartnerService: BusinessPartnerService) {}

@Get()
async getBusinessPartners(): Promise<BusinessPartner[]> {
return await this.businessPartnerService
.getAllBusinessPartners()
.catch(error => {
throw new HttpException(
`Failed to get business partners - ${error.message}`,
500
);
});
}
}

Nest will handle the returned Promise automatically. Add a catch() handler to specify how errors are handled (otherwise it would only show an internal server error when something goes wrong). Reload the http://localhost:8080/business-partner URL to retrieve a list of business partners.

Congratulations, you just made your first call with the SAP Cloud SDK!

Manage Destinations Centrally (Optional)

To avoid repeating your destination configuration for every request execution, you can set a destinations environment variable to manage your destinations. In Node.js applications, it is common to use a .env file to maintain such environment variables for a given project. Create a .env file in the root directory of your project and define the destinations environment variable as follows:

destinations = [
{
name: '<DESTINATIONNAME>',
url: '<URL to your system>',
username: '<USERNAME>',
password: '<PASSWORD>'
}
];
note

Every environment variable in the .env file has to be defined on one line. You can add more destinations to the array.

This is what it would look like for the mock server:

destinations = [{ name: 'MockServer', url: 'http://localhost:3000' }];
caution

Please do not use this approach in production and also include the .env file in your .gitignore list, so that it is not checked in.

Now that you have defined the destinations, make sure that they are available in the process. For this, use the config package provided by nest.js. You can install it with the following command:

npm install @nestjs/config

To load the environment variables defined in the .env file, add the ConfigModule provided by the config package to the application's @Module definition. Open app.module.ts and update it with the following code:

import { ConfigModule } from '@nestjs/config';

@Module({
imports: [ConfigModule.forRoot()],
controllers: [AppController, BusinessPartnerController],
providers: [AppService, BusinessPartnerService],
})

ConfigModule is imported from the config package and added to the module's imports. If no arguments are passed to the forRoot() method, the .env file has to be located in the project root. For details on the possible configuration see the nest documentation. To reference a destination in the request execution, replace the URL with a destinationName - MockServer in this example:

async getAllBusinessPartners(): Promise<BusinessPartner[]> {
const { businessPartnerApi } = businessPartnerService();
return await businessPartnerApi.requestBuilder().getAll().execute({
destinationName: 'MockServer'
});
}

Final Code Review

In this tutorial, you added a new custom route to your application. Using the SAP Cloud SDK, you executed an OData request to fetch a list of business partners. You configured the destinations environment variable using a .env file.

Here are the code files discussed on this page, if you are using the mock server:

import { Controller, Get, HttpException } from '@nestjs/common';
import { BusinessPartner } from '../../services/business-partner-service';
import { BusinessPartnerService } from './business-partner.service';

@Controller('business-partner')
export class BusinessPartnerController {
constructor(private businessPartnerService: BusinessPartnerService) {}

@Get()
async getBusinessPartners(): Promise<BusinessPartner[]> {
return await this.businessPartnerService
.getAllBusinessPartners()
.catch(error => {
throw new HttpException(
`Failed to get business partners - ${error.message}`,
500
);
});
}
}