How I ended up running a React.js front end and Java back end on Google App Engine.

Leejjon
10 min readMar 7, 2019

If you where just triggered by the title and want to know how I did this, scroll all the way down for the implementation details.

History

Around 2010 most web developers learned to make websites via the many free PHP/MySQL hosting packages out there. In the “enterprise” world, people used Java. If something was simple, you’d use PHP. If something needed to perform, you’d use a Java back end. Oracle and IBM had their big application servers (Oracle WebLogic, IBM WebSphere) that could run on a cluster of servers. They were pretty advanced for their time, you could have several nodes that took over each others work if one went down. However, you needed a license to use them in production. That would cost over 100k. Nobody that hosts a hobby site is going to pay such amounts, so people just learned PHP instead. I admit PHP also was a lot easier to mix in with HTML and create dynamic pages quickly. But when it came to actual programming I didn’t like it due to lack of proper Object Orientation support and the weak typing.

Manual server configuration

As a Java developer I attempted run a server myself. Since I lived at my parents house I was not allowed to let a computer that was using a lot of power run 24/7. So I bought a cheap thin client that came with Windows CE. It had 100 mb ram and no hard drive. A friend and I managed to create a bootable usb drive with the “Damn Small Linux” distribution on it with Java, Apache Tomcat and an Apache Derby database. I started to develop a personal website and learned JSP and JSTL. Thanks to the Java experience at my job, I managed to figure out Servlets, JSP and JSTL despite the steep learning curve. But when I started using the database, the CPU went to 100% and my website wouldn’t load. So I rented a VPS and got things working, but then my hard drive corrupted and I lost all source files. I changed hosting plans and thus lost my server configuration. Since this day I’m using Git for all my projects, and I never ever wanted to do manual server configuration again (obviously still have to do it at work).

The Java Runtime on App Engine

Google’s App Engine was the best option for cheap Java hosting. It was solving problems for me I didn’t even know I would run into them. Despite the limitations in the early versions of App Engine, (a lot of classes in the system library weren’t allowed) I loved it. Especially how easy it was to run multiple versions and rollback if the latest doesn’t work as expected. For my hobby projects I didn’t care about the vendor lock in. At both jobs I’ve had between 2010 and 2019, I tried to convince the companies that I worked for to move to Google App Engine, or any proper modern cloud environment. Sadly they had promised their customers they wouldn’t run anything in the dangerous “cloud”, so I was still doing a lot of troubleshooting on manually configured servers. For my blindpool project I learned how to use JavaScript to do GET and POST calls to my Servlets and thus create a single page application that I hosted on Google App Engine.

Cross platform user interfaces

I have always had problems creating nice UI’s, whether on desktop or the web. While creating my Bluff Poker app for Android and iOS, I tried to use a fully cross platform Java Game framework called LibGDX. The libGDX user interface is mostly a wrapper around OpenGL. With the help of Multi OS Engine I had one code base in Java and managed to ship them to both the Google Play and Apple App stores. LibGDX also had a GWT plugin to get a web version of the user interface rendered, but GWT has been abandoned by Google. I can imagine it was not attractive for non-Java people that started out with plain HTML,CSS and JavaScript. I think that, for that same reason TypeScript seems to be more popular than Dart, as TypeScript looks like a JavaScript extension.

I hate JavaScript, but am learning it anyway

In my opinion, JavaScript is a mess and people that like it are too lazy to learn the rules of a more strict (static typed) language and thus write code that is error prone. It might seem that not having static typing makes a language simpler because you don’t have to bother to define types, but I feel that leaving typing out of the syntax, make it hard for the programmer and others that read the code to figure out what really is happening under the hood. It’s like you put all your stuff randomly somewhere in your room because giving an item its own spot costs time. On the long run, it’s going to be a mess and you will waste time on finding the things you need. Sadly, I see that it’s the only option when creating user interfaces, so I’m currently re-learning it via this free e-book. When I feel confident with JavaScript I will definitely move to something more strict like TypeScript.

Mobile development

