SAP Tech Bytes: Using the SAP Approuter at dev time – a full-stack journey with UI5 and SAP CAP – Cloud Foundry Basics #6

This SAP Tech Byte is about how to develop a full-stack application using the SAP Approuter, UI5, and the SAP Cloud Application Programming Model (CAP). In part 1 we will focus on how to setup our project so that both the UI5 Tooling and the CAP server can be started with a single command and run on the same port. For this we will use the cds-plugin-ui5, which frees us from complex proxy or destination configuration we had in the past. In part 2 we will add productive authentication and run our application with a hybrid setup using the SAP Authorization and Trust Management Service (XSUAA) on SAP BTP, Cloud Foundry environment. For this we will use the dev-approuter, which is a dev time wrapper for the SAP Approuter. It allows us to use all of the SAP Approuter capabilities (OAuth 2.0 login flow with XSUAA etc.) while still running the UI5 Tooling and CAP server in the same process.

The source code for this blog post can be found at https://github.com/SAP-samples/sap-tech-bytes/tree/cloud-foundry-basics/post6.

This blog post is also available in video format – presented as part of Devtoberfest 2023:

 

NPM Workspaces

We start this project by setting up NPM workspaces, which make it easy to manage and run multiple Node.js applications inside one bigger project. Also, NPM workspaces install node modules very efficiently, so that multiple applications share the same resources. To set up NPM workspaces, all we need is this simple package.json file:

{
    "name": "devtoberfest-dev-approuter",
    "workspaces": [
        "packages/*"
    ],
    "scripts": {
        "dev": "npm run dev -w=approuter"
    },
    "sapux": [
      "packages/uimodule"
    ]
}

The line most relevant for now is the one defining the packages/ directory as the home for all our workspaces (think Node.js applications). The rest of the file contains configuration for things that not yet exist in our project, but which we will add shortly.

 

SAP CAP Application

Let’s create and navigate into the packages/ directory:

mkdir packages
cd packages/

We can now create a SAP CAP Application in the form of the well-known bookshop sample. Such an application can easily be generated by running the following commands in the Terminal of your choice:

cds init cap-server
cd cap-server/
cds add sample

Note: “cap-server” is the name of our application and is therefore exchangeable.

And that’s it for the SAP CAP part. Our server is ready.

 

UI5 Application

Let’s now create a new UI5 application from scratch. Using the SAP Fiori Tools Application Generator we can easily choose one of the available floorplans, select our SAP CAP project as the data source, and then enable the use of the cds-plugin-ui5:

Wait a minute, why do we need the cds-plugin-ui5 in the first place?

SAP CAP applications by default come with an app/ directory, which is the place for frontend applications. Any contents (such as html files) inside this directory get statically served by the SAP CAP server. One could be tempted so simply put UI5 apps in there and serve them through the SAP CAP server (and to be honest this works fairly well), but this approach completely misses out on all of the capabilities that the UI5 Tooling has to offer. Think about a UI5 application developed with TypeScript for example. TypeScript cannot run in the browser as it first needs to be transpiled to JavaScript. We need the UI5 Tooling in this case. Additionally, there should be a clear separation of concern and both components – the SAP CAP backend and UI5 frontend – should be developed and served using their respective tooling. The cds-plugin-ui5 is very helpful in that regard as it hooks the UI5 Tooling into the SAP CAP server, which means we can use both toolings using a single command.

Back to our project. Enabling the cds-plugin-ui5 and NPM workspaces in the SAP Fiori Tools Application Generator (see red box above) adds both configurations to the SAP CAP’s package.json. However, we already have configured NPM workspaces one level higher in the parent directory, so we can delete this newly added configuration from the SAP CAP’s package.json. It is important to leave the cds-plugin-ui5 dependency though, so that upon start of the SAP CAP server the cds-plugin-ui5 can hook all connected UI5 apps into the server.

