Skip to Content
Technical Articles
Author's profile photo Marius Obert

CloudFoundryFun #5 – Secure React Applications on SAP Cloud Platform

In this fifth post in my CloudFoundryFun series, I will show how to deploy web apps, written with any UI framework, to Cloud Foundry. For this, I deliberately didn’t choose a SAPUI5 app, but a great arcade game that has been coded with Facebook’s React.js. As I don’t want anyone to play this game, I secured it with the SAP Cloud Platform Identity Provider (IdP). 

Play Asteroids powered by React, secured by SAP Cloud Platform

Nowadays in the cloud era, web interfaces are the de-facto standard interface for all kinds of applications. It’s hard to think of cloud applications not having a web interface. Even “mobile-first apps” like WhatsApp have a very successful web interface, the same holds for classical “desktop” apps like Excel Online.

We didn’t get here by chance! Web technologies are easy to learn and well standardized. In spite of all that standardization, web technologies still offer a lot of flexibility. This flexibility is appreciated by a large ecosystem of developers and laid the groundwork for a myriad of UI frameworks such as OpenUI5 or React. When we think of web technologies, we often think of frontend UI frameworks. We tend to forget the backend technologies, which we need for distinct tasks. This is why I dedicated this blog post to a service that supports the backend part of your apps.

Frontend Apps vs Backend Services

Frontend frameworks usually help us to bootstrap the app and to render the HTML DOM, e.g. less heavy computational tasks. In contrast to that, the backend technologies handle two types of tasks. Computational heavy ones, e.g. crunching your latest numbers, and lightweight tasks, e.g. serving the static files of your apps. It isn’t easy to scale your server for both types of tasks. Depending on which tasks outweigh the other, the backend server has a different resource consumption. You can see the different resource consumption patterns reflected in your monthly bill. This is why SAP offers the HTML5 Application Repository service.

I know what you might think now: “Why do I need this service to serve static files. Can’t I just use a simple HTTP server to host my web apps?“. And I would say you are completely right. This solution can be implemented within a very short time frame and works very well for simple scenarios. For more complex scenarios, though, it will get a little bit more difficult as you might face the following requirements:

Security – Your server needs to check whether the UI files should actually be delivered to the users. This means you would need to implement a security check or possibly even implement an OAuth flow.

Integration – In almost all cases, a UI comes with the mentioned computational heavy backend tasks. These requests need to be served from the same server as the static content to follow the same-origin policy of your browser. So you need to take care of the integration of both parts of your app.

Scaling – As your backend code grows and gets more complex, you might require more computational power. How can you scale both backend tasks, serving static content and the execution of the backend logic, at the same time?

Extensibility – You might want to share UI libs between different apps. How can you access the UI files from a different project or even from a different Cloud Foundry space?

Caching – This is very important, especially for apps that are requested frequently. Smart caching can help you to scale out later and to save actual money!

All of these aspects require a modification of your standard web server and make the simple scenario more complex. You wouldn’t want to implement all these requirements, especially since the app router already implements them. Plus, it integrates perfectly with the HTML5 App Repository service. You want your files to be served in a cloud-native way. In most cases, you don’t care about the how.  And this is exactly why you want to use the HTML5 App Repository service.

Note: If you have purchased the Application Runtime, you already have purchased this service too. 

How to use the HTML5 Application Repository

Using this service is very easy. You just need to push your prepared (aka uglified) web app to the HTML5 App Repo. This can be done in one of these two ways:

  • You leverage the upload script of the npm package @sap/html5-app-deployer which can be integrated into your MultiTarget Application (MTA) deployment flow. You might have already seen this script in action, this is what the Web IDE does for you under the hood.
  • You use the HTML5 Application Repository plugin for the Cloud Foundry CLI. With this plugin, you can run the following command to push your web app to the repository. This is the approach we’re going to use later.
    cf html5-push

As said before, most of the required functionality is already implemented in the app router which is available via SAP’s npm repository. The app router can integrate apps from the repository seamlessly. You need to complete these two steps to integrate the HTML5 repository into your app router:

  1. Bind the app router to the HTML5 App Repository via service binding
  2. Add a route in the xs-app.json config file to tell the app router that ingress traffic should be redirected to the repository.
    "routes": [{
        "source": "^(.*)$",
        "target": "$1",
        "service": "html5-apps-repo-rt"
    }]

Hands-on: Deploy Reacteroids on SAP Cloud Platform

For this hands-on, I picked a very popular arcade game that has been reimplemented in React: Reacteroids. ☄️(React is a very popular JavaScript framework for building user interfaces by Facebook.)

Preparation

Before we get to the fun part, we need to install some tools which are mandatory for cloud development on SAP Cloud Platform (if you haven’t done so already):

