While executing tasks we all need the right tools to get the job done. This goes for every profession as it goes for an SAP CAP – developer. We are provided a whole set of different tools thanks to the CAP – team. But making the right choice of tool for the right task, using it in an efficient way is up to us. That’s why I’m writing this blogpost to share a few of my daily CAP – tools and experiences with all of you. Maybe it can help you in your current or future CAP – developments. Feel free to share your favorite tools and experiences or which tools are missing in my toolbox in your opinion.
Note: some features might only be available from certain releases. I tried to add the link to the CAPire documentation to as many features possible, where the version is shown.
Project setup tools
When setting up a new CAP project the following tools help me to quickly have a working POC up and running.
Initializing the project with all basic required files:
cds init my-cap-projects-toolbox
cd my-cap-projects-toolbox
Adding and enabling typescript in the project:
https://cap.cloud.sap/docs/node.js/typescript#enable-typescript-support
cds add typescript
npm i
Add a basic 'db/datamodel.cds' file:
using {cuid} from '@sap/cds/common';
namespace my.cap.toolbox;
entity Books : cuid {
title : String;
description : String;
author : Association to Authors;
}
entity Authors : cuid {
firstName : String;
lastName : String;
fullName : String = firstName || ' ' || lastName;
books : Composition of many Books on books.author = $self;
}
Add a basic service 'srv/ToolboxService.cds' file:
using my.cap.toolbox as toolbox from '../db/schema';
service ToolboxService {
entity Authors as projection on toolbox.Authors;
entity Books as projection on toolbox.Books;
}
Providing initial data to the project:
cds add data --records 5
(--content-type json for JSON format) (--force to override existing data)
https://cap.cloud.sap/docs/tools/cds-cli#data
Adding a .http file to test all CRUD operations on the exposed entities:
cds add http
https://cap.cloud.sap/docs/tools/cds-cli#http
Providing handlers for the exposed entities:
cds add handler
If you encounter issues while adding this because of a different local or global package installation you can also execute the command as such (if correct version installed):
npx cds add handler
https://cap.cloud.sap/docs/tools/cds-cli#handler
Add this point I’m ready to start a full working CAP application, this using the command below:
cds watch
Indeed, we do not have to use the cds-ts or cds-tsx command anymore:
https://cap.cloud.sap/docs/releases/dec24#cds-watch-for-typescript
By executing a CRUD request from the generated 'test/httpToolboxService.http' file we can immediately test our created service:
When performing a follow-up GET-request we see the entry was added to the db.
Since a custom handler was added by executing the cds add handler command we could write custom logic and debug the service. To debug the service (in SAP Business Application Studio) you could simply open a new terminal by pressing the ‘+’-button, choosing he ‘JavaScript Debug Terminal’ option, followed by running the ‘cds watch ‘ command. This will start the application in debug mode (I noticed that the debugger was not triggered in case of ‘cds watch’, when using ‘cds-ts’ watch it did):
Let us add a breakpoint in the “After READ” handler:
Execute the GET command to read all Authors from your .http file and see how to breakpoint in your custom handler is triggered:
To enhance my CAP – projects configuration even more I add the ‘Hybrid-testing’ feature to it. This allows me to test my application with different profiles and to use bound instances of my application.
More information about this feature can be found on the SAP CAPire documentation:
Or in my blogpost called ‘SAP CAP Hybrid Testing’.
Once your application has been deployed and you desperately need to debug the remote application, there is the amazing blog post of Arley ‘Set Up Remote Debugging to Diagnose CAP Applications (Node.js Stack) at Runtime on SAP BTP, Cloud Fo...’ which will help you to get the job done.
Or you can try out the (at this moment in Beta) ‘cds debug’ command (only VS-Code I believe at the moment): https://cap.cloud.sap/docs/releases/nov24#cds-debug
If you would like to see more great TypeScript examples/explanations you can have a look at the Devtoberfest session ‘It's Time for TypeScript (CAP edition)’ of Mike Doyle: https://www.youtube.com/watch?v=5DX6FwR5fss
Interested in more ‘TypeScript’ and ‘cds add’ features?
Then the Devtoberfest session ‘CAP Tools - What's new’ of Christian Georgi is the place to be:
https://www.youtube.com/watch?v=c1s8n-_mR3M
Setting up, configuring, running, testing, and debugging couldn’t be easier thanks to these great CAP – tools and features. No time wasted by providing initial data or finding the correct configuration. The manual tasks were reduced to the minimum so we developers can focus on what we like most, developing a CAP application.
Project development tools
When developing CAP applications, you might be surprised how many solutions there are already out there answering your needs. You do not have to develop every feature yourself, you just need to use them! The only thing you need to do is the visit the SAP CAPire plugins documentation page and use whatever is required for your feature to build.
Further I would like to share some insights and tools which I found extremely handy during my latest CAP developments. The reason I picked out the once mentioned below, is that they were not so obvious at first, or a little harder to find.
Open Types
One would wonder why you need such an annotation. Well, in a certain very dynamic project, we have an application that needs to send a dynamic structure to the backend application. Not being able to send the data in such a structure would force us to create unlogic data models.
Let us use our added .http test file and send a POST request to our CAP application.
We add a non-existing property called “favoriteFood” to the payload and we see we get the error-message:
[error] - 400 > {
code: '400',
message: 'Property "favoriteFood" does not exist in ToolboxService.Authors'
}
We can annotate our entity with the ‘@open’ aspect to allow such behavior:
When we try to send the same payload again we see that in our custom handler the property is available in the req.data:
Note that this property will not be present in the response of the created entry. More information about open types can be found on the SAP CAPire documentation.
cds.odata.X
The cds.odata contains two great functions which allow you to either parse or urlify the input. I’m not entirely sure if this is against best practices to use this OData function from '@sap/cds', feel free to enlighten me about it. 😊
Continuing on this dynamic solution we needed to be able to provide a dynamic query/selection that needs needs be adjustable by admin users. Imagine you have a database table maintained by an admin via a UI application. The UI application allows the user in a friendly way to save the following string to a database, and thus can be maintained by the UI:
Authors?$select=ID,lastName&$expand=Books($select=ID,title ;$orderby=title)
When a consuming user wants to read the Authors entity, the string above could be used to select data from the entity. But executing such an OData query to your database could get tricky without the right tool.
But with the functionality cds.odata.parse this is only 1 line taking care of the job:
cds.odata.parse("Authors?$select=ID,lastName&$expand=books($select=ID,title ;$orderby=title)")
It generates you a SELECT query executable onto your database:
Such a solution could be a done via the simple lines below:
The reversed action is possible as well, but again not 100% sure if this is against the best practice rule.
Entity selections
There are many cases where you will be querying remote services and they might not have naming conventions following yours. That’s why you will be providing aliases for those external entities. In the example below we are consuming the Northwind OData service and we select and alias a few properties:
When implementing a custom handler for this entity it is important to know how you are selecting from this remote service.
Note that in the example below we are selecting from “Orders” being a string:
Which results in a response not considering our alias and selection:
When selecting from the imported and cds-typer generated entity Orders:
We see that our selection and aliases have been applied onto the selection and thus the result:
I would encourage you to always go for the second example of selection unless you have a specific need for it of course. One could call it selection on service and selection on database level if it makes sense to distinguish it that way. Maybe you already noticed, but that means that if we use this string entity prefixed by our service name concatenated by a “.” we can select from different service “.cds” files.
Long story short you could be selecting like this:
await northwindSrv.run(SELECT.from("ServiceName.EntityName"))
Where your ServiceName is the name of your service defined in a service.cds file.
The following screenshot will clarify a lot:
So as the above screenshot shows, you have a ToolboxService.cds (green) which has handlers implemented in a ToolboxService.ts file. This implements a READ handler, which SELECT from a ToolboxService2.cds file (red), that only exposes the ID of Orders with a different alias (blue) and results in only the ID’s returned with the alias “myID” from ToolboxService2. This shows you can on one hand seriously connect, reuse, and implement service in a very extendable way. But that on the other hand if you do not document or have an aligned way of working, you can really create a huge nest of code that is very hard to understand.
Temporal Data
Temporal in a nutshell could be explained as data which is still valid and not expired. This is of course a very basic explanation for the Temporal data concept. More information about can be found on the SAP CAPire documentation.
You can define such an entity using the “temporal” aspect. Your entity could look like this:
While the initial data looks like this:
As you notice there are valid from and valid to properties defined because of the temporal aspect. Exposing the entity and retrieving the data would result in the following response:
In the response above you see that only the “valid” entry is returned.
If you would like to retrieve all the entries, you could implement a custom handler and perform the data selections as follows:
Notice that we here select from “Bikes” as a string and not from the generated cds-typer import. Exactly the opposite of the Northwind example. You could thus expose two entities, one for valid entries and one for all the entries.
Not to forget that you could also use the foreseen temporal query parameters in the SAP CAP documentation. Which is the preferred way to implement your temporal consumption. But now you see how this temporal behaves under the hood. 😊
Conclusion
Via this way I hope to have shared some of the amazing tools offered by CAP, which I use daily to get the job done. There obviously are many more tools, all useful when facing certain challenges. Too many to list, but perhaps less obvious once are now shared in my opinion. Happy to hear if they were a secret to you and if they can be useful in your future projects.
Happy developments!