But how does this “connection” between the SAP CAP server and the UI5 apps work? There are two options here. The first one being to simply place the UI5 apps inside the app/ directory of the SAP CAP project, which is the default place for frontend applications. The second option is to add the UI5 apps as devDependencies to the SAP CAP’s package.json and provide the path to them (when using NPM workspaces, a wildcard * is enough – NPM will handle the module resolution properly). We will go with the second approach here add the “uimodule” as a devDependency. All of the mentioned changes leave us with the following package.json for the SAP CAP project:

{
    "name": "cap-server",
    "version": "1.0.0",
    "description": "A simple CAP project.",
    "repository": "<Add your repository here>",
    "license": "UNLICENSED",
    "private": true,
    "dependencies": {
        "@sap/cds": "^7",
        "express": "^4"
    },
    "devDependencies": {
        "@cap-js/sqlite": "^1",
        "cds-plugin-ui5": "^0.2.1",
        "uimodule": "*"
    },
    "scripts": {
        "start": "cds-serve"
    }
}

In spirit of the side-by-side approach (as far as tooling is concerned) mentioned earlier, we will now move our newly created uimodule out of the SAP CAP server and place it next to it. After that, we can completely remove the app/ directory, which we no longer need, so we end up with the following project structure (dot files and generated files omitted for brevity):

- packages/
  - cap-server/
    - db/
    - srv/
    - package.json
  + uimodule/
- package.json

So far so good. We can now install all dependencies from the project root (not the SAP CAP root) – thanks to the NPM workspaces:

npm install

Interesting side note: For the uimodule “installing” means creating a symbolic link inside the node_modules/ to the actual uimodule.

We should now be able to start both the SAP CAP server and the UI5 Tooling simultaneously. All we have to do is start the SAP CAP server, the cds-plugin-ui5 will do the rest for us. Run the following command from the SAP CAP root (/packages/cap-server/):

npm start

In the logs of the terminal we can see that the UI5 app was mounted to the SAP CAP server:

We can also see the UI5 app being served with the SAP CAP server. It even gives us a small hint that this web app is being served using the UI5 Tooling:

Our UI5 app is fully functional and display the data it retrieves from the SAP CAP server. And the best part is, we didn’t have to configure any destination or proxies, as both the backend and frontend run on the same port:

 

Authentication

Our current dev setup is pretty nice and powerful already, but there is one aspect we haven’t touched so far – and that is authentication. Our SAP CAP server currently uses basic (mocked) authentication, which should be sufficient for the largest portion of your development efforts. But what if we want to test our app with “real” productive authentication using the SAP Authorization and Trust Management Service on SAP BTP (XSUAA)? As far as SAP CAP is concerned, this is not a problem at all. In the SAP CAP project directory, we can simply run the following command to add the XSUAA feature to the app:

cds add xsuaa && npm install

We can then create an instance of the XSUAA service (in case we don’t have on yet), create the respective service key and bind it to our local SAP CAP project:

cf create-service xsuaa application my-xsuaa -c xs-security.json
cf create-service-key my-xsuaa my-sk
cds bind --to my-xsuaa:my-sk

Note: This approach uses the cf CLI. Of course, you could achieve the same thing using the SAP BTP Cockpit in the browser.

Ok, so far so good, but if we were to run our full-stack application now, we would always get a “401 – Unauthorized” error since we haven’t logged in (see screen shot below). Without logging into the XSUAA instance (running on SAP BTP) we don’t have a valid JSON Web Token (JWT) and SAP CAP rightfully rejects our requests. Maybe this already rings a bell with you (the title of this blog post certainly gives a hint) – we need an SAP Approuter to handle the login flow.

 

Approuter

An SAP Approuter is a simple Node.js based application that is using the @sap/approuter package. It is used to forward user requests to several data sources (usually via destinations), handle user logins and sessions, and also serve static content, such as frontend applications. This sounds almost exactly like what we need, but only almost. Our UI5 application, which is developed in TypeScript, doesn’t really qualify as “static content” as it needs to be transpiled (using the UI5 Tooling) before we can run it in the browser and the SAP Approuter can serve it. This means we want to connect an SAP Approuter to the UI5 Tooling, and also to the SAP CAP server while we are at it. There is a new dev-time wrapper for the SAP Approuter that fullfils exactly this purpose: the dev-approuter. Let’s use it.

