Skip to Content
Author's profile photo Sven Huberti

SAP API Management – Develop and manage API-first enterprise microservices with SAP Cloud Platform and API Management – part 1

When I started coding, I was writing ASP pages that I hosted on IIS. It was amazing: from static HTML pages to dynamic applications running in a browser! After that I moved to Java POJOs and ended my coding career with JSFs… That time seems far away, and since then a LOT has been happening (including me stopping the coding).

Today, we are talking about agile development, sprints, continuous integration. etc., which have changed the organizational way of working. But this change would not have been possible without new technologies and concepts such as micro services, NodeJS, Bootstrap, RESTful APIs, PaaS, Maven, Git, etc..

So when I decided to create a little REST API implementation, needless to say I had to get my stuff together first. Obviously, because I am acquainted with middleware and especially API management, I wanted to start designing my API first. To do so, I decided to use OpenAPI (natively supported by SAP API Management) as description language. The opensource tools available around OpenAPI easily lets you generate server stubs; in SAP API Management, I can choose between NodeJS, Jax-RS or Spring. I decided to go for NodeJS, in order to deploy my API in the SAP Cloud Platform based on Cloud Foundry. Indeed, from there, you can use a Cloud Foundry Service Broker to simply and efficiently protect your API through SAP API Management, as described by Shruthi here.

As you can see, SAP supports the full lifecycle of an API, from the design to the monitoring, implementation and runtime included.

In this blog, I will focus especially on designing, implementing and securing the API in order to get you started with your own API.

 

To start with, here is a quick overview of the parts we’ll cover (in red).
As an API Developer, we’ll use the SAP API Management API Designer to generate the OpenAPI defintion of our API. Then we’ll generate NodeJS server stubs for it, adapt them to our environment, and deploy the application on the SAP Cloud Platform Cloud Foundry PaaS. Eventually, we’ll protect that API with an API proxy from SAP API Management.

In this blog, I am assuming that you are familiar with SAP API Management and the SAP Cloud Platform to seom extend.

Let’s get started…

Design your API with OpenAPI (f.k.a. Swagger)

First of all, let’s open the API Designer. This is done through SAP API Management (which you can use from SAP Cloud Platform trial). Click on “Develop” in the menu, and click on the “Create in API Designer” button under the “API” tab.

In the API Designer, you can create or import an OpenAPI file or convert OData or RAML into OpenAPI.

Because the focus of this blog is not on OpenAPI, let’s keep things simple. Copy the API defintion from below into your editor.

swagger: "2.0"
info:
  version: "1.0.0"
  title: "PartnerBanking Information API"
  x-targetEndpoint: "https://trial.apim1.hanatrial.ondemand.com"
host: "trial.apim1.hanatrial.ondemand.com:443"
basePath: "/v1/PartnerBankingInformation"
schemes:
- "http"
- "https"
consumes:
- "application/json"
produces:
- "application/json"
paths:
  /partnerBankingInformation:
    get:
      description: "\nReturns all the banking information of the partner specified\
        \ through its name or ID\n\nExample of use:\n\n/partnerBankingInformation?apikey=123&partnerId=123456\n"
      operationId: "getPartnerBankingInformation"
      parameters:
      - name: "apiKey"
        in: "query"
        description: "The API key provided by the Developer Portal upon API subscription"
        required: true
        type: "string"
      - name: "partnerName"
        in: "query"
        description: "The name of the partner"
        required: false
        type: "string"
      - name: "partnerId"
        in: "query"
        description: "The partner id number"
        required: false
        type: "string"
      responses:
        200:
          description: "Success"
          schema:
            $ref: "#/definitions/partnerBankingInformation"
        default:
          description: "Error"
          schema:
            $ref: "#/definitions/ErrorResponse"
      x-swagger-router-controller: "Default"
    post:
      description: "Creates a partner banking information."
      operationId: "postPartnerBankingInformation"
      parameters:
      - name: "apiKey"
        in: "query"
        description: "The API key provided by the Developer Portal upon API subscription"
        required: true
        type: "string"
      - name: "partnerID"
        in: "query"
        description: "The partner id number"
        required: true
        type: "string"
      - name: "partnerName"
        in: "query"
        description: "The partner name"
        required: true
        type: "string"
      - name: "partnerPhoneNumber"
        in: "query"
        description: "The partner phone number"
        required: true
        type: "string"
      - name: "partnerEmail"
        in: "query"
        description: "The partner email"
        required: true
        type: "string"
      - name: "partnerAddress"
        in: "query"
        description: "The partner address"
        required: true
        type: "string"
      - name: "partnerIBAN"
        in: "query"
        description: "The partner IBAN"
        required: true
        type: "string"
      responses:
        200:
          description: "Success"
          schema:
            $ref: "#/definitions/partnerBankingInformation"
        default:
          description: "Error"
          schema:
            $ref: "#/definitions/ErrorResponse"
      x-swagger-router-controller: "Default"
    put:
      description: "Updates a partner banking information."
      operationId: "putPartnerBankingInformation"
      parameters:
      - name: "apiKey"
        in: "query"
        description: "The API key provided by the Developer Portal upon API subscription"
        required: true
        type: "string"
      - name: "partnerID"
        in: "query"
        description: "The partner id number"
        required: true
        type: "string"
      - name: "partnerName"
        in: "query"
        description: "The partner name"
        required: true
        type: "string"
      - name: "partnerPhoneNumber"
        in: "query"
        description: "The partner phone number"
        required: true
        type: "string"
      - name: "partnerEmail"
        in: "query"
        description: "The partner email"
        required: true
        type: "string"
      - name: "partnerAddress"
        in: "query"
        description: "The partner address"
        required: true
        type: "string"
      - name: "partnerIBAN"
        in: "query"
        description: "The partner IBAN"
        required: true
        type: "string"
      responses:
        200:
          description: "Success"
          schema:
            $ref: "#/definitions/partnerBankingInformation"
        default:
          description: "Error"
          schema:
            $ref: "#/definitions/ErrorResponse"
      x-swagger-router-controller: "Default"
    x-swagger-router-controller: "PartnerBankingInformation"