Publish the app

  1. Clone and setup the Reacteroids repo
    git clone https://github.com/chriz001/Reacteroids​
    cd Reacteroids/
    npm install
  2. Build the project, e.g. uglify the source code and move all required files into the “dist” directory
    npm run build​
  3. To bootstrap the React app, add an index.html file to the “dist” folder with the following content
    <!doctype html>
    <html>
      <head>
        <title>Reacteroids on SAP Cloud Platform</title>
      </head>
      <body>
        <div id="root">
        </div>
        <script src="bundle.js"></script>
      </body>
    </html>​
  4. Define the welcome file and the routes. You might already know this file if you’ve used the app router before. Create the file dist/xs-app.json​:
    {
      "welcomeFile": "/index.html",
      "routes":[{ 
        "source":"^(.*)", 
        "target": "$1",
        "service": "html5-apps-repo-rt" 
      }]
    }
  5. Since this is a React app and not an OpenUI5 app, we need to add a manifest.json file manually. This file contains the name of the application which will translate to the URL path later. Create the file dist/manifest.json​:
    {
      "sap.app": {
        "id":"Reacteroids",
        "applicationVersion":{
          "version": "1.0.0"
        }
      }
    }
  6. Push the html5 app to the cloud platform
    cf html5-push​

    Note: This command will create a new service instance with a generated name and 2MB of storage. If you want to use a custom name/size please use:

    cf create-service html5-apps-repo app-host myRepoHost -c '{\"sizeLimit\": 5}'
    cf html5-push -n myRepoHost

    (thanks for the hint David Best)

This is all you have to do to deploy your React app! 

Deploy the App router

Next, we need to deploy an instance of the app router in case you don’t have one already running. As of today, version 8.5.0 is the most recent version. As this blog post isn’t about deploying app routers, we will leverage a pre-built app router from the new SAP-Samples GitHub account. (Btw: I recommend that you follow this brand new GitHub account.)

  1. Clone the repo

    git clone https://github.com/SAP-samples/multi-cloud-html5-apps-samples
    cd multi-cloud-html5-apps-samples/standalone-approuter-html5-runtime
    
  2. Modify the router/xs-app.json file to redirect all incoming traffic to our application in the repository
    {
      "welcomeFile": "/Reacteroids/index.html",
      "authenticationMethod": "none"
    }​
  3. Build the app router
    npm install
    npm run build
  4. Log in to the Cloud Foundry space in your region (here eu10)
    cf login -a https://api.cf.eu10.hana.ondemand.com​
  5. Deploy the prepared app router

    cf deploy mta_archives/approuter_html5_1.0.0.mtar

    While the app router is being deployed, we have the chance to inspect it more closely.

    package.json

    You’ll notice that this file is very minimalistic. It only defines the name of the package and one single dependency, which it executes in the start script. During this startup, it will parse the content of the xs-app.json file.

    xs-app.json

    This file is a little bit larger. In the first line, we define the welcomeFile, which is the default path the user should be redirected to. Note that “Reacteroids” is the name we choose in the manifest.json file above. Further, we define that all incoming requests should be redirected to the HTML5 App Repository runtime (rt) service.

    mta.yaml

    The app descriptor defines our app router application and two services including the service bindings.

  6. Access the URL you see in the console once the command has finished successfully. Now there’s only one thing to do:

 

Sign in with your SAP credentials and have some (CloudFoundry-)Fun! (Hint: the larger your browser window, the easier is the game)

In the next step, we could write a simple backend service that saves the scores in a database. This would just require a small modification of the React source code and the addition of a tiny backend service. The app router already enforces that all users are already logged in, this means the backend service could easily extract the user name easily from the attached JWT token.

Do you have more extension ideas already in the back of your mind? Let me know in the comments!

Summary

In this edition you have learned:

  • How to use the HTML5 App Repository to securely access UI apps across Cloud Foundry Spaces
  • What the Cloud Foundry plugin “HTML5 Repo” is for and how to use it
  • That the HTML5 App Repository service is not only for SAPUI5 and OpenUI5
  • How to serve web apps in SAP Cloud Platform in a secure and scalable way
  • That arcade games are (still) fun

Further Reading: Official SAP Help documentation

About this series

This was the fifth blog post of my monthly series #CloudFoundryFun. The name already says all there is to it, this series won’t be about building enterprise apps on Cloud Foundry. I think there are already plenty of great posts about those aspects out there. This series rather thinks outside the box and demonstrates unconventional Cloud Foundry use-cases.

CloudFoundryFun #6 – Run TypeScript Apps on Cloud Foundry

 

