In this SAP Tech Byte I will describe how to run a UI5 app with an express server and deploy it to the Cloud Foundry environment. You might wonder why one should use express to serve a UI5 app instead of using the UI5 Tooling (e.g. running "ui5 serve", which also runs express under the hood by the way). One reason for this might be that you want to handle more endpoints within your app and execute something server-side, e.g. a file upload. We recently came across this situation in the SAP Community Code Challenge, which inspired me to write this blog post.
Initializing the UI5 app
A great tool for scaffolding UI5 apps is the
easy-ui5 generator. Let's open a terminal and install it globally so we have it set up for future occasions as well:
npm install -g yo generator-easy-ui5
After installing it, we can initialize our project:
yo easy-ui5 project
In the dialog you want to make the following selections (you can choose a different name for your project, of course):
Open the newly created directory in your favourite editor.
Setting up the express server
First, we want to install
express and another package called
dotenv, which we will need later:
npm install express dotenv
Now we can create a new
index.js at root level of our project and insert the following code:
const express = require("express");
const path = require("path");
const app = express();
app.use("/", express.static(path.join(__dirname, "./uimodule/webapp")));
const port = process.env.PORT || 8000;
app.listen(port, () => console.log(`Server started at http://localhost:${port}`));
This spins up an express server and statically serves the files in
uimodule/webapp for incoming reguests.
The new start command for our app is
node index.js.
Because the Cloud Foundry environment always wants to start node.js apps with the command
npm start (and also because it's good practice), let's replace the current start script in our
package.json with our new start command:
...
"scripts": {
"start": "node index.js",
...
}
...
This leaves us with a problem. You might have noticed that we just deleted the
ui5 serve command, which picks up the configuration defined in the
ui5.yaml file, such as which framework and libraries we use. This means the
index.html in our UI5 app will now have to work "standalone". In order to achieve that, we have to modify the bootstrapping of our app. In our
index.html, we insert the link to the sap-ui-core CDN, instead of linking to local resources:
<script
id="sap-ui-bootstrap"
src="https://openui5.hana.ondemand.com/resources/sap-ui-core.js"
...
</script>
We can now start our app:
npm start
Our app should now be reachable at http://localhost:8000:
Preparing the deployment to Cloud Foundry
Before pushing our app to Cloud Foundry, we want to build it. Let's do that now:
npm run build:ui
The build created a new folder
uimodule/dist as it is specified in the
build:ui script in our
package.json. In our
index.js we have told our express server to statically serve all files inside the
uimodule/webapp folder for incoming requests. But after the build and running in the cloud, we want to serve the files from
uimodule/dist. This means our express server needs to know whether it is running in development or not and needs to behave differently based on that condition. This is where the
dotenv package comes into play, which we installed in the beginning. The
dotenv package picks up whatever we put in a
.env file. So let's create this file and insert the following code.
NODE_ENV="development"
We have now hardcoded the node environment to be "development". In order to not push this information to Cloud Foundry, we create yet another file, called
.cfignore. In there, we list all the files and folders we don't want to deploy:
.env
uimodule/webapp/ #not needed, as we have our build result in uimodule/dist/
node_modules/ #cloud foundry will run another npm install for us
Now we can go into our
index.js file and import the
dotenv package as well as insert the condition that checks for the node environment the express server is running in:
const express = require("express");
require("dotenv").config();
const path = require("path");
const app = express();
app.use(express.json());
if (process.env.NODE_ENV === "development") {
console.log("Running in development...");
app.use("/", express.static(path.join(__dirname, "./uimodule/webapp")));
} else {
app.use("/", express.static(path.join(__dirname, "./uimodule/dist")));
}
app.get("/another-endpoint", function(req, res) {
res.send({
message: "Hi there!"
});
});
const port = process.env.PORT || 8000;
app.listen(port, () => console.log(`Server started at http://localhost:${port}`));
During development the
dotenv package will pick up the
.env file and set the
NODE_ENV to
development. In this case the folder
uimodule/webapp will be served. But running in Cloud Foundry, the
.env won't exist and therefore the
NODE_ENV will be undefined. In that case the
uimodule/dist will be served.
Notice how we also made our express server capable of handling JSON (line 6) and how we added
/another-endpoint to our backend. Feel free to execute whatever piece of code you wish inside this function. Simply call "/another-endpoint" from within your app and let the express server do the work.
Deploying to Cloud Foundry
We want to deploy our app to Cloud Foundry using the convenient
cf push command. For that, we need a
manifest.yaml file on root level of our project with minimal deployment configuration:
---
applications:
- name: ui5express
buildpacks:
- nodejs_buildpack
memory: 512M
After the deployment is finished, we should see our app successfully running in the cloud (you can find the URL in the logs of the deployment):
Trying it yourself
There’s a branch for this SAP Tech Bytes post in the accompanying
SAP Tech Bytes repository: Check it out and follow the instructions here:
https://github.com/SAP-samples/sap-tech-bytes/tree/2022-02-18-ui5-express-cf
SAP Tech Bytes is an initiative to bring you bite-sized information on all manner of topics, in
video and
written format. Enjoy!