Technology Blogs by Members
Explore a vibrant mix of technical expertise, industry insights, and tech buzz in member blogs covering SAP products, technology, and events. Get in the mix!
Showing results for 
Search instead for 
Did you mean: 
Active Contributor
This is a follow-up blog post to my previous one called Keep the Core Clean with RAP Model.

Like in the previous blog post, this is not about convincing you to adopt a clean core policy and why it is important, but instead, this blog post is more about asking the question: Can we keep the Core Clean with CAP Model?

I would suggest reading the introduction of the previous blog post to understand the motivation for keeping the core clean.

With the concept and motivation already out of the way (and you are on board with it), let’s get into the action of applying it, and for this blog post, we will be using the CAP framework for NodeJS.

Just note that this blog post will cover the Side-by-Side Extensibility scenario which will make use of an OData API service.




The Goal

The goal is to create a custom Fiori application on BTP (side-by-side extensibility scenario) using the CAP framework while leveraging the standard OData API service. The remote OData service is capable of providing full OData entity operations namely: create, read, update, and delete — and for the CAP service, we will need to add custom handlers to implement the call to those external service operations. And since CAP is supporting the Fiori Elements framework, we will add the draft feature into the mix.



The Demo Project

The demo project is available at the GitHub repository:


External Service: ProductAPI

For the sake of simplicity, I created my own external service using the same CAP project so that it will be easy to test the app that consumes this external service. Also, this is the same CAP-based external service that I used in my previous blog post Keep the Core Clean with RAP Model.

Service Model: srv > ProductAPI.cds
using {md} from '../db/schema';

service ProductAPI {
entity Products as projection on md.Products;

I used this service to generate the metadata.xml file that will be used for cds import later.


CAP Project

In order to consume the external service, we need to first do the cds import to generate the service model definition for the external service. You can refer to this blog post if you're not familiar yet with how this is done: CAP: Consume External Service – Part 1.

The configuration in the package.json file shown below will make use of mocked external service.

Configuration: package.json > cds > requires:
"product_external": {
"kind": "odata-v2",
"model": "srv/external/products"

Again, this is just for the simplicity of testing. You would typically want to use an SAP standard external OData Service (i.e. S/4HANA) for this case.

Service Model: srv > ProductRemoteService.cds
using {product_external as external} from './external/products';

@impl: './handler/ProductRemoteService.js'
service ProductRemoteService {

entity Products as projection on external.Products;


Service Handler: srv > handler > ProductRemoteService.js
module.exports = async service => {
const external = await"product_external");
const { Products } = service.entities;

service.on(["CREATE", "READ", "UPDATE", "DELETE"], Products, async request => {
try {
return await;
} catch (error) {
const errors = error.reason.response.body.error.innererror.errordetails;
errors.forEach(({ code, message, target }) => {
request.error({ code, message: message.value || message, target });
return error;


The service model is using a custom handler for handling CRUD operations.


Fiori Element App

There are two Fiori Element apps that are generated using SAP Fiori Tools:

  • Manage Products (External Service) for OData V4

  • Manage Products (External Service) for OData V2

Both apps can be launched from the sandbox launchpad and can be found on app/products-remote and app/products-remote-v2 folders respectively.

Note that though we have two apps, the annotations are added one-time only and can be found in app/products-remote-v2 folder.



Like any other CAP project, start testing by running the cds watch command in the terminal, then run the respective app as already enumerated above.


OData V4 Service

The app to test will be Manage Products (External Service) OData V4.

As in the previous blog post, CAP also doesn't support draft scenarios for handling external services. When pushing my luck, I was greeted with the error message below:

So technically, CAP's service API doesn't support draft-generated properties out-of-the-box. Theoretically, we can add some custom logic to handle the difference between services with draft properties and external service that doesn't have these properties. It may be tricky, but in theory, this should be possible. However, this topic is beyond the scope of this blog post, so we will not dig into this.

So now we don't have any choice but to turn off the @odata.draft.enabled annotation. And then test the app:

As you can see, just like in the RAP sample project, we also don't have the create and edit buttons, this is again, because Fiori Elements for OData V4 doesn't support non-draft scenarios. Once again, we are heading into the not-so-fortunate case of downgrading to OData V2 service.


OData V2 Service

The app to test will be Manage Products (External Service) OData V2.

The app is capable of performing all the create, read, update, and delete operations. See the below screenshots as proof that the functionality is ready for you to use:




So going back to the question: Can we keep the Core Clean with CAP Model? The answer is not so straightforward, YES and NO answers.

If you’re expecting that we can use CAP, OData V4, and Fiori Elements — the answer is NO, this is because CAP doesn't support handling draft-generated properties out-of-the-box but maybe if you put a little more effort you can handle it yourself. But if you’re willing to settle for the OData V2 version (non-draft Fiori Elements) then you got a YES as an answer.

If I were to draw a conclusion now between the experience of keeping the core clean with RAP vs CAP, I would say that we reached a stalemate here because both of the frameworks are having the same capabilities and limitations. However, CAP has a distinct advantage over RAP, that being said, CAP didn't block us completely from implementing the gap of supporting the draft functionality for external service.

In the end, I hit the limitations of the frameworks I tried so far. However, there's one possibility that I purposely avoided discussing in this blog post, because I already reserved that topic for the next blog post: Keep the Core Clean with Rizing CDSX!





Appreciate it if you have any comments, suggestions, or questions. Cheers!~
Labels in this area