Building a React App that connects to MySQL via NodeJS using Docker

The day I thought of giving web development an actual chance and decided to get good at it, I managed to create a responsive site with a simple login system that could pass as a very simple social network of sorts (minus instant messaging). While I was proud of it, little did I know the technologies I used were on the way out; ES5, jQuery, vanilla CSS and mySQLi, while good, could not offer the flexibility that more modern languages would.

After I landed an internship to work in a reputable team, I realized the skills that made me the most knowledgeable of my peers relegated me to the rightful place at which I would only ask for help, learn and adapt to what at first seemed like mountains of code. Little by little, struggle by struggle, my skills adapted and as the internship ended I realized the best way to prove to myself that I could truly call what I had learned my own would be to mix all of that into renovating my website.

Although the front-end development and styling would become a sort of guilty pleasure, I had yet to realize the back-end would present a few more bumps than I was ready to overcome. While true, I already had practical knowledge of PHP to access databases, the way I choose to develop before involved use of FTP; this limited me to a development experience that only included a production environment.

Rather than meshing a bunch of processes to have them run together, I was told — and rather encouraged — by friends to use the oh-so-popular Docker. While I had brief encounters with it in the past, its development was almost fully handled by my seniors, and thus I had little experience with it. But one has to sacrifice and bet what he does not have in order to make the dream of attaining what one could get a reality.

Below is the combined effort of a week of reading, experimenting, and troubleshooting, in which I have made proper use of several tutorials in here and the rest of the net to create a simple React application that connects to a NodeJS server, which in turn connects to a MySQL image to retrieve database information, thereby sending it to the React app that acts as a client.

What you will get from this tutorial: The app that will be created in this tutorial includes use of stateless components (to try and adhere to latest practices) and multiple docker images working together to form a single application.

What you need to (mostly) know: Basic knowledge of ES6, latest React code practices, NodeJS, and docker commands for image building.

The tutorial: We will be using React as client, NodeJS as server, and MySQL for database control. And, although I will explicitly specify what folder and where we’ll be working for each section, each of the steps and sections done here will be numbered to facilitate troubleshooting.

With that, let’s start coding!

I. Root files

  1. Create a folder to contain the whole app. For the purposes of the example, I named mine react-node-mysql-docker-boilerplate.

Note that there are extension fields declared for MYSQL_USER, MYSQL_PASSWORD, MYSQL_DATABASE, and REACT_APP_SERVER_PORT, which will be reused by the services to avoid writing variables over and over. (Note the importance of REACT_APP_SERVER_PORT, as this one is declared this way to be able to use it in the react app). The services used are 4: mysql, phpmyadmin, server, and client. I will explain each of the services:

  • mysql will be used to house our database. It will use the extension fields declared for host, database, user and password (and a root password of its own just in case) to enable connections for the other services. The port 3306 is exposed as that is the default port used in mysql connections with the mysql package. The unless-stopped restart policy is set to try and avoid losing database data when stopping the services. Make a note of the volume used; this will be explained as the db folder is created.

Now for the .gitignore :

Now, while there may be many more things you would wish to not push when finishing commits, these are the only ones necessary in this project.

II. The database file

  1. Create a folder called db at the root level (right inside the react-node-mysql-docker-boilerplate folder), and place a sample.sql file inside that will pass as an example database:

III. The front end: cleaning up

  1. Inside the react-node-mysql-docker-boilerplate folder, execute the following command on your terminal: npx create-react-app client This will create a simple CRA application that we will modify to suit our needs.

IV. The front end: Webpack

  1. The only two packages the front-end uses are webpack and axios . Install them like this: yarn add webpack@4.29.6 axios . Note that, at the time of creating this tutorial, the react-scripts expect to use this version of webpack, but depending on when you’re following this tutorial, you may need a different version.

V. The front end: A simple component

  1. In the src folder, create a folder called components

Note that by itself, this component is not going to do any more than display this is a sample component . For now, we’ll leave the component like this and add the code to communicate with the server later

VI. The front end: Exports central

  1. In order to export the SampleComponent (and any other components you would create), we need an index.js file that takes them all, and facilitates the import with the alias. Create a file index.js right inside the components folder:

VII. The front end: Importing the component

  1. Now we go back into app.jsx , to import the component we just created:

Note that the components location from where we’re importing must match the one declared as alias in webpack.config.js .

VIII. The front end: Docker configuration

  1. In order to be able to create a docker image to run our app as client, we need a Dockerfile . So let’s create a simple one inside the client folder as follows:

