One key ingredient of SAP CAP is it's openness. Openness especially means, that we are free to choose the development tools, the used database, the libraries, etc. Especially we are free to choose on which platform we run and operate CAP. All we need is @sap/cds-dk
and node.js
.
This article demonstrates:
For this article I reuse my sample project "Yet Another Covid-19 Tracker" which I described recently in the article SAP CAP Remote Services & SAP Fiori Elements.
But of course you can follow this article as well with your own project or a sample cap project from the SAP Github Samples.
While development SAP CAP comes with several Features activated by default to ease rapid development. These include for example an in-memory db, mocked authentication, Fiori Preview, live reload or mocked bindings. In production we usually do not want to lean back on default Features meant for development, but want to make Features explicit.
We can easily check which Features are activated by running cds env
. In package.json
or .cdsrc.json
we can explicitly override the default setting, if we want to. Find more about environment variables in the official SAP CAP for node.js documentation.
The default cds environment variables are set according to the current NODE_ENV
variable. NODE_ENV
is usually set to production
in productive environments and something else, e.g., development, in other environments. If set to production
the development features are deactivated. This may cause problems and errors if we rely on these features, e.g., a missing service binding.
In the following table you can compare the difference between development and productive settings of a clean CAP project as of June 2021:
Parameter | development | production |
---|---|---|
_sources | [.env, default-env.json, .cdsrc.json, defaults.js, process.env] | [default-env.json, .cdsrc.json, defaults.js, process.env] |
env | development | ' |
features.fiori_preview | true | false |
features.fiori_routes | true | false |
features.in_memory_db | true | false |
features.live_reload | true | false |
features.mocked_bindings | true | false |
profiles | [ 'development' ] | [ 'production' ] |
requires.auth.kind | mocked-auth | JWT-auth |
requires.auth.strategy | mock | JWT |
requires.auth.users.* | true | ' |
requires.auth.users.alice.roles | [ 'admin' ] | ' |
requires.auth.users.bob.roles | [ 'builder' ] | ' |
requires.sql.credentials.database | :memory: | ' |
requires.sql.kind | sqlite | hana |
requires.sql.use | sqlite | hana |
For my project I would like to use the in-memory DB Feature. Therefore, I simply add the following configuration into .cdsrc.json
:
{
"features": {
"in_memory_db": true
}
}
The different behavior of CAP in development and production requires us to test any CAP application (or node.js application in general) before deploying into production. We can do this by simulating a productive environment by first setting NODE_ENV
to production.
# windows powershell
$env:NODE_ENV="production"
# windows cmd
set NODE_ENV=production
# unix
export NODE_ENV=production
Next we simulate typical steps a deployment executes by running the following commands:
# clean install of production dependencies
npm ci --only=production
# start cap app
npm run start
As the NODE_ENV
variable is set to production the commands behave differently and remove dev dependencies and the cds commands included in the start script use the productive settings as well.
Warning: Be aware of the fact, that you may have installed @sap/cds-dk
globally, which contains commands which are not available in a productive environment. If you rely on those commands you may need to add the package to your dependencies, or better rethink to not rely on the command(s) at all.
Another typical pitfall is to forget setting the environment variables in the target environment. While development we typically use configuration files like .env
or default-env.json
to set those variables. But they are usually not checked in into the code repository because they are not only environment specific but may contain sensitive information such as passwords or API keys.
Therefore, we need to check if we have set all environment variables in the target environment before deployment.
Note: Based on the environment variables, .env
files are not evaluated in production.
In a productive environment we usually don't want to log every http request we serve. Therefore, we can set the log level for the module cds to warn
or error
for examplein .cdsrc.json
:
{
"log": {
"levels": {
"cds": "warn"
}
}
}
One popular Platforms (PaaS) to deploy and run projects in the cloud is Heroku. Heroku is especially handy for simple POCs or building MVPs as it is very simple and straight forward. You can directly deploy from your git repository to Heroku.
After we created a new project we need to configure all environment variables which are only included in environment specific files and therefore not part of the deployment. One typical variable could be the destinations
variable, which contains name, url, user and password of a service which we want to integrate.
Deployment is as easy as clicking a button after connecting your git repository with heroku. Alternatively, you can also listen on changes on a specific branch and deploy whenever a new commit occurs.
Deploying to Heroku is straight forward. But we can do it even more generic and containerize the application with docker.
Therefore, we create a Dockerfile
in the root folder of the project:
FROM node:14-alpine
# use productive environment
ENV NODE_ENV production
# copy source as node user
WORKDIR /usr/src/app
COPY . .
# install dependencies
RUN npm ci --only=production
# run app as node user
EXPOSE 4004
USER node
CMD ["npm", "start"]
Then we can run the following command to build the docker file:
docker build -t <YOUR_CONTAINER_NAME> .
This docker container can now be deployed anywhere. You can test it locally as follows:
docker run -p 4004:4004 -t <YOUR_CONTAINER_NAME>
If something does not work and you want to inspect the container you can open a shell in the container like this:
docker run -i -t <YOUR_CONTAINER_NAME> /bin/sh
You see that it is quite simple to use and deploy SAP CAP anywhere. Staying in the SAP ecosystem and using SAP's services eases developing enterprise grade applications, but it is not mandatory.
My application uses an in-memory DB for caching purposes only. If you want to use a persistent DB but do not want to use HANA, then you should give cds-pg a try, which is an adapter for PostgreSQL.
If you want to try out the app, you can find it here:
Answer free text questions against the DB using GPT-3.
Let's build an adapter with SAP CAP to transform an OData Service to a custom REST Service
Unchain SAP CAP: How to enable social login and role based access control using Auth0
Evaluate and explore the capabilities of SAP CAP and SAP Fiori Elements with "Yet Another Covid-19 Tracker".