definitions:
  partnerBankingInformation:
    properties:
      partnerId:
        type: "string"
        description: "Identifier of the partner"
      partnerName:
        type: "string"
        description: "Name of the partner"
      partnerPhoneNumber:
        type: "string"
        description: "Phone number of the partner"
      partnerEmail:
        type: "string"
        description: "Email of the partner"
      partnerAddress:
        type: "string"
        description: "Full address including street, number, ZIP code and city name"
      partnerIBAN:
        type: "string"
        description: "IBAN of partner"
  ErrorResponse:
    required:
    - "message"
    properties:
      message:
        type: "string"

As you can see from the defintion file, it represents an API for getting and setting Partner Banking Information (GET/POST/PUT). Note that the API could have been simplified by passing the payload as body instead of params, but again: that is not the focus here. 😉

Here is the result in the API Designer:

Since this definition file is fine for our use case, we will not make any change to it at this point in time. However, feel free to play around with it in order to understand OpenAPI and the API Designer better.

Now that the API defintion is fine, let’s generate the NodeJS stubs from the API Designer.

To do so, simply click on “Generate Server / NodeJS”.

Change the name of the application to something meaningful like “PartnerBankingInformation” and click on “Generate Project”.

The project is now being generated for you, packaged in a ZIP file and provided as download through your browser.

Take that file and unzip it in your work directory.

Adapt your API

Within your work directory, you will now find the NodeJS project files that were generated for you.

Within these files, we’ll do a couple of changes. We will adapt the package.json file and the index.js file. Note that the controllers/DefaultService.js file contains the actual implementation of the operations defined in the Swagger defintion. This is what you may want to change when implementing your own business logic.

Also note that we are still refining the generation of the files mentioned above, hence the adjustments on package.json and index.js may not be necesary in the future.

 

Package.json

The package.json file is basically describing your application so that NodeJS knows metadata (version, application name, …) and dependencies to properly deploy the app.

Here we need to add one thing: the Cloud Foundry dependency.

Edit your package.json file to match the following one:

{
  "name": "partnerbanking-information-api",
  "version": "1.0.0",
  "description": "No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen)",
  "main": "index.js",
  "scripts": {
	"prestart": "npm install",
	"start": "node index.js"
  },
  "keywords": [
    "swagger"
  ],
  "license": "Unlicense",
  "private": true,
  "dependencies": {
    "cfenv": "^1.0.0",
    "connect": "^3.2.0",
    "js-yaml": "^3.3.0",
    "swagger-tools": "0.10.1"
  }
}

Note that we added the “cfenv” dependency in the dependencies section.

"cfenv": "^1.0.0",

 

index.js

Within the index.js file, which is called whenever our application is started, we need to make sure to bind the application to the right port.

For now, the port is hard-coded but we want this to be working in the Cloud Foundry environment as well.

So lets change the server port to something more dynamic:

Change the line #8 “var serverPort = 443” with the following code snippet:

//var serverPort = 443;

//Modified
var cfenv = require("cfenv");
var appEnv = cfenv.getAppEnv();
var serverPort = appEnv.port || 443;

As you can see, we are now getting variables from CF and hence we assign the CF port to our application. By default we still use 443.

 

Test your application

If you have been curious, you may have looked at the DefaultService.js file already. If not, here is what it looks like:

So you can see that if you were to call the API now, the call would be routed to the getPartnerBankingInformation method, sending you back some JSON example data.

In a real-life scenario, this is where you would implement your own business logic, which is out-of-scope for this blog.

In the part 2 of my blog, I will explain how to upload the application to your SAP Cloud Platform cloud foundry environment, and then protect it by using SAP API Management.

 

Assigned Tags

      1 Comment
      You must be Logged on to comment or reply to a post.
      Author's profile photo Ramos de Oliveira Sergio Alberto Salomao
      Ramos de Oliveira Sergio Alberto Salomao

      Good morning Dear.

      How are you?

      I would like to ask a question regarding which SAP Cloud Platform service I should use to serve my client.

      Here is the scenario that my client has today.

      Legacy Workflow System (Non SAP) -> Sensedia Platform API -> ERP Legacy System (Non SAP). This is a synchronous scenario.

      The Legacy Workflow System calls an API that is implemented in the API Plataform Sensedia software. This API has the functionality of searching data, through a view, into an oracle database and returns the result to the Legacy Workflow system.

      The customer's goal is to replace API Platform Sensedia software with SAP HCI-CPI. From what I've been researching, this scenario can be done through SAP API Managemente and API Designer without the need to use SAP HCI-CPI. Is this my understanding correct?

      My other question is: How do I use an API developed in JAVA or Node.js, responsible for searching data in an oracle database, with SAP HCI-CPI?

      Could you help me please?

      Regards,

      Sérgio Salomão