MongoDB, Express, React and Node.js on the AppCloud with Docker
Hi there! Since you are reading this, chances are that you are interested in building and deploying isomorphic/universal web applications on the AppCloud or you would like to know more about the underlying technologies and techniques. If that’s the case then you are on the right spot. In the next few paragraphs, I will show you how to:
- Quickly bootstrap an isomorphic/universal web app
Wrap it into a Docker container without coupling the application to Cloud Foundry VCAP_SERVICES
- Deploy it on the AppCloud
Let us start then …
Bootstrap an Isomorphic/Universal Web App
In order to quickly bootstrap one such application, I will be using MERN a command line, scaffolding tool which uses MongoDB, Express, React, and NodeJS (among other tools & libraries) to get you started .
In case you do not know MERN, here are the 6 steps you need to get you started. The final result of these steps will be a simple blogging platform.
- Install MongoDB (or via Homebrew, in case you are a Mac user) and start it; make sure MongoDB listens on its default port 27017
- Install NodeJS (or via NVM, recommended for frequent NodeJS users)
- Install MERN command line interface via:
npm install -g mern-cli
- Create your blog
mern <YourApp> && cd <YourApp>
- Install Node.js packages
- Start the app
After these steps you should be able to access the app via your favourite browser on: http://localhost:8000
Containerizing Your App with Docker
When developing an application for the AppCloud sooner or later you will want to consume some services (such as MongoDB, RabbitMQ, etc.). When this happens you will essentially face the following question:
How do I connect to this service?
As you probably know, AppCloud is an instance of Cloud Foundry and therefore they (often) share the same documentation. Starting a cloud service (AppCloud docs / Cloud Foundry docs) and binding it to an application (AppCloud docs / Cloud Foundry docs) is pretty well explain in the docs. When using the AppCloud, you can also create and bind services via its UI.
What happens once you have bound a service to an application is that Cloud Foundry (CF) provides a VCAP_SERVICES environmental variable to be used by the app at runtime. This variable contains information (in a JSON format) on how to connect to your service. Here is what it looks like:
Reading further through the docs you will come across on two approaches for using the VCAP_SERVICES. Here is a summary of each of them:
- Develop some logic in your application which parses & makes use of VCAP_SERVICES – the approach requires that you write some non-business oriented code for treating Cloud Foundry specifics which kind of cements your application to Cloud Foundry. What if one day you decide to move to something else?
As a summary, both approaches lead to the same end – tying up your app to Cloud Foundry.
The good news is that we can do better. Looking at the example VCAP_SERVICE on Fig.1, what we want (and need) in order to connect to MongoDB is the URI. As a reminder, the MERN-based project from the previous blog had the following structure:
Inside the “config.js” file there is the following line:
mongoURL: process.env.MONGO_URL || 'mongodb://localhost:27017/mern-starter',
which basically states that if there is a MONGO_URL environment variable then take the URI from there or else use the default one. So, if we manage to extract the URI from the VCAP_SERVICES and put it in a MONGO_URL environment variable then our problem is solved.
We can tackle this in at least two ways, both of which do not involve any dependencies on Cloud Foundry:
- Manually extract and setup the environment variable – this could be done via the AppCloud UI “Set Environment Variable” feature or via a command line and/or Application manifest (for more on this manifest see the docs). Keep in mind though that if the service binding changes you will have to manually update the environment variable
- Build a Docker container which holds a logic for extracting the MongoDB URI from VCAP_SERVICES and exporting it as an environment variable.
First approach has the drawback of a manual work which needs to be performed each time you deploy an app (and therefore does not really scale) and the benefits of being relatively simple and straightforward. The second approach has the reversed characteristics. It’s reusable but it requires some investment in choosing an appropriate tool and setting up the Docker image. This post is designed to resolve the negative side of the second approach, namely giving you a how-to of building such a reusable Docker image.
The below solution revolves around “a lightweight and flexible command-line JSON processor” called jq. This tool allows you to convert the content of VCAP_SERVICES variable into this:
by executing the following command:
echo $(echo $VCAP_SERVICES | jq '.mongodb.credentials.uri') | cut -d "\"" -f 2
Knowing that, what we need is to build an executable bash script (I’ve called it start.sh), with this content:
The file is then placed inside the MERN-based project, as shown in Fig. 4 below:
Once you have that the only piece of the puzzle is the content of the Dockerfile, which is as follows:
Building & Uploading Docker Image to AppCloud
Now that we have the project ready for running and deploying, here is how to proceed:
- Open a terminal and go to the directory containing the Dockerfile
- Build the Docker image by running:
docker build -t swd/mern:1.0 .
- List the Docker images on your machine:
- Tag the image built in step 2:
docker tag <IMAGE_ID> <YOUR_DOCKERHUB_USERNAME>/mern:latest
- Register & login to Docker Hub:
docker login --username=<YOUR_DOCKERHUB_USERNAME>
- Push the Docker image to Docker Hub:
docker push <YOUR_DOCKERHUB_USERNAME>/mern
- Login to AppCloud
cf login -a https://api.lyra-836.appcloud.swisscom.com -u <YOUR_APP_CLOUD_USER>
- Push the Docker image from Docker Hub to AppCloud:
cf push <APP_NAME> -o <YOUR_DOCKERHUB_USERNAME>/mern
You might find it handy to follow the logs by running:
cf logs <APP_NAME>
Do you have experience with ReactJS, MERN, Docker or AppCloud? Your feedback is most welcomed!
- code duplication – good example is pagination functionality. Such a functionality is usually implemented by a front-end and back-end pagination component, yet the goal is the same. Why can’t we just have one software component to build & maintain? Such a component would be an isomorphic one.
- performance – this is linked to the “code duplication” problem. If you ever written any data fetching functionality you most probably have asked yourself why can’t you just fetch the data directly from the DB? Instead, we need to traverse several layers which rarely add anything but validation and connectivity to the database.