In this post, I show-case the latest innovation for web apps in SAP BTP, Cloud Foundry environment - hosting them without an approuter. With this new capability of the SAP Launchpad service, you no longer need to deploy backend components to host web apps - which, therefore, run serverless on SAP BTP.
Updates:
29th Jan 2021: Rebranding to SAP BTP
16th Feb 2021: Switch to instance-level destinations
22nd Mar 2021: Performance tip
Now that the
HTML5 Application Repository gets more and more popular, I received a couple of questions about whether it is possible to use only one
approuter for multiple web apps to lower the
total cost of ownership (TCO). The good news is that this can be done fairly easily. The even better news is that you don't need an approuter at all! The existing
SAP Cloud Portal subscription and the new
SAP Launchpad subscription service already come with an integrated
HTML5 application runtime that will allow you to access web apps directly via the Launchpad URL. In this blog post, I will explain how to deploy regular web apps and SAP Fiori apps and let them be managed by SAP BTP, Cloud Foundry runtime (Check out this post if you want to do the same in the
Kyma runtime). Fiori Apps can then easily be found by the Content Explorer of the Launchpad service and directly be added to a Launchpad.
The Content Explorer of the Launchpad service detected an available SAP Fiori app automatically and suggests adding it to the SAP Fiori Launchpad.
Old state
Several things need to happen in SAP BTP, Cloud Foundry environment to run a web app. For once, the application needs to be hosted somewhere. As I explained in a
talk earlier this year, many reasons speak for the HTML5 Application Repository service. Therefore, we need to
(a) upload the web app into this repository during the deployment. Next, we need an approuter to
(b) redirect incoming traffic to the web app that resides in the repository service. If the web app is an SAP Fiori application, we might have used the Cloud Foundry service "portal" to embed it in an SAP Fiori Launchpad. For this, we needed a
(c) deployer application to upload the configuration file to the service instance bound to the approuter. For most apps, there are also destinations involved which point to the backend systems that provide data. So we also need to
(d) maintain destination in the SAP BTP Cockpit.
a)
https://www.npmjs.com/package/@sap/approuter
b)
https://www.npmjs.com/package/@sap/html5-app-deployer
c)
https://www.npmjs.com/package/@sap/portal-cf-content-deployer
d) <Done manually in the SAP BTP Cockpit>
The architecture of the old approach
For two (b and c) out of these four tasks, we typically leverage the Cloud Foundry tasks, which are Node.js modules. These modules run for a short time and stop once the job is completed. During this time, they consume memory and CPU resources, which has a small (but present) impact on your TCO. The most immense impact on the TCO is caused by the approuter. It runs 24/7 and scales with the complexity and usage of the web app. In many scenarios, developers use even one approuter per web app, which multiplies the TCO.
This also explains why I got asked by multiple developers, whether it's possible to use a single approuter for multiple SAP Fiori apps. In the next section, I will show you something even better: You'll be able to
serve your web apps with NO backend component at all.
Make your web apps "serverless"
The core idea is quite simple: The SAP Fiori Launchpad itself also a web app that is connected to an HTML5 application runtime. The advantage of that service is that it's hosted by SAP for you, so you don't need to manage the HTML5 application runtime, and you can simply consume it.
This post described how you can use the SAP Business Application Studio to create a new SAPUI5 app managed by SAP BTP out-of-the-box.
The URL of your SAP Fiori Launchpad service typically looks like this:
https://<yourID>.launchpad.cfapps.<region>.hana.ondemand.com/site?siteId=<siteID>#Shell-home
We can now reuse the approuter of this application and make our web apps accessible under:
https://<yourID>.launchpad.cfapps.<region>.hana.ondemand.com/<myService>.<appID>-<version>;
As you can see, each web app needs to contain a unique appId, the version, and refer to a service to form this URL - all three values are maintained in the
manifest.json
. If you want to build such a web app "by hand," start with a minimal version of this
manifest.json
file:
{
"_version": "1.12.0",
"sap.app": {
"id": "helloworld",
"applicationVersion": {
"version": "1.0.0"
}
},
"sap.cloud": {
"service": "basic.service"
},
"sap.ui5": {
"dependencies": {
"minUI5Version": "1.65.0"
}
}
}
Besides this, you need to create
service keys for certain Cloud Foundry service instances. These keys are necessary for destinations that are created during the deployment of your application. You can use the following snippet to define the keys and destinations in your
mta.yaml
file.
modules:
- name: hello-world-destination-content
type: com.sap.application.content
build-parameters:
no-source: true
requires:
- name: hello-world_uaa
parameters:
service-key:
name: hello-world_uaa-key
- name: hello-world_html_repo_host
parameters:
service-key:
name: hello-world_html_repo_host-key
- name: hello-world-destination-service
parameters:
content-target: true
parameters:
content:
instance:
existing_destinations_policy: update
destinations:
- Name: my_service_hello_world_html_repo_host
ServiceInstanceName: hello-world_html_repo_host
ServiceKeyName: hello-world_html_repo_host-key
sap.cloud.service: basic.service
- Authentication: OAuth2UserTokenExchange
Name: my_service_uaa_hello_world
ServiceInstanceName: hello-world_uaa
ServiceKeyName: uaa_hello-world-key
sap.cloud.service: basic.service
...
resources:
- name: hello-world-destination-service
type: org.cloudfoundry.managed-service
parameters:
service: destination
service-name: hello-world-destination-service
service-plan: lite
- name: hello-world_html_repo_host
type: org.cloudfoundry.managed-service
parameters:
service: html5-apps-repo
service-plan: app-host
- name: hello-world_uaa
type: org.cloudfoundry.managed-service
parameters:
path: ./xs-security.json
service: xsuaa
service-plan: application
You can now remove the approuter module from your project as you don't need it anymore. This helps you a lot to reduce the TCO footprint of your project. In case you defined routes on the approuter (
router/xs-app.json
), you need to copy these routes to the
xs-app.json
file of your web app. When you do this, you move the route to the URL suffix of the web app. E.g. the URL will be
https://<yourID>.launchpad.cfapps.<region>.hana.ondemand.com/<myService>.<appID>-<version>/route1
instead of
https://<yourID>.launchpad.cfapps.<region>.hana.ondemand.com/route1
To account for this change, you need to switch from absolute to relative URLs in your web app. Most likely you'll need to make changes to the
datasources property in the
manifest.json.
"sap.app": {
"dataSources": {
"mainService": {
// OLD: "uri": "/v2/Northwind/Northwind.svc/",
"uri": "v2/Northwind/Northwind.svc/",
You can find the
entire sample project on GitHub if you want to clone it from there. Once you run the typical build and deploy commands, you will be able to see your web app listed under "HTML5 Applications" on your subaccount.
The cockpit shows a list of all web apps that are available in this subaccount.
Note: This UI is not available in trial accounts at the moment. You will only be able to see this in production accounts!
Click on the application name to open the web app. You'll notice that the URL is different than the URL of typical approuter apps because there is not Cloud Foundry application involved here.
Deploy without a Cloud Foundry task
In the old approach, we used CF modules of type
com.sap.html5.application-content
to zip and upload the static web app resources to the HTML5 Application Repository. In the new approach, we will use the native GACD interface to upload the web application directly into the HTML5 Application Repository. This means
we don't need a Cloud Foundry task for b) any longer, and therefore it won't consume memory and CPU.
For this, we need to make sure the web app is zipped during the build process. This can be achieved in multiple ways, either with a
package from npm or with
this custom task for the
UI5 tooling builder.
Once the zip archive has been created, you just to reference it from the new MTA module:
modules:
- name: hello-world_ui_deployer
type: com.sap.application.content
path: .
requires:
- name: hello-world_html_repo_host
parameters:
content-target: true
build-parameters:
build-result: resources
requires:
- artifacts:
- HTML5Module-content.zip
name: HTML5Module
target-path: resources/
Notice that the module type has been changed and that there is no package.json
file needed anymore. With this, you reduced the footprint of your overall projects a little more.
Expose SAP Fiori apps in the Content Explorer
In this new approach, we won't be using the "developer sandbox" of the Fiori Launchpad (aka the Portal service instance) but the actual management UI that comes with the full Launchpad subscription.
In other words, we don't need the service instance in the mta.yaml
file, and therefore task c) is also out of the game. We just made the footprint of the project a little bit smaller.
Your Fiori app needs to fulfill the following criteria when you want to make it accessible via the Content Explorer:
- Contain an inbound navigation intent in the manifest.
{
"sap.app": {
"crossNavigation": {
"inbounds": {
"fe-inbound": {
"signature": {
"parameters": {},
"additionalParameters": "allowed"
},
"semanticObject": "masterDetail",
"action": "display",
"title": "Available Categories",
"subTitle": "",
"icon": ""
}
- Include a "manifest-bundle.zip" file that includes the manifest.
Redeploy your project after you made these changes. Now, navigate to the management user interface for the Launchpad subscription and go to the
Provider Manager. You should see a content provider named "
HTML5 Apps", use the refresh button to sync with the HTML5 Application Repository. You'll also need to click this button every time when a changed application should be updated on the launchpad site.
The HTML5 Application Repository as a Content Provider
Next, switch over to the
Content Manager and choose the tab
Content Explorer. You are now able to see your Fiori App and to click the "
Add to My Content" button.
The Content Explorer found one SAP Fiori app.
Congrats! You have now removed all Cloud Foundry modules that consume memory and CPU from your project and therefore reduced the footprint of your SAP Fiori app significantly.
The architecture of the new approach
You can find an entire sample Fiori app in our
Multi-Cloud HTML5 Apps Repo on GitHub.
Or you can follow
this tutorial to write new SAP Fiori apps from the scratch with
easy-ui5.
Destinations as code
Infrastructure as code is used to define compute resources in a text file and to provision them automatically when needed. Similarly, this feature can relieve you of configuring the destinations manually in the SAP BTP Cockpit and automating your development flow.
I recently learned that you can define destinations in a JSON file, similar to the
xs-security.json
file that we already know. This feature can be used to declutter the
destinations on the subaccount level and move them to the service instance-level. You only need to make sure that HTML5 runtime is enabled for the service instance and that the update strategy of the destinations is defined in the configuration file:
{
"HTML5Runtime_enabled": true,
"version": "1.0.0",
"init_data": {
"instance": {
"existing_destinations_policy": "update",
"destinations": [
{
"Name": "Northwind",
"Description": "Automatically generated Northwind destination",
"Authentication": "NoAuthentication",
"ProxyType": "Internet",
"Type": "HTTP",
"URL": "https://services.odata.org",
"HTML5.DynamicDestination": true
}
]
}
}
}
Do not add credentials in such a file because then you might accidentally include them in the git history of your repo.
And last but not least, you need to include this JSON file in the resource definition of the destination service instance in the
mta.yaml
file:
resources:
- name: mock-destination
type: org.cloudfoundry.managed-service
parameters:
service-plan: lite
service: destination
path: ./destinations.json
More information on this can be found in the
documentation.
So task d) from above is not needed any longer either. With this, we got rid of all tasks that required manual work or consumed compute resources to deploy a web app. Hurray!
You can find an entire sample Fiori app in our
Multi-Cloud HTML5 Apps Repo on GitHub.
Hands-On
Check out this
video tutorial which will show you how to use the managed application router in SAP Business Application Studio:
Watch it here
Advantages
I already mentioned it a couple of times across this post; the most significant advantage of this approach is the reduced footprint that
saves you money. But this is, by far, not the only benefit. It also
helps you scale your web apps because you won't need to decide how much memory you want to allocate to the approuter. By reducing the included modules, you can
simplify the architecture of the project significantly and, therefore, also reduce the planning overhead when designing such a Fiori App. This simplified architecture also
accelerates the deployment. It now only takes about 30 seconds to deploy a Fiori Application and to create the needed service keys and destinations, whereby it took a few minutes and required manual configuration in the past.
Disadvantages
Even though many reasons speak for the new approach,
there are also a couple of reasons that speak for an approuter that is entirely under your control. One reason might be that you prefer to keep the routes on the approuter-level. Or maybe you want to
configure or
extend the default approuter. It could also make sense to keep the approuter combined with servers written with the
Cloud Application Programming Model.
Performance tip
Requests for
i18n files that return HTTP code 404 will slow down your web app. Many times the locale includes the country while the translation in the i18n doesn’t include the country causing the UI5 fallback and the many redundant 404.
So it is highly recommended to check the console logs and provide the missing translation files, providing also full locale language and country, to avoid the UI5 fallback logic.
For better performance, resource bundles are cached and these redundant calls to the server can be avoided.
Summary
In this post, you have learned:
- that an approuter is no longer needed to host web apps in SAP BTP, Cloud Foundry environment
- how to let SAP BTP manage your web apps
- how to make an SAP Fiori app detectable via the Content Explorer
- how to define destinations with text files
- that it can still make sense to use an approuter