For this, I choose to use the alpine version of the node image as it is very small. All we then do in this file is create a directory for the react app inside /app , set that as its working directory, copy package.json and yarn.lock and all the files within the directory where the Dockerfile is located, and run yarn install . For reference, it is possible to simply write this (line 10) as yarn , but I find that including the install helps out in quickly looking at the code and realizing what each line does. Lastly, we execute the command yarn start to start the react app.

IX. The front end: Ignoring

  1. If we leave the files as is, the client image may turn out to be large, and more so if you work based on this code and add your own dependencies. That’s because we’re indiscriminately adding everything to the /app folder of the image. What we need to do then is create a .dockerignore that excludes any unwanted folders from copying:

Thus, the node_modules folder (which gets created internally with yarn install ) and the build folder, which has no real purpose for this tutorial, are ignored.

With this, you should have a project structure in the client folder that looks like this:

X. The back end: Setting up

  1. Inside the react-node-mysql-docker-boilerplate folder, create a folder called server.

With the package.json , we can now install the following packages: cors, express, mysql and nodemon, with this command:

yarn add cors express mysql nodemon

XI. The back end: The express server

  1. Create a file index.js inside the server folder. This will house the app’s usage of express, which is necessary to utilize a proper server that will listen to get and post requests:

Note that we require three packages:

  • cors, to enable connections and bypass the ever-so-ugly error No ‘Access-Control-Allow-Origin’ header is present on the requested resource. Origin ‘http://localhost:8000' is therefore not allowed access.

So, with the mysql package in use, we use it to create a connection pool via createPool, where the credentials are specified. It is important to use createPool instead of createConnection as the latter expects a connection “on the fly”, as in doing so as the path is caught, and not once the server starts (at least with this configuration). Using createConnection within the path causes a PROTOCOL_ENQUEUE_HANDSHAKE_TWICE once the app is refreshed (but not on the first load), and using it outside requests causes PROTOCOL_ENQUEUE_AFTER_FATAL_ERROR , which only gets solved when code in index.js is changed, as the app reloads thanks to nodemon (to be discussed next). Either way, neither is an efficient option for a production environment.

Lastly, all this get request does is receive a parameter called table , then execute a simple query, select * from sample (in this case), via pool.query . If there is an error, it will be sent back. Otherwise, the listing obtained by the query will be sent back.

XII. The back end: hot server load

  1. In package.json , delete the test script and add one called start :

"start": "nodemon index.js"

This allows for the server to reload as soon as changes are made to it, to try to keep the flow of development and viewing changes in browser as seamlessly as possible.

XII. The back end: Docker configuration

  1. All we have to do to enable the creation of a docker image out of this server app is to create the same Dockerfile that the client folder has.

Now you should have a structure of the server folder that looks like this:

XIII. Back to the front end: Communication

  1. Open the file sample-component.jsx again.

Everything should work now. As a last check, you should have a complete project structure that looks like this:

XIV. Preparing and running the Docker images

  1. On the terminal, run docker-compose build . This will skip both images for mysql and phpmyadmin as these depend on images, and will build the images for client and server . Whether you’re doing this on Linux, Mac, or Windows, it may take between 1–2 minutes to build each image and probably a maximum of 5 minutes for both images to be built. Note that, although some of the packages may display errors of installation (similar to the image above), this does not necessarily mean the build process has failed, unless the last line mentions that there have been errors and terminates.

However, it is very much possible to get problems with the server image. To check which containers (with images) are running, you can use the command docker ps. It may be that, because nodemon works in a different way than react-scripts , it still depends on a node_modules folder to exist locally. To diagnose, try running docker-compose up server . Without the node_modules folder, you would get something like this:

And running like this will give you an ERR_CONNECTION_REFUSEDupon trying to access the server app (right as axios invokes the get request). This is easily fixed by navigating into the server folder, then running yarn install .

Another error, though far less common, is to get the following upon running docker-compose up -d : Cannot start service mysql: driver failed programming external connectivity on endpoint. This is no fault of the code; rather of Docker. All we need to do to fix this is restart it.

XV. The results

  1. Now that the images are running in containers, open your favorite browser and navigate to http://localhost:3000 , which is the address of the react app. You should only see this:

But, if you open the developer console, if everything went right, you should see the table that was fetched on the output:

And done! Although having the database output in the console is pointless, this was done for the sake of the tutorial. In your case, and for more complex applications, you should be able to use the data received to modify the component’s state, triggering a render that could get the data from the state and output the table in any way you desire.

Should you need it, I have created a boilerplate here https://github.com/gfcf14/react-node-mysql-docker-boilerplate . Please feel free to comment or open issues on github or here about problems with following the code and/or improvements.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store