Skip to Content
Technical Articles

CAP: Consume External Service – Part 1

The openSAP course Building Applications with SAP Cloud Application Programming Model has just concluded. Overall, the course is a good overview of what the CAP Model has to offer. It has some hands-on demo that are beginner-friendly. However, to be able to get a deeper understanding of the moving parts and configuration aspect of the CDS framework, one has to work on their own use case and do their own development.

So that’s what I did, and I decided to focus on consuming external OData Service using the Cloud Foundry Destination Service. Apparently, there’s quite a number of pitfalls I stumbled into, hence, I’m sharing my solution on this blog.

Special thanks to David Kunz for helping in troubleshooting and guiding me on my issue!

 

 

Prerequisites


  • SAP Business Application Studio / Visual Studio Code
  • SAP Cloud Platform Account – Cloud Foundry Environment

 

Create the Node.js Application


If you have been working with OData Services, then you probably already familiar with the NorthWind OData Service. This is a good starting point for learning how to use/consume a public OData service, and in this scenario, I will be using the OData V4 NorthWind service.

https://services.odata.org/Experimental/OData/OData.svc/

We will start to create a Node.js application from scratch using Cloud Application Programming Model. I’m using Visual Studio Code as my local development IDE, but you can use SAP Business Application Studio and still achieve the same results.

  • 1. Initialize your project using the CDS CLI tool
> cds init

The project was generated with the skeleton of the project. As with any Node.js modules, the package.json file is that starting point of your Node.js application, the rest of the folders are empty, and in our case, we will only need to use the srv folder.

 

  • 2. Get the service document or EDMX file of our OData Service by using the $metadata tag. And then save the xml data as NorthWind.edmx.
https://services.odata.org/Experimental/OData/OData.svc/$metadata

  • 3. Use the cds import command to import the .edmx file into your project
> cds import NorthWind.edmx

There are a few things that happened during the import.

  • The NorthWind.edmx file was imported to folder srv/external

  • The NorthWind.csn file was generated based on the edmx file and was also inside the folder srv/external. CSN stands for Core Schema Notation, and this is the Schema Definition that is understood and used by the CDS framework.

  • The package.json file was updated with the cds.requires configuration for the imported external service called NorthWind.

 

  • 4. Create a catalog-service.cds file to define our Catalog Service
using {NorthWind as external} from './external/NorthWind.csn';

service CatalogService {

    @readonly
    entity Products as projection on external.Products {
        key ID, Name, Description, ReleaseDate, DiscontinuedDate, Rating, Price
    };

}

Here, we need to import the NorthWind.csn definition to be used as a projection for our Products entity.

 

  • 5. Create a custom handler for our Catalog Service and implement the READ event of Products entity. Name the file catalog-service.js.
const cds = require('@sap/cds');

module.exports = cds.service.impl(async function() {
	const { Products } = this.entities;
	const service = await cds.connect.to('NorthWind');
	this.on('READ', Products, request => {
		return service.tx(request).run(request.query);
	});
});

It is important to name the handler file the same as the .cds counterpart as the CDS framework expects this.

 

Test the application using Mock Data


  • 1. Mock the data for our catalog service to be able to test our application locally and in isolation. Create a folder data under the external folder. And in this data folder, create a new file called NorthWind-Products.csv with contents below:
ID;Name;Description;ReleaseDate;DiscontinuedDate;Rating;Price
0;Bread;Whole grain bread;1992-01-01T00:00:00Z;;4;2.5
1;Milk;Low fat milk;1995-10-01T00:00:00Z;;3;3.5

It is important that the file follows the proper naming convention i.e. <name of the service>-<name of the entity>.csv as this is expected by the framework.

 

  • 2. We are now ready to test our application. So start it up using the cds command:
> cds watch

If you encounter an error log after executing the above command, then that’s just about right. It seems that when the framework generated the CSN file, there were some violations in the entity dependency and this is being highlighted in the terminal as an error.

I’m not quite sure why the framework would generate an erroneous .csn file, but it’s not the end of the world, the errors are quite easy to fix if you are familiar with OData definitions. Just follow along the error description as it describes which file has an error (NorthWind.csn) and which line (162), what artifact (NorthWind.Person) and what is the missing entity (NorthWind.Customer).

I’ve already gone through the pain of fixing it one-by-one and here is the fixed file: NorthWind.csn

