Creating a basic CRUD frontend with React :

A big issue for our React application is that React sets process.env values at buildtime unlike our frontend which sets process.env at runtime. This is problematic as it means that we cannot set configuration values at runtime making our application non-portable as we have no runtime configuration. To get around this we pass in values using a JavaScript file which is created at runtime using a shell script. I used an implementation of this which was created by kunokdev (many thanks) which can be found here (https://github.com/kunokdev/cra-runtime-environment-variables) along with a thorough explanation. These are the tl;dr notes I made to assist in getting understanding and getting this working quickly:

env.sh is used to allow environment variables to be used at runtime in the React app since React sets process.env values as buildtime. env.sh will allow use of env variables using the format window.env.ENV_VAR_NAME in your React app. env.sh creates a env-config.js file populated with by env-variable info which must be run so that the browser has env variables. ex. place <script src="/env-config.js"></script> at top of index.html env.sh will ONLY use the env variables specified in the .env file. The .env file must have default values, which will be overwritten by the system's/container's environment variables.

To create a basic CRUD frontend with React we create a React app with four components which allow us to create, read, update, and delete (CRUD) using our backend, and also provide the means of navigating to each of these components. We use React’s ‘BrowserRouter’, ‘Route’ and ‘Switch’ to allow a user to see a specific component when visiting a specific path in their URL.

We use Material-UI to help create our components due to its ease of use and solid documentation. In the views/files AddUser.js, UpdateUser.js, and DeleteUser.js we basically have the same component which contact different API endpoints on our backend depending on their intended function. In these files, we have a event, ‘handleInputChange’ which updates a state variable value when an input field is altered, and an event, ‘onSubmit’ which sends the information from our input fields to our backend. We also have links to other locations on our frontend.

AddUser.js. UpdateUser.js and DeleteUser.js is at https://gitlab.com/Anonias/pern_fullstack_gitlabcicd

In our component/file ‘AllUsers.js’ we retrieve all users and their information from our backend using ‘componentDidMount()’ which is the function called when a component is created in React which is then displayed by generating a Material-UI List. We again also have links to other components.

Here we have a fairly minimal index.js which basically wraps around our App component and names it ‘client’ which allows webpack, a module bundler which uses wizardry to combine all of our modules/packages and JavaScript files into a single JavaScript file (which is/was needed by browsers to work and lowers bandwidth required), to know that it is the element with id ‘client’ in our index.html.

Once again, we have a package.json file to manage dependencies, scripts, and some other metadata. An important note to make is that we have a ‘start’ script here which launches our frontend server using ‘webpack-dev-server’ on at localhost/0.0.0.0, and also has a ‘build’ script which specifies that webpack be used. A nice feature of using ‘webpack-dev-server’ is that it allows hot-reloading which means that we can edit any JavaScript files in our frontend and the frontend will be automatically updated/reloaded to reflect changes while it is running. As an aside, it is also worth noting that, ‘Babel’ is a tool used with webpack to compile down modern JavaScript into JavaScript compatible with older browsers.

We also have a webpack.config.js which has configuration values used by webpack to work its wizardry.

Containerizing our frontend and backend w/Docker:

We have some pretty simple dockerfiles which we use to create Docker containers for our frontend and backend. Using the base image ‘node’, we download packages/modules using ‘npm install’, build our apps, and expose/open the ports needed by our servers. We then set the command to needed to run our servers which will be run when the container is started.

backend Dockerfile

frontend Dockerfile

Putting it all together with Docker-Compose:

According to the official Docker docs at https://docs.docker.com/compose/,

Docker-Compose is a tool for defining and running multi-container Docker applications. With Compose, you use a YAML file to configure your application’s services. Then, with a single command, you create and start all the services from your configuration.

Below is a heavily annotated docker-compose.yml which should allow you to see how Docker-Compose is used to run our multi-container application. Some information provided pertains to Kubernetes and may be useful to your understanding later.

Note that Docker-Compose populates each container with environment variables from a file which we use for configuring our application. An example of such a config file is below.

Other .env files located at https://gitlab.com/Anonias/pern_fullstack_gitlabcicd

To see how files are structured, visit https://gitlab.com/Anonias/pern_fullstack_gitlabcicd.

Conclusion

Congratulations, at this point you should have a CRUD PERN webapp with hot-reloading running on containers setup through Docker-Compose. Next steps are to get this working with Gitlab CICD, Google Cloud Platform, Kubernetes, and Helm.

Part 2 coming soon.

EDIT: Part 2 is not coming as I’m busy (and ran out of GCP credits).

Feel free to look at https://gitlab.com/Anonias/gmail_aggregator_webapp which accomplishes the mentioned goals without the step-by-step writeup.