Prototyping with SAP CAP
Deep dive workshop to learn more about:
- Prototyping with SAP CAP
- How to use uniform APIs to consume local or remote services
- Out-of-the-box support for SAP Fiori elements UX
- Events and Messaging using SAP Event Mesh
- Essential steps to deploy a CAP application to SAP BTP
FullScreen mode
There's built-in support for fullscreen mode. Press F on your keyboard to view the presentation in fullscreen mode. Once in fullscreen mode, press the ESC key to exit.
UDINA Trial Sub Account
In connection with the UNIORG SAP BTP workshop, UNIORG offers a time-limited trial sub-account that can be used for an initial introduction to development with SAP BTP.
The sub-account can be connected to the SAP Cloud Connector to securely access on-premise systems from the SAP BTP Cloud.
Plan Restrictions
- Developer Access
- 5 named S-User
How to Generate a new S-User ID - Authorizations
- SAP Business Application Studio Developer
- Cloud Connectivity and Destination Admin
- SAP BTP SubAccount Viewer
- 5 named S-User
- Cloud Foundry Quota Plan
- Runtime Memory: 512 MByte
- Routes: 4
- Service Instances: 8
- App Instances: 4
- No paid service plans
- Business Application Studio – Free Plan
- A user can only have up to 2 dev spaces.
- A user can only have 1 dev space in the RUNNING state at a time.
- The maximum size limit of a dev space is 4 GB.
Setup your DEV Space
Launch My DEV Spaces

Create CAP DEV Space
Choose Full Stack Cloud Application application and press

Grow As You Go
This section contains best practices for speeding up development by jump-starting projects with zero setup, eliminating boilerplate code, and parallelizing work.

Jumpstarting Project
The following steps show creating new projects with a SAP BAS Template, but it is also possible by using the CDS Development Kit.
Start with template wizzard
Choose CAP Project

Enter CAP Project Details

Project Storyboard

Create External Data Model
Option 1 (BAS Admin) : Add destination with Service Center
Having the role BAS Admin, the data model can be created using the Storyboard.

The Service Center opens up and then add a new SAP System for your udina-<customer>-trial
subaccount.

Option 2 (Destination Admin) : Create destination with SAP BTP Cockpit
If you are assigned to the role BAS Admin, but you are allowed to create BTP Destinations, you can manually add a relevant service destination.
Open the BTP Cockpit section Connectivity -> Destinations and create a New Destination with the following settings:
Destination Configuration | Value |
---|---|
Name | Northwind |
Type | HTTP |
Description | oData V2 Demo Service |
URL | https://services.odata.org/V2/Northwind/Northwind.svc/ |
Proxy Type | Internet |
Authentication | NoAuthentication |
Additional Properties | Value |
---|---|
HTML5.DynamicDestination | true |
WebIDEAdditionalData | full_url |
WebIDEEnabled | true |
WebIDEUsage | odata_gen |
Full Path is mandatory
BAS currently only supports service destinations with full path
Add External Data Model
Use Service Action Add External Data Model to CAP Project to add service to CAP project.