I had a hard time to learn the iOS and Android API’s in my spare time. I bought a second hand iPhone SE (€350) I didn’t even use as a daily phone, an Apple Developer license (€99/year) and a Mac Mini (€550) that was five years old just to be able to publish my app. Thankfully these costs will be easier to cover now I have enough experience to do mobile development professionally as a freelancer. I also had some problems with the reviewers at Apple because they didn’t understood the Bluff Poker dice game. I simply only wanted to get it in the store to share it with my friends. I had to make a YouTube video on how to use it, so they would accept the app. After being accepted for a year, Apple kicked my Bluff Poker App out of the store because it was “gambling” related. After some complaints it was suddenly allowed again.

Progressive Web Apps

I learned that React Native developers were able to skip the review process by updating their apps using JavaScript. These apps still have to be allowed to the App Store to load their first version, but from there you can update it instantly as many times as you want. It got even better, Google started to promote “Progressive Web Apps”. Thanks to a concept called service workers, PWA’s can run in the background even when browser tabs are closed. This gives the possibility of offline apps with push notifications and a lot more. The full concept is explained at Google’s PWA website. PWA support was added to Android quickly, and also to Chrome and Firefox on desktops. The big question was whether Apple would build it into Safari on iOS. PWA’s are definitely a threat to their income from the Apple App Store. But in 2018 they did build it into Safari and thus iOS. The support is far from perfect, but you can see an up to date list of what is possible with PWA’s on the different platforms here: https://whatwebcando.today/

The React.js front-end

I had no clue which web framework to pick. I mostly heard about Angular, React and recently Vue. Web frameworks are rising and dying extremely fast and I did not test out all current web frameworks to pick the best one. One of my friends has been using React.js and React Native for the last two years at his company and recommended it. The JSX syntax in React.js has a super easy way to declare dynamic HTML. It does have a learning curve especially if you didn’t use JavaScript for a few years. It’s good that a big company (Facebook) backs the development. There is a simple create-react-app sample on Github that is a PWA out of the box. Using NPM as package manager, I found it pretty easy to start adding existing react components.

I added for example:

  • React routing, to do client-side routing. You want that if a user clicks on a link and your service worker is running, it will handle the request in the client. This means no full reload of the page.
  • Material-ui, a React.js implementation of the Material design by Google. It has great documentation on their site.
  • AliBaba’s react-intl-universal module to add multi language support.
  • A flag icon library called react-flag-icon-css, because we don’t want to create flag icons by ourselves.

Sample package.json:

"dependencies": {
"@material-ui/core": "^4.0.0-alpha.0",
"@material-ui/icons": "~4.0.0-alpha.0",
"@material-ui/styles": "^4.0.0-alpha.0",
"express": "4.16.4",
"react-flag-icon-css": "^1.0.25",
"react-intl-universal": "^1.15.5",
"react-router-dom": "^4.3.1",
"typeface-roboto": "0.0.54",
"webfontloader": "^1.6.28"
},

Now copy and pasting material-ui samples to get fancy UI components was easy, but getting them to display the correct information was not. Be sure to read up on how to pass props through components and to work with state in React.

How to host the front end in App Engine?

My first attempt to host the React.js front end was to try and fit it in my Java back end project that was build by maven, to see if it could serve the static front end files. After a little research I came to the conclusion that this is not what I wanted. Here is why:

  • It’s difficult to merge a React.js project built with NPM and a Java back end project built with Maven into one deploy-able package.
  • You want to be able to update the front and back ends separately. If the back end crashes, you don’t want the front end to be unreachable too.
  • You can build advanced caching mechanisms in the Java back end to host static files efficiently. But there are other ways to host static files very efficient, so why would you want to build that in your Java back end. Don’t reinvent the wheel.

Google recommends to use microservices within an App engine project instead of building one monolith that serves all requests. So I’m going to have a front end service that uses the Node.js runtime and a back end service that uses the Java runtime.

There are a lot of ways to host front ends on Google Cloud alone, yet it’s not just hosting static files. What you want is that whatever page is loaded, it will just load the static frontend and have the react components do rest calls to the backend to retrieve the data they need to display in the page.

