Quicklinks:
App Router Chapters
Quick Ref
Sample project files
One last round of our
Application Router learning.
After we’ve learned several basic configuration options, we’ve been ready to use the Application Router.
Today, we’re dedicating a blog for enhancing development experience:
How to use app router in
local development ?
As always: you can skip this blog if you’re exigent and prefer sophisticated blogs – or if you never work locally
Your choice...
Prerequisite
You need
Node.js on your local machine
You should have gone through
Part 1
Preparation
You can go through
Part 1 if not already done
Learning 10: How to run app router locally
As usual, we start with a simple learning:
Install app router locally and define a simple route
It is similar like learning 1
10.1. Install app router locally
To be honest, there’s nothing special about local app router installation:
Same as before, we define a dependency to the app router module in
package.json file
OK, so we create a folder, e.g.
C:\tmp_approuter_loc
No subfolder, since we’re not using a manifest
In folder
C:\tmp_approuter_loc create a file with name
package.json
In folder
C:\tmp_approuter_loc create a file with name
xs-app.json
Paste the following content into the
package.json file:
{
"dependencies": {
"@sap/approuter": "^6.8.2"
},
"scripts": {
"start": "node node_modules/@sap/approuter/approuter.js"
}
}
You can see: it is the same like before.
The only difference to cloud-scenario: today we really need to install the libraries
(When deploying to the cloud, the libraries are downloaded there, if not present
Open command prompt, navigate to folder
C:\tmp_approuter_loc
and run
npm install
That’s it: we’ve installed app router locally
Have you noticed that it is all the same like in previous tutorials?
However, now we need to pay attention to the configuration
10.2. Configure app router for local usage
In previous tutorials, we’ve learned that configuration is done in 2 files:
xs-app.json
No special configuration required here for local app router
{
"authenticationMethod": "none",
"routes": [
{
"source": "^/saproute/(.*)$",
"target": "$1",
"destination": "env_destination_saphome"
}
]
}
manifest.yml
Here we define the destinations which are used in
xs-app.json
So NOW we have a problem:
manifest.yml is used only in cloud
During deployment, the destinations become part of the environment of our deployed app (in this case it is the app which contains the app router)
When using app router locally, we have no deployment, manifest.yml is useless.
But nevertheless, we need the destinations as environment variable in the node process which runs the app router
Fortunately...
this is not a big problem:
app router has built-in support for local mode:
it can load required environment variables from a file with name
default-env.json
As such,
We create a file with name
default-env.json
in the root folder, next to
package.json (this is from where we start the app router)
The content of this file is a JSON object with the same structure like the environment variables in SAP Cloud Platform
For our simple example, we copy the following content into the
default-env.json file
{
"destinations" : [
{
"name": "env_destination_saphome",
"url": "http://sap.com/"
}
]
}
10.3. Start app router locally
Now we can start the app router.
How to start it locally?
This step is simple as well:
App router is a server which can be started using the command which we defined as “start script” in the
package.json
Remember?
"scripts": {
"start": "node node_modules/@sap/approuter/approuter.js"
To start app router on our local machine, we open a command prompt, step into
C:\tmp_approuter_loc
and run the following command:
npm start
This command will start the app router and in the console we see some output like the following:
During startup, app router writes some interesting info about its settings to the console
The log output which is interesting for us: the last line. We can see that the app router – server is running on port 5000
10.4. Using local app router
Now we can open a browser window and invoke the route which we defined above.
We know that the URL has to start with localhost:5000
And we know the route we defined: saproute
As such, we can invoke the following URL with the Browser:
http://localhost:5000/saproute/
As a result, the sap homepage is displayed
Learning 11: How to use xsuaa with local App Router
Our first very simple example has already shown the mechanism how cloud-specific settings are passed to app router in local scenario:
using a special file
Now the second challenge: security
We know that the most important feature of app router is to do the OAuth flow for us.
I mean, app router can connect to the Authorization Server and handle the authentication, acquire a JWT token and forward it to the configured endpoint
To achieve that, the approuter-app is bound to an instance of XSUAA - service
As such, after deployment, app router can connect to that xsuaa
Under the hood:
After deployment, the app which is bound to xsuaa receives the needed info as environment variable
The environment variable
VCAP_SERVICES contains the address and credentials required to connect to the XSUAA (Authorization Server) to handle the OAuth flow
App router can read the content of the environment variable doing something like
process.env
(we know, app router is implemented in Node.js)
We see: we can make use of the same mechanism like we did for the destinations
We can copy the value of
VCAP_SERVICES to our
default-env.json file
Then the app router can handle OAuth flow even when running locally
Let’s try it
11.1. OAuth protected target service
To try our scenario, we need an OAuth protected service which we want to call with the help of app router
Such target service can be an OData service created with “SAP Cloud Platform Backend Service”
Or it can be any other service running on SAP Cloud Platform
Or it can be any other service running locally (probably using the same mechanism, to connect to xsuaa from local server)
What do you prefer?
Your choice.
What I’m doing:
In this blog, I’d like to avoid repeating the Backend-Service-stuff, so I’m using a service running in the cloud.
My example service URL:
https://myapp.cfapps.eu10.hana.ondemand.com/mysrv/Products
I try to invoke the URL: no chance, it will respond with “Unauthorized”
Yes, it does so.
Even for me
And that’s good.
Because we need it for the show
11.2. Configure local app router with XSUAA
Now what we want:
Use app router in the middle between us and the protected service
What we would do in “normal” mode:
Deploy app router to the cloud and bind it to the same instance of xsuaa
What we do in “local” mode:
We need the credentials of that “same xsuaa” on our local machine
We have to view and copy the environment variable for xsuaa
To achieve that, we have 2 options:
1. Check the environment variables of that protected service (called myapp in my example)
2. Create Service Key for the same xsuaa instance used by the protected service
Description
11.2.1. View environment variable
On command line:
cf env myapp
Using Cloud cockpit:
Navigate to the details screen of the deployed app
In the left menu pane click on ‘’Environment Variables”
OK, now that everybody has seen the environment variables, we can copy the JSON
Note:
We need a note about copy&pasting
If we copy&paste the whole Environment Variables section, then we get an error
Reason: the Environment Variables section is not
one valid JSON object.
It is
two valid JSON objects
And they're following each other
See the coarse structure:
{
"VCAP_SERVICES": {
. . .
}
}
{
"VCAP_APPLICATION": {
. . .
}
}
As such, when copying, make sure to copy only this object:
{
"VCAP_SERVICES": {
. . .
}
}
Note:
This is a sub-note of the copy&paste-note:
If the app is bound to multiple services, then the VCAP_SERVICES object can get very big.
However, for our app router scenario we need only the xsuaa section
You can reduce the JSON,
or copy&paste all
your choice
11.2.2. Create Service Key
Alternatively, you can get the required credentials via service key
I don’t know why you should follow this description, the previous alternative is faster,
but:
your choice
On command line:
To create service key:
cf csk xsuaaName myServiceKey
Then to view its content:
cf service-key xsuaaName myServiceKey
Result: a JSON object which we can copy&paste
Note:
See below note about copy&pasting service key-json
In cockpit:
Go to your space -> Services -> Service Instances
Click on the instance of xsuaa service -> Service Keys
Create a Service Key with any name: your choice
Click into the section with created service key -> Ctrl+A -> Ctrl+C
Note:
Again, we need a note about copy&pasting:
A service key is only a key, not the environment variable which we need
As such, those of you who had chosen to use the create-service-key-way, have to manually create the required structure:
{
"VCAP_SERVICES": {
"xsuaa": [
{
"tags": [
"xsuaa"
],
"credentials": {
"clientid": "sb-thexsappname!t12345",
. . .
You can see:
The content of the “credentials”-node is the service-key itself
Note:
The “tags” property is required because this is how app router finds the proper node, while parsing the JSON
How to copy&paste:
We have already a
default-env.json file which already contains a valid JSON object.
We have to make sure to paste the copied
VCAP_SERVICES correctly, otherwise the JSON parser will fail
The current structure:
{
"destinations" : [
{
"name": "env_destination_saphome" . . . .
}
]
}
We have to add a comma, then paste the new object.
Note:
The new object doesn’t have surrounding brackets:
{
"destinations" : [
{
"name": "env_destination_saphome" . . . .
}
],
"THE_NEW_OBJECT_GOES_HERE" : . . . . .
}
Finally after paste:
{
"destinations" : [
{
"name": "env_destination_saphome",
. . .
}
],
"VCAP_SERVICES": {
"xsuaa": [
{
"credentials": {
"clientid": "sb-thexsappname!t12345"
. . .
}
}
]
}
}
Very finally:
This is a sub-finally of the default-env-finally above:
To summarize:
what we've done is to copy a JSON object into a local file
Very note:
I don’t know if this is a sub-note, I’m confused…
Anyways, in the
appendix, you can find all sample files
11.2.3. destination
Now that we’re prepared for local app router with xsuaa, we can do the usual route configuration
First we add a new destination to the manif........Sorry, obviously we’re not using manifest
So we add a new destination to the destinations-node of our
default-env.json file
The destination points to our protected service which we’ve chosen in 11.1.
In my example:
{
"name": "env_destination_mysrv",
"url": "https://myapp.cfapps.eu10.hana.ondemand.com",
"forwardAuthToken": true
}
Note:
Don’t forget to add a comma after the first destination, otherwise the JSON would be invalid
Note:
Don’t forget to add the property forwardAuthToken, because this destination is oauth-protected
Note:
Don’t forget to remember the name of the destination, we’ll need it in the route definition
Note:
Please refer to
appendix section for full sample code
11.2.4. route
Now we can add a new route to our xs-app.json file
{
"source": "^/protectedroute/(.*)$",
"target": "$1",
"destination": "env_destination_mysrv",
"authenticationType": "xsuaa"
}
And important:
We have to change the top-level property
authenticationMethod
from
"authenticationMethod": "none",
to
"authenticationMethod": "route",
See
appendix...
11.3 Start local app router
If your app router is still running, you need to stop it
Otherwise it wouldn’t read the new configuration
As usual, press Ctrl + C, then Y
Then re-start it like described above, no special param is required
Make sure to watch the log output, because the startup would fail if the
default-env.json file would have invalid JSON
11.4. Usage
Remember:
the forbidden URL which we wanted to call was (in my example)
https://myapp.cfapps.eu10.hana.ondemand.com/mysrv/Products
Now we can enter the following URL in a browser window
http://localhost:5000/protectedroute/mysrv/Products
As a result, a login screen is displayed
After entering our cloud-user credentials, we can see the desired result
Summary
For me, local app router was a huge benefit for local app development
It helps when defining routes, so we don’t need to deploy app router each time, when we fix anything in the configuration
If we're running or developping an OAuth-protected service locally (e.g. with CAP) then we urgently need a local app router to avoid that we have to do the OAuth flow with REST client each time we want to try the app
It helps tremendously when working locally on an app which requires app router, e.g. ui5
Note about local development
You’ve noticed:
We have to distinguish between several levels of “local”
The app router is running locally
The XSUAA Authorization Server is running in the cloud
The target endpoint can be local server or remote server
So the described example was a mix of local development and remote Authorization Server
Quick Reference
For local app router we need:
- Install and configure normally via
package.json and
xs-app.json
- Add
default.env.json containing destinations
- Start with
npm start and access at localhost:5000
To use xsuaa:
- Get xsuaa credentials via app env or service key
- copy
VCAP_SERVICES to
default-env.json file
Links
SAP Help Portal: Application Router documentation
entry page
Overview of
tutorial series
Appendix 1: Background info about local configuration
If you’re interested to check how app router supports local scenario:
Go to folder
C:\tmp_approuter_loc\node_modules\@sap\approuter\lib\configuration
and open the file
env-config.js
Here you can see that the library
@Sap/xsenv is used to load a file with hardcoded name
default-env.json into the environment
The convenience method
xsenv.loadEnv()just parses the json and sets all found properties as variables into
process.env
Like that they can be accessed by app router
Appendix 2: Sample project files
See here exemplary files for the scenarios described above
package.json
{
"dependencies": {
"@sap/approuter": "^6.8.2"
},
"scripts": {
"start": "node node_modules/@sap/approuter/approuter.js"
}
}
xs-app.json
{
"authenticationMethod": "route",
"routes": [
{
"source": "^/saproute/(.*)$",
"target": "$1",
"destination": "env_destination_saphome"
},
{
"source": "^/protectedroute/(.*)$",
"target": "$1",
"destination": "env_destination_mysrv",
"authenticationType": "xsuaa"
}
]
}
default-env.json
{
"destinations" : [
{
"name": "env_destination_saphome",
"url": "http://sap.com/"
},
{
"name": "env_destination_mysrv",
"url": "https://msgmock.cfapps.eu10.hana.ondemand.com",
"forwardAuthToken": true
}
],
"VCAP_SERVICES": {
"xsuaa": [
{
"tags": [
"xsuaa"
],
"credentials": {
"apiurl": "https://api.authentication.eu10.hana.ondemand.com",
"clientid": "sb-xsappname!t12345",
"clientsecret": "V7Qfqe4YxjsUEMwn-v/iGxxx=",
"identityzone": "abcdef123trial",
"identityzoneid": "b2d58db5-123-aaa-bbb123-8b23be70fd5c",
"sburl": "https://internal-xsuaa.authentication.eu10.hana.ondemand.com",
"tenantid": "b2d58db5-635f-44a2-aaa-bbb-ccce70fd5c",
"tenantmode": "dedicated",
"uaadomain": "authentication.eu10.hana.ondemand.com",
"url": "https://abcdef123trial.authentication.eu10.hana.ondemand.com",
"xsappname": "xsappname!12345"
}
}
]
}
}