Assigned Tags

      28 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Jaehoon Chung
      Jaehoon Chung

      Hi Marius,

      Thank you for writing this comprehensive blog:)

       

      I am currently facing issue on step 6, when publishing the HTML5 app.

      When I run the "cf html5-push" command, I am getting the error blow.

      Could not upload applications to app-host-id '3b710d02-517e-4f97-8b16-0f7031af5c8c' :  maximum file length exceeded

      Is this an issue with my insufficient HTML5 App Repo quota?

      If so, how much App Runtime and HTML5 app-host quota is needed to upload this application?

      Best regards

      Author's profile photo Marius Obert
      Marius Obert
      Blog Post Author

      I'm glad you liked the post!

      I assume your entire project is larger than 2 MB on your disk. To fix this, you can either try to reduce the project size (smaller images/remove the webpack map files) or by extending the size of the repository.

      The app-host service instance defaults to 2(MB) when you don't specify the size during the "cf create-service" command:

      cf create-service html5-apps-repo app-host myRepoHost -c {"sizeLimit": 5}

      Alternatively, you can specify the size in the MTA.yaml descriptor.

       - name: uideployer_html5_repo_host
         parameters:
            service-plan: app-host
            service: html5-apps-repo
            config: sizeLimit: 5

      More info on sizing/html5 repo

      Author's profile photo David Best
      David Best

      I was unable to upload using 'cf html5-push', even after creating an instance of the html5-apps-repo service.  When I ran 'cf html5-push', a new instance of the service was created with a name like: app-host-1581972596. The new instance still had the to the 2MB limit, so the push failed.

      I was able to push the content with the following commands (Windows OS):

      cf create-service html5-apps-repo app-host myRepoHost -c '{\"sizeLimit\": 5}'
      
      cf html5-push -n myRepoHost
      Author's profile photo Marius Obert
      Marius Obert
      Blog Post Author

      Hi David,

      sorry for the confusion. Yes, if you don't specify the name of the service instance, it will create a new one with 2MB (the default size).

      If you want to create an instance with a custom name/size, you need to do exactly what you mentioned.

      I'll update the post to reflect that too.

      Thanks

      Author's profile photo Eric Solberg
      Eric Solberg

      Hmmm.... when I access the app URL I'm getting 404 Not found.

      Author's profile photo Marius Obert
      Marius Obert
      Blog Post Author

      That is very strange. If the app has been deployed successfully you should get at very least a 5XX error. 404 means the webapp doesn't even exist

      Author's profile photo Eric Solberg
      Eric Solberg

      Thanks for the reply... I've double checked that I'm following all the steps (and did deploy the web app). I'm getting up to speed on CF so there are parts here that aren't clear to me- it could very well be my error. But it seems to me the instructions on the app router here are missing something. For example, the make command didn't work. But I followed the directions in the README for installing the approuter, and these did work. In xs-app.json it specifies a service name of "html5-apps-repo-rt" - but this isn't the name of the app repo service that was deployed. But I tried different names (redeploying app router each time) including "html5-apps-repo" and "html5-app-repo" and it didn't seem to make a difference. 

      I'll keep playing with it- I am going through a learning curve. Thanks!

      Author's profile photo Eric Solberg
      Eric Solberg

      Still no luck here. In the README for the app router, the very first requirement is:
      Create an SAP Cloud Platform trial account in the region Europe (Frankfurt)

      Perhaps this is the problem, as my CF trial is running in US East (us10). I'm not sure what in this router is specific to Frankfurt though... still trying to figure it out.

      Author's profile photo Marius Obert
      Marius Obert
      Blog Post Author

      Thanks again for your feedback, I've updated the blog post accordingly.

      Author's profile photo David Best
      David Best

      I got an error when running the make command to build the approuter mtar file.  This github issue discussion helped me work past the error:  https://github.com/SAP-samples/html5-app-repo-router/issues/2

      I was able to build the MTA file using this command instead of running the make command:

      mbt build -p=cf
      Author's profile photo Marius Obert
      Marius Obert
      Blog Post Author

      This sounds really strange. Are you using the most recent version of the mbt module? You can update the version by changing the version string to 1.0.9 in the package.json and run "npm install" again.

       

      If so, It would be great if you could open an issue on GitHub for this.

      Author's profile photo Frank Siegel
      Frank Siegel

      Hi Marius,

      Great post. Is it somehow possible to get rid of the "/Reacteroids" part in the url? So instead of https://<hostname>/Reacteroids/index.html calling the app via https://<hostname>/index.html

      Thanks & Regards,
      Frank

      Author's profile photo Marius Obert
      Marius Obert
      Blog Post Author

      Hi Frank,

      a great question! Yes, this is also possible. For this, you need to change the router/xs-app.json file to

      {
        "welcomeFile": "/index.html",
        "authenticationMethod": "route",
        "routes": [
          {
            "source": "^(.*)",
            "target": "Reacteroids/$1",
            "service": "html5-apps-repo-rt"
          }
        ]
      }
      
      Author's profile photo Philipp Steinroetter
      Philipp Steinroetter

      Hi Marius,

       

      thanks for the blog!

       

      I am currently trying to set up a basic MTA on CF with a node backend, react frontend, and the app router. I am struggling with defining the mta.yaml, especially regarding the build process. By any chance, do you have a reference for something like that? I can't find one...

       

      Thanks
      Philipp

      Author's profile photo Marius Obert
      Marius Obert
      Blog Post Author

      Hi Philipp,

      I understand that it can be hard to set up the mta.yaml for such a scenario. I did write a post about using a TypeScript server in an MTA project, this might help you as there is also a custom build step involved.

      Maybe the github page of the mbt tool can also help you if you need more configuration parameters.

      Author's profile photo Steve Bannister
      Steve Bannister

      Hi Marius,

      Great blog post!

      Is there any advantage to placing static content in the html5 repo, as opposed to placing it within the approuter module itself?

      Thanks,    Steve

      Author's profile photo Marius Obert
      Marius Obert
      Blog Post Author

      Hi Steve,

      this is a very interesting question ?.

      When you embed the static content in the application router, you connect the life cycles of these two components (approuter and web app) directly. This means that your entire web app will be down when you deploy a UI update (assuming you don’t use blue-green deployments). I actually talked about this topic during UI5con Belgium earlier this year, here’s the recording of that session.

      On top of that: With the brand new “central FLP”, you don’t need an approuter any longer. Therefore, you don’t need to run and scale the app router at all. You can actually run your static content serverless then. More information about this is to come ?

      Author's profile photo Marius Obert
      Marius Obert
      Blog Post Author

      Hi,

       

      I just published another blog post that will talk more about that "central FLP" aka "managed HTML5 runtime". It will also explain why it is beneficial to use the HTML5 application repository.

      Author's profile photo Gautam Krishnan
      Gautam Krishnan

      Hi Marius,

      I am facing an issue while deploying the application to Cloud Foundry.

      Steps I performed.

      1. pushed the application to SAP HTML5 Application repository.
      2. created an .mtar file for the application.
      3. while deploying the .mtar file to SAP Cloud Foundry, I am facing “resources folder does not exist error”.

      Not sure if I am following the correct process, but my end-goal is to deploy my react app into CF as a MTA  project.

       

      Please find attached snapshot of project structure and the error I'm getting. The project folder is "my-app".

      Author's profile photo Marius Obert
      Marius Obert
      Blog Post Author

      Hi Gautam,

      that means that the deployer module has an issue. The folder structure should be as follows

      deployer

      -> resources

      -> webapp1

      -> webapp2

       

      Can you check if this is the structure inside your project?

      Author's profile photo B. Deterd
      B. Deterd

      Can i deploy a non-ui5 app also with a managed approuter ?

      (including a connectivity service to a sap onpremise system )

      Author's profile photo Marius Obert
      Marius Obert
      Blog Post Author

      Good question. Yes, that works too. You can have a look at the basic sample here which is also a non-ui5 app.

      Author's profile photo B. Deterd
      B. Deterd

      Yes. I got this working

      One thing is not clear to me.

      I never defined a connectivity service in my mta.yaml file but the app is able to connect to an onpremise sap system. Is the connectivity service already part of the managed approuter ?

       

      Author's profile photo Marius Obert
      Marius Obert
      Blog Post Author

      Correct 🙂

      Author's profile photo B. Deterd
      B. Deterd

      Hi Marius,

       

      One last question? ?

      My app works fine but the file xs-app.json is visible for every logged-in user
      (https://<host>/xs-app.json)

      That does not feel right because the user can see all the internal routes I defined

      How can I prevent the visibility of xs-app.json?

       

       

       

      Author's profile photo Marius Obert
      Marius Obert
      Blog Post Author

      Another very good question! Tbh, I never thought of that. I agree that it seems a little bit strange at first but I'm not sure if that file needs to be hidden.

      It doesn't contain sensitive information and adversaries could find out how the routing works if they carefully watch the network trace with the developer tools of the browser.

      Can you please explain which information you are concerned about?

      Author's profile photo B. Deterd
      B. Deterd

      I agree it does not contain sensitive information.
      It just seems a bit odd to have a configuration file exposed to every user

      I am struggling to get the name/email/id of the logged-in user displayed when I encountered this xs-app.json in runtime.

       

      Btw How do I get some userinfo from the managed approuter ?

      There should be a /getuserinfo service ?

       

      Sorry for all the questions

      Author's profile photo Marius Obert
      Marius Obert
      Blog Post Author

      No worries 🙂

      This is actually a quite common question. There is no such service per se but you can easily build one as instructed here and here. In case you are implementing an SAP Fiori app, you can also use this function to retrieve the user info.