Example of serving a static React.js front end that uses clientside routing with Express.js

Express.js is a web server for node.js. I use it just to serve the static frontend files you get when running `npm run build`. Maybe you think that using the node.js runtime in app engine is overkill for static files. However, there is some logic needed to make sure that any url that are not static files will serve you the index. html of the front end. Below is the code that is using the express.js framework to serve static content:

// [START app]
'use strict';

const express = require('express');
const path = require('path');
const app = express();

/* This code makes sure that every request that matches a static file in the build folder, it serves that file. */
app.use(express.static(path.join(__dirname, 'build')));


/* This code makes sure that any request that does not matches a static file in the build folder, will just serve index.html. Client side routing is going to make sure that the correct content will be loaded from there. */
app.get('/*', function (req, res) {
res.sendFile(path.join(__dirname, 'build', 'index.html'));
});

// Start the server
const PORT = process.env.PORT || 8080;
app.listen(PORT, () => {
console.log(`App listening on port ${PORT}`);
console.log('Press Ctrl+C to quit.');
});
// [END app]

All urls(besides urls that match existing static files) will serve the index.html. For example:

Be careful with relative links in your index.html. Make any links to js files point at the root of the domain to avoid the entry path from constructing a wrong url (https://example.com/css/style.css is good, https://example.com/some/category/css/style.css won’t work).

Serving the front end and back end from two different services on the same domain

Both the frontend and backend services have a file to configure the App Engine runtime. For the frontend on the Node.js runtime, this is an app.yaml file (I deploy the node.js project using `gcloud app deploy` in the folder where app.yaml is located):

runtime: nodejs8

# Leejjon: I first wanted to call this service "frontend", but having a default service seems to be required.
# Source: https://stackoverflow.com/questions/42360790/why-do-i-need-to-deploy-a-default-app-before-i-can-deploy-multiple-services-in/42361987#42361987
service: default
env: standard
instance_class: F1
# First handler is for Google Site verification. Second is for
# redirecting everything else to the node.js app.
handlers:
- url: /googleb39dfeb792153037\.html
static_files: build/googleb39dfeb792153037.html
upload: build/googleb39dfeb792153037\.html
secure: always
- url: '.*'
script
: auto
secure: always
automatic_scaling:
min_idle_instances: automatic
max_idle_instances: automatic
min_pending_latency: automatic
max_pending_latency: automatic

For the Java runtime, there has to be a file called web-appengine.xml in the style of typical web.xml files found in Java web applications. In here I specify that the service is called “backend” (I deploy the Java backend using `mvn appengine:deploy`):

<?xml version="1.0" encoding="utf-8"?>
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
<threadsafe>true</threadsafe>
<runtime>java8</runtime>
<sessions-enabled>true</sessions-enabled>
<service>backend</service>
</appengine-web-app>

NOTE: This web-appengine.xml file is deprecated. You also need an app.yaml file now since the java 11 runtime and later.

To let Google’s load balancer decide what micro service needs to handle what request, you can define a dispatch.yaml file to overwrite App Engine’s default routing rules. My dispatch.yaml file looks like this:

# Rules for dispatch file...
# Put specific cases at the top.
dispatch:
# Route the urls that point to the Java backend's API calls
- url: "*/api/v1/*"
service
: backend
# Route all other urls to the React.js frontend
- url: "*/*"
service
: default

You can deploy your dispatch.yaml file with `gcloud app deploy dispatch.yaml`.

On the Google Cloud console this configuration will show up like:

The dispatch routes on both services.

You can probably make it a little more efficient by marking the static JS and CSS files as static files in the app.yaml. Then Google will use some special static file hosting service to serve them, and thus your node.js runtime will not be bothered with them. I will bother with that once I have so many users that optimizing will actually save me money.

The code of my blindpool project can be found here. It might change a lot.

--

--

Leejjon

Java/TypeScript Developer. Interested in web/mobile/backend/database/cloud. Freelancing, only interested in job offers from employers directly. No middle men.