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.
Jump-Starting Projects
Assuming that you’ve installed @sap/cds-dk, jump-starting a project in CAP is as simple as this:
mkdir demo && cd demo # create a new project folder
cds watch # ask cds to watch for things to come
Convention over Configuration
Following the principles of Convention over Configuration, CAP provides defaults for many things that you’d have to configure in other frameworks. Stay within the confines of these defaults to benefit from things just working automatically. You can always override the defaults by adding your own configurations.
Zero Setup
The project folder is empty at the beginning, so there’s really no setup required. No config files, no manifests whatsoever. The simple reason is that we don’t need them in these early phases of a project. We may need some later on, for example, when we reach the step about Deploying to the Cloud, but there’s no need to bother with this now.
Contracts First
Most frequently, CAP projects are about creating services. Service definitions describe the API contract between a service provider and its consumers. To speed things up, you can quickly create an all-in-one service as follows:
Copy and paste this into a file srv/cat-service.cds
:
@path:'/browse'
service CatalogService {
entity Books {
key ID : UUID;
title : String;
descr : String;
author : Association to Authors;
}
entity Authors {
key ID : UUID;
name : String;
books : Association to many Books on books.author=$self;
birth : Date;
death : Date;
}
}
Prefer Top-Down Approaches
Instead of following a bottom-up approach, starting from the data model, then putting services on top, then adding UIs, and so on, it’s much better to apply a top-down approach as follows:
- Roughly sketch your application’s usage and use-cases, for example, your UIs.
- Capture required interfaces in use case-oriented service definitions.
- Start building the actual UIs on top, and in parallel…
- Have other teams working on the data models below.
This (a) allows you to separate and parallelize work loads and (b) results in much better service interfaces than the one you’d get by using the bottom-up approach.
Running Out-of-the-Box
When we save the file we created in the former step, the cds
watcher in the terminal immediately reacts, showing this output:
[cds] - connect to datasource - sqlite::memory:
[cds] - serving CatalogService at /browse
[cds] - service definitions loaded from:
srv/cat-service.cds
node_modules/@sap/cds/common.cds
[cds] - server listening on http://localhost:4004 ... (terminate with ^C)
[cds] - launched in: 355.732ms
Full-Fledged OData Services
Click the link http://localhost:4004, … et voila, we are in contact with a full-fledged OData service (for example, see $metadata) in which we can even start Fiori previews to get idea of what a UI might look like.
Let’s do some ad hoc tests:
Served by Generic Providers
What we see here are the effects of Generic Providers, which handle many things out of the box, such as compiling the CDS models into OData $metadata
documents on the fly, as well automatically serving all CRUD requests, thereby handling all the OData protocol features such as $batch
, up to complex choreographies such as Fiori Draft. This saves us lots of work at this point and allows us to immediately go on with the next steps instead of implementing all of this in boilerplate code.
Learn more about generic providers.
Mocking App Services
Use cds run --in-memory
to quickly start a lightweight Node.js server with sqlite’s transient in-memory database instead of always deploying to and connecting to your target database. Do that not only in Node.js projects but also as a mock server in Java projects, for example, for frontend-related tasks, or as a mock up for remote services to integrate with.
Prerequisites
- API description of the mocked service
- CAP tools installed (Create a Business Service with Node.js Using Visual Studio Code)
- Node.js installed (Official Node.js website)
The sample mock server created in the following steps is based on the mock server developed in the TechEd 2019 tutorial Creating an SAP S/4HANA Extension with SAP Cloud Application Programming Model and SAP Cloud SDK.
Create a Project for the Mock Server
- Create an empty project for the mock server by executing
cds init mockserver
in the terminal. - Execute
code mockserver
to open the newly created project in VSCode. - Open the package.json file and add
"@sap/cds-dk": "^1.0.0"
as a dependency. Executenpm i
to install all dependencies.
Add Service API Definition
- Download the service API definition from the SAP API Business Hub in EDMX format.
- Import the downloaded API definition by running
cds import ~/Downloads/API_BUSINESS_PARTNER.edmx
. This converts the EDMX service API definition to a Core Schema Notation (CSN) definition and places it into a local subfoldersrv/external
.
Add a Dummy services.cds
File
- In the
srv
folder, create a file namedservices.cds
. - Add this line in the file:
using { API_BUSINESS_PARTNER } from './external/API_BUSINESS_PARTNER';
Keep this file empty to serve imported APIs the default way. It can be used to tailor the mock server to your specific needs, for example, by adding or overriding certain definitions.
Run the Mock Server
- Execute
cds run --with-mocks --in-memory
to start the mock server with in-memory database.
Alternatively you can execute cds watch
, which essentially is a shortcut to the same cds run
command but also starts a monitor to restart the server automatically if sources are changed.
Optionally Add Sample Data
- Create a new file
init.js
in thesrv
folder. - Paste the following code:
module.exports = (db)=>{
const { A_BusinessPartnerAddress: Addresses } = db.entities(
'API_BUSINESS_PARTNER'
)
return cds.run ([
INSERT.into(Addresses).columns(
'BusinessPartner',
'AddressID',
'CityName',
'StreetName'
).rows(
[ '1003764', '28238', 'Walldorf', 'Dietmar-Hopp-Allee' ],
[ '1003765', '28241', 'Palo Alto', 'Hillview Avenue' ],
[ '1003766', '28244', 'Hallbergmoos', 'Zeppelinstraße' ],
[ '1003767', '28247', 'Potsdam', 'Konrad-Zuse-Ring' ]
)
// add more INSERTs here, as appropriate
])
}
Mock Custom Responses
To extend the mock server with custom logic, you can create a custom handler. To do so, create a .js
file with the same name next to the imported service definition file, in our case srv/external/API_BUSINESS_PARTNER.js
. Add the custom logic there:
module.exports = cds.service.impl (srv => {
// add your custom handlers here...
})
Mock Error Cases
To create error cases, explicitly return errors in a custom handler by using the req.error
or req.reject
functions. For example, add the following code in the API_BUSINESS_PARTNER.js
file:
module.exports = cds.service.impl(srv => {
srv.before('READ', 'A_BusinessPartnerAddress', req => {
const { BusinessPartner, AddressID:ID } = req.data
if (BusinessPartner === '1003764' && ID === '28238')
req.reject (500, 'Your error message.')
})
})
To trigger this error, use the following request:
http://localhost:4004/api-business-partner/A_BusinessPartnerAddress(BusinessPartner='1003764',AddressID='28238')
Reset Mock Data at Runtime
To reset the mock data at runtime without restarting the mock server, define an unbound action.
When using
cds watch
, executingrs
in the terminal with the running watch command will restart the mock server and reset the mock data without the need of an unbound action.
Declare the action in the mock service definition. In srv/services.cds
add the following code:
extend service API_BUSINESS_PARTNER with {
action reset();
}
In srv/external/API_BUSINESS_PARTNER.js
add the implementation of the action:
srv.on('reset',async () => {
const db = await cds.connect.to('db')
await db.run(()=> require('../init')(db))
})
This will delete the data from the database and fill it with the initial data.
Trigger the reset action with the following POST request:
http://localhost:4004/api-business-partner/reset
Growing On…
- Domain Modeling
- Providing
- Events & Messaging
- Using Generic Providers
- Using Databases
- Localization (i18n)
- Adding Localized Data
- Adding Temporal Data
- Adding Authorization
- Adding Data Privacy
- Using Multitenancy
- Reuse & Compose
- SaaS Extensibility
- Serving OData APIs
- Serving SAP Fiori UIs
- Deploying to the Cloud
- Adding Audit Logging
- Using Monitoring & Analytics
- Adding Tests
- Using CI/CD
Deploying to the Cloud
CAP applications can be deployed to SAP BTP, Cloud Foundry environment. In the end, it’s about deploying regular Node.js and/or Java applications, and about creating and binding appropriate service instances (see the Cloud Foundry Developer Guide). For more details, see Deploying to the Cloud.