Check, if BAS created the relevant package.json
service section:
"cds": {
"requires": {
"Northwind": {
"kind": "odata-v2",
"model": "srv/external/Northwind",
"[production]": {
"credentials": {
"destination": "Northwind"
}
}
}
}
}
Use real destination for development by adding default-env.json
Deprecated
The usage of default-env.json
is generally deprecated and cds bind should be used instead, but it needs a deployed service. To overcome this chicken-egg problem (testing before deployment), we will use this approach for demo purposes.
{
"VCAP_SERVICES": {},
"destinations": [
{
"name": "Northwind",
"url": "https://services.odata.org/V2/Northwind/Northwind.svc/"
}
]
}
Remove production switch from package.json
Just delete the marked lines from existing code and use context menu Format Document
to fix text indent:
"cds": {
"requires": {
"Northwind": {
"kind": "odata-v2",
"model": "srv/external/Northwind",
"[production]": {
"credentials": {
"destination": "Northwind"
}
}
}
}
}
Restart CDS service in terminal to load destination environment
cds watch
Test external service
https://localhost:4004/northwind-sample/Customers
Add missing dependencies for external oData V2 support
Missing dependency error @sap-cloud-sdk/http-client
Since CAP is a glue framework and by default uses oData v4, you need to add missing dependency @sap-cloud-sdk/http-client
to add oData v2 support for external service consumption.
npm i @sap-cloud-sdk/http-client
Re-Test external service
https://localhost:4004/northwind-sample/Customers
Mashing up with Remote Services
Combining local and remote services using Mashing.
Enhance cat-service.cds
using my.bookshop as my from '../db/data-model';
using Northwind from './external/Northwind.cds';
service CatalogService {
@readonly entity Books as projection on my.Books;
@readonly entity Customers as projection on Northwind.Customers;
}
Create new cat-service.js
service implementation
const cds = require('@sap/cds');
module.exports = cds.service.impl(async function () {
const Northwind = await cds.connect.to('Northwind');
this.on('READ', 'Customers', req => {
//console.log("read external customers", req.query);
return Northwind.run(req.query);
});
});
REST Client
The REST Client for Visual Studio Code allows you to send HTTP request and view the response in Visual Studio Code directly (similarly to Postman).
Create a ./test
folder and add a file service.http
with content.
GET http://localhost:4004/catalog/Books
###
GET http://localhost:4004/catalog/Customers
### Example POST service call with CSRF token
@host = https://my-sap-system:443
@path = /sap/opu/odata/sap/api_purchasereq_process_srv/
# @name fetchToken
HEAD {{host}}{{path}}
Authorization: Basic {{$dotenv USERNAME}} {{$dotenv PASSWORD}}
X-CSRF-Token: Fetch
###
# @name createPurchaseRequisition
POST {{host}}{{path}}A_PurchaseRequisitionHeader
Authorization: Basic {{$dotenv USERNAME}} {{$dotenv PASSWORD}}
X-CSRF-Token: {{fetchToken.response.headers.x-csrf-token}}
Content-Type: application/json
{
data: {}
}
Externalize sensitive data to ./test/.env
and exclude it from git inside .gitignore
USERNAME=<Username>
PASSWORD=<Password>
Enhance Bookshop Data Model
Enriching the book shop similarly to tutorial Build a Business Application Using CAP for Node.js, which focuses on local CAP development with VS Code.
Enhance ./db/data-model.cds
namespace my.bookshop;
using {
Country,
managed
} from '@sap/cds/common';
entity Books {
key ID : Integer;
title : localized String;
author : Association to Authors;
stock : Integer;
}
entity Authors {
key ID : Integer;
name : String;
books : Association to many Books
on books.author = $self;
}
entity Orders : managed {
key ID : UUID;
book : Association to Books;
country : Country;
amount : Integer;
}
Enhance ./srv/cat-service.cds
using my.bookshop as my from '../db/data-model';
using Northwind from './external/Northwind.cds';
service CatalogService {
entity Books as select from my.Books;
entity Authors as select from my.Authors;
entity Orders as select from my.Orders;
@readonly
entity Customers as projection on Northwind.Customers;
}
Add initial data ./db/data/my.bookshop-Authors.csv
Use CAP DATA MODELS AND SERVICES view to create sample,
ID;name
101;Emily Brontë
107;Charlote Brontë
150;Edgar Allen Poe
170;Richard Carpenter
Replace initial data ./db/data/my.bookshop-Books.csv
with
ID;title;author_ID;stock
201;Wuthering Heights;101;12
207;Jane Eyre;107;11
251;The Raven;150;333
252;Eleonora;150;555
271;Catweazle;170;22
Test expand
https://localhost:4004/catalog/Books?$expand=author
Localize data
Copy ./db/data/my.bookshop-Books.csv
to ./db/data/my.bookshop-Books_texts.csv
ID;locale;title
201;de;DE Wuthering Height
201;en;EN Wuthering Height
Test locales
https://localhost:4004/catalog/Books?$expand=texts
Add Virtual Element
Virtual Elements allow to implement calculated chracteristics. This keyword indicates that this element isn’t added to persistent artifacts.
Add Books virtual element stockState
inside data-model.cds
entity Books {
key ID : Integer;
title : localized String;
author : Association to Authors;
stock : Integer;
virtual stockState : String enum {
Error = 'Error';
Information = 'Information';
None = 'None';
Success = 'Success';
Warning = 'Warning';
};
}
Add custom logic ./srv/cat-service.js
this.after('READ', 'Books', each => {
// Add some discount for overstocked books
if (each.stock > 111) each.title += ' -- 11% discount!'
// Calculate Virtual stock state to highlight critical stocks
each.stockState = (each.stock < 20) ? 'Warning' : 'None'
})
Deploy to SAP BTP
Following the SAP CAP Cookbook Deploy to Cloud Foundry, the CAP command-line tool helps to prepare the project for the deployment to SAP BTP.
How SAP CAP project folders deploy to SAP BTP
Using XSUAA-Based Authentication
Configure your app for XSUAA-based authentication:
cds add xsuaa --for production
The following files have been generated:
File | Description |
---|---|
manifest.yml | Cloud Foundry App Manifest |
mta.yaml | MTA Development Descriptor |
xs-security.json | Application Security Descriptor |
Using MTA-Based Deployment
We’ll be using the Cloud MTA Build Tool to execute the deployment:
cds add mta
The mta.yaml
has been updated by adding a module for the CAP service, that is referencing the XSUAA resource.
Restrict the memory usage of your CAP service (defaults to 1024M) by adding a memory parameter to the CAP module:
modules
- name: workshop-srv
type: nodejs
path: gen/srv
parameters:
buildpack: nodejs_buildpack
memory: 256M
Using a local SQLite database
Instead of using a production-ready cloud database, our prototyping will use the sqlite3 in-memory database instead.
Move sqlite from devDependencies to dependencies (prod):
npm i sqlite3 -P
Edit mta.yaml to auto build the prototype database
build-parameters:
before-all:
- builder: custom
commands:
- npx -p @sap/cds-dk cds build --production
- npx -p @sap/cds-dk cds deploy --to sqlite:gen/srv/srv/db.sqlite --no-save
Add database dependency to package.json
{
"[production]": {
"db": {
"kind": "sqlite",
"credentials": {
"database": "srv/db.sqlite"
}
}
}
}
Using Custom App Router as Gateway
For scenarios without SAP Fiori Launchpad, the app router needs to be deployed along with your application:
cds add approuter --for production
The following files have been generated:
File | Description |
---|---|
app/default-env.json | Local environment destination injections |
app/package.json | Node.js Approuter metadata |
app/xs-app.json | Application Routing Configuration |
The mta.yaml
has been updated by adding the approuter module.
Add helper scripts to package.json
"scripts": {
"start": "cds run",
"watch": "cds watch",
"build": "rimraf resources mta_archives && mbt build --mtar archive",
"deploy": "cf deploy mta_archives/archive.mtar --retries 1",
"target:test": "cf target -o udina-<customer>-trial -s services"
}
Build Services
Make sure to install formerly added services to node_modules
:
npm i
and trigger the build archive process using the build script.
The following file have been generated:
File | Description |
---|---|
mta_archives/archive.mtar | MTA Archive |
Deploy Services
Login to Cloud Foundry using CTRL SHIFT P and select option:
CF: Login to Cloud Foundry
or alternatively use terminal command line cf login
.
Make sure to use the right deployment target
Check that you are using the cf deployment target udina-<customer>-trial
to avoid deploying into wrong SAP BTP Sub Account!
Test Services
Open the SAP BTP Cockpit for the Sub Account udina-<customer>-trial
and open the Cloud Foundry Space services.
The deployment created two applications:
Name | Description |
---|---|
workshop | The application router |
workshop-srv | The CAP service |
Test the services.
Try to open Northwind Customers entity set, that is returning an error:
Error
Error during request to remote service: Failed to load destination.
Create SAPUI5 freestyle Applicatiom
- Use CTRL SHIFT P and select option
Fiori: Open Application Generator
to start the Template Wizard. - Choose Application Type:
SAPUI5 freestyle
- Select
SAPUI5 Application
- Choose
Next
- Select Data Source:
Use a Local CAP Project
- Choose your CAP project:
workshop
- Chosse OData service:
CatalogService
- Choose
Next
- Choose View name:
View
- Choose
Next
- Choose Module name:
freestyleui5
- Choose Application title:
Freestyle UI5
- Make sure, option
Add deployment configuration to MTA project
is checked - Choose
Next
- Choose Target:
Cloud Foundry
- Choose Destination name:
None
- Choose
Next
Investigate changes
Investigate changes inside mta.yaml
and see new entries:
- modules
- workshop-app-content
- freestyleui5
- resources
- workshop-repo-host
- workshop-destination-service
Implement UI5 view
Replace <content/>
with marked lines inside View.view.xml
<mvc:View controllerName="freestyleui5.controller.View" displayBlock="true"
xmlns="sap.m" xmlns:mvc="sap.ui.core.mvc">
<Page id="page" title="{i18n>title}">
<List id="list" items="{/Books}">
<StandardListItem title="{title}" info="{stock}"/>
</List>
</Page>
</mvc:View>
Exclude app sources from Approuter
Change mta.yaml
to skip app
- name: workshop
type: approuter.nodejs
path: app/
build-parameters:
ignore: ["freestyleui5/"]
Deploy Updates
- Build MTA
- Deploy MTA
Investigate HTML5 Application Repository
cf html5-info
cf html5-list
Bound HTML5 repository runtime to Approuter
Add missing dependencies inside mta.yaml
requires:
- name: workshop-repo-runtime
resources:
- name: workshop-repo-runtime
type: org.cloudfoundry.managed-service
parameters:
service: html5-apps-repo
service-plan: app-runtime
Bound Destination resource to CAP
Add missing dependencies inside mta.yaml
requires:
- name: workshop-destination-service
Add missing HTML5 route to Approuter
Edit app/xs-app.json
:
{
"source": "^/freestyleui5(/.*)",
"target": "/freestyleui5/$1",
"service": "html5-apps-repo-rt",
"authenticationType": "xsuaa"
},
Deploy Updates
- Build MTA
- Deploy MTA
Test services
- Test Freestyle UI5 application
- Test Northwind /catalog/Customers service using destination
Quick fix freestyleui5 app relative service path
Add route to app/freestyleui5/xs-app.json
{
"source": "^/catalog/(.*)$",
"destination": "srv-api",
"authenticationType": "xsuaa",
"csrfProtection": true
},
Deploy Updates
- Build MTA
- Deploy MTA
Create SAP Fiori elements UX
comming soon...
Undeploy your project
Use the generated script undeploy or manually call:
cf undeploy workshop --delete-services --delete-service-keys --delete-service-brokers
OData briefly explained
There are various ways to learn about OData and starting with the native HTTP request may be the most direct one. Read Learning more or the Full Documentation for further details.
Entity Data Model (EDM)
OData Resource | Similar to | Is Described in an Entity Data Model by |
---|---|---|
Collection | Table | Entity Set - A navigation property on an entity type that identifies a collection of entities |
Entry | Structure | Entity Type - Note: Entity Types may be part of a type hierarchy |
Property of an entry | Field | Primitive or Complex Entity Type Property |
Complex Type | Deep Structure | Complex Type |
Link | Relation | A Navigation Property defined on an Entity Type |
Service Operation | BAPI | Function Import |
URI Convention
A URI used by an OData service has up to three significant parts:
- the service root URI
- resource path
- query string options.
Example Calls
Method | Task | URI Segment |
---|---|---|
GET | Metadata | /$metadata |
GET | Request resources | /Books |
GET | Request individual resource | /Books(201) |
GET | Queries | /Books?$select=title,stock&$expand=author&$top=3 |
GET | Invoke function | /Books(201)/ns.rateAuthor() |
POST | Create resource | /Books { ID: 999, title: "abc", stock: 3 } |
Query String Options
Query | Description |
---|---|
$select | Subset of properties to be returned |
$orderby | Specifies an expression for determining what values are used to order the collection of Entries |
$top | Identifies a subset of the Entries in the Collection of Entries |
$skip | Skips N entries in the result of the Collection of Entries |
$filter | Filters a subset of the Entries in the Collection of Entries Operators: Eq,Ne,Gt,Ge,Lt,Le,And,Or,Not Functions: substringof,endswith,startswith,lnegth,indexof,tolower,toupper,trim,concat |
$expand | Returns linked navigation properties inlined with the results |
$format | Returned data format [Atom,Xml,Json,Xlsx(ABAP)...IANA-defined-content-type] |
$count=true | Result includes a count of the number of Entries in the Collection |