As we are using NPM workspaces, we can simply create a new directory for our new (dev-)approuter in the packages/ directory:

mkdir approuter

We can then add this package.json to the approuter/ directory:

{
    "name": "approuter",
    "scripts": {
        "dev": "CDS_ENV=hybrid node -e 'new require(`dev-approuter`)' _"
    },
    "devDependencies": {
        "dev-approuter": "*",
        "cap-server": "*",
        "uimodule": "*"
    }
}

Note that the “dev” script starts the dev-approuter with the CDS_ENV environment variable set to hybrid, which makes sure the SAP CAP server gets started in hybrid mode using the XSUAA instance on SAP BTP. Speaking of the SAP CAP server (“cap-server”) – it is added to the application as a devDepdendency, just like our UI5 application (“uimodule”). This is very similar to the way we configured the cds-plugin-ui5 – remember? This is because the dev-approuter reuses the cds-plugin-ui5 under the hood.

The SAP Approuter’s routing configuration is usually described in xs-app.json file. For the dev-approuter however, we can create an xs-dev.json file, which is to safely separate dev time configuration from productive code. The xs-dev.json can be seen as a superset of the xs-app.json – it follows the same syntax and has the same features, but there is one extra goodie: you can directly link dependencies (defined in the package.json) to routes. See this xs-dev.json file, which we now add to the approuter/ directory:

{
    "welcomeFile": "uimodule/index.html",
    "authenticationMethod": "route",
    "routes": [
        {
            "source": "^/user-api(.*)",
            "target": "$1",
            "service": "sap-approuter-userapi"
        },
        {
            "dependency": "uimodule",
            "authenticationType": "xsuaa"
        },
        {
            "dependency": "cap-server",
            "authenticationType": "xsuaa"
        }
    ]
}

The bottom to route (reflecting our dependencies) are protected with XSUAA, which means we will have to provide the dev-approuter with credentials to connect to the XSUAA instance on SAP BTP. We can get the service key which we created earlier with the following command:

cf service-key my-xsuaa my-sk

Copy the service key from the terminal output and provide it to the dev-approuter via a default-env.json file in the approuter/ directory:

{
    "PORT": 5001,
    "destinations": [
        {
            "Name": "cap-server",
            "Authentication": "NoAuthentication",
            "ProxyType": "Internet",
            "Type": "HTTP",
            "URL": "http://localhost:4004",
            "forwardAuthToken": true
        }
    ],
    "VCAP_SERVICES": {
        "xsuaa": [
            {
                "tags": [
                    "xsuaa"
                ],
                "credentials": { SERVICE KEY FROM TERMINAL OUTPUT }
            }
        ]
    }
}

As you can see, the default-env.json in our case not only includes the service key credentials, but also a destination to our SAP CAP server. This is necessary, because we require the additional parameter forwardAuthToken to be true, and the destination injected by the dev-approuter by default does not include this parameter.

We are now ready to start the dev-approuter using the following command from the project root (the NPM workspaces root):

npm run dev

Et voilà, we can now login and access our UI5 application through the dev-approuter, which displays the data from the SAP CAP server:

We can verify that we are logged in with our SAP BTP user by accessing the /user-api route (also defined in xs-dev.json):

By the way, the livereload for the UI5 application (via the UI5 Tooling) also works using the dev-approuter.

And that’s it, we now have an SAP Approuter using the dev-approuter package to hook the UI5 Tooling as well as the SAP CAP server into it. All of this is running in hybrid mode connected to our XSUAA instance on SAP BTP – talk about a powerful full-stack dev setup!

 

Feel free to reach out in case you have any questions!

 


sap-tech-bytes

 

 

 

 

SAP Tech Bytes is an initiative to bring you bite-sized information on all manner of topics, in video and written format. Enjoy!

 

 

Scroll to Top