Once you have fixed and saved the NorthWind.csn file, the node application is automatically restarted by the nodemon process, this is because we have started the application using cds watch command.

The application would start normally, as you can see in the screenshot below:

  • 3. Open your application in your browser using below URL:
http://localhost:4004

And click on the Products entity for the catalog service:

Here you will see the data that we mock for our Products entity. You can go back to the main page of the service and browse the external service entities, but there won’t be any data available because we only created mock data for Products entity.

 

Test the application using Real Data


  • 1. Modify the configuration in your package.json file to add the credentials.url property. Don’t forget to save the changes.
	"cds": {
		"requires": {
			"NorthWind": {
				"kind": "odata",
				"model": "srv/external/NorthWind",
				"credentials": {
					"url": "https://services.odata.org/Experimental/OData/OData.svc"
				}
			}
		}
	}
  • 2. If you still have the cds watch running, then head back to your browser to test the application.

As you can see, the starting page of the node service is only showing catalog path. This is an expected behaviour since you are no longer running on mock data. The addition of the credentials.url property makes the application use the real external service’s data and the north-wind path is now gone. This is the exact situation once you deploy the node application to cloud foundry environment.

Now let’s click on the Products entity to view the data.

This time the Products entity is displaying the actual list of products as if you run the original NorthWind OData Service.

You can verify this by using below URL in a separate browser:

https://services.odata.org/Experimental/OData/OData.svc/Products

 

Closing


Now you know how to consume an external OData Service using the CAP Model. We are able to test our Node.js application using Mock data and also test it with real data just by doing a simple configuration change.

On my next blog, I will show the deployment to cloud foundry and setting up the destination using the destination service.

UPDATE:

Here’s the part 2 –> Consume External Service – Part 2

 

~~~~~~~~~~~~~~~~

Appreciate it if you have any comments, suggestions, or questions. Cheers!~

14 Comments
You must be Logged on to comment or reply to a post.
      • Hi Jhodel,

         

        I am cleaning the project. meanwhile, I am sending you package.json file.

        {
        “name”: “HelloWorld”,
        “version”: “1.0.0”,
        “description”: “A simple CAP project.”,
        “repository”: “<Add your repository here>”,
        “license”: “UNLICENSED”,
        “private”: true,
        “dependencies”: {
        “@sap/cds”: “^3”,
        “@sap/hana-client”: “^2.5.86”,
        “@sap/hdi-deploy”: “^3.11.5”,
        “express”: “^4”
        },
        “scripts”: {
        “start”: “npx cds run”
        },
        “devDependencies”: {
        “sqlite3”: “^5.0.0”
        },
        “cds”: {
        “requires”: {
        “NorthWind”: {
        “kind”: “odata”,
        “model”: “srv/external/NorthWind”,
        “credentials”: {
        “url”: “https://services.odata.org/Experimental/OData/OData.svc”
        }
        }
        },
        “build”: {
        “target”: “gen”,
        “tasks”: [
        {
        “src”: “srv”,
        “for”: “node-cf”,
        “options”: {
        “model”: [
        “db”,
        “srv”,
        “app”
        ]
        }
        },
        {
        “src”: “db”,
        “for”: “hana”,
        “options”: {
        “model”: [
        “db”,
        “srv”,
        “app”
        ]
        }
        }
        ]
        }
        }
        }

        Regards,

        Lalit

        • Hi Lalit Goyal ,

          I must say that your issue got me puzzled for quite a while. The issue is due to missing “s” in exports:

          const cds = require('@sap/cds');
          
          module.exports = cds.service.impl(async function() { // <--- right here
          	const { Products } = this.entities;
          
          	const service = await cds.connect.to('NorthWind');
          	this.on('READ', Products, request => {
          		return service.tx(request).run(request.query);
          	});
          });

          The hint was that, when entering in debug mode, it will never execute any lines of code in this file because the function was never exported. And apparently, cds framework won’t complain and assume that you are running using mock data.

  • Jhodel Cailan

    Hi Jhodel,

    first of all thank you very much for this wonderful tutorial.

    I have just a short question: Does the Fiori Preview work for you, if

    you want to display the northwind products in the table? I get data when clicking the service

    url, but not within the fiori preview – although I annotated correctly.

    Only, when using CSV it works…

     

    BR

    Rufat