This is the second part of my previous post. If you haven't read my first post, please check it out as it serves as a base for this part.
- Connecting & Running with Concurrently (first post)
- Using Docker (this post)
TL;DR | If you don't know docker you can just turn back. But, If you still want to read go ahead.
In this post we'll be looking at the docker way of running React and Node.js. This is a kind of advance development setup and I hope you already installed and know the basics of docker and docker-compose. If you want to know more about docker head over to docker.com
# Initial setup
$ mkdir awesome_project
In this approach we'll not polluting the root folder. Client and Server will stay on their dedicated folder. In this way we can separate client and server at any time if we have to, without breaking anything. To make it work properly, each of them should have a Dockerfile and all will be connected with the docker-compose.
# Client Setup (React)
~ Create react app
$ cd awesome_project && npx create-react-app client
This will create a folder named client which holds our react app.
~ Dockerfile for React
Create a file name Dockerfile
in the client folder and paste the following code.
FROM node:lts-slim
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
EXPOSE 3000
CMD [ "npm", "start" ]
This will be our docker image instructions for our react app where our react app will get compiled and run.
# Server Setup (Node.js)
Our server code will stay in a folder named server
in the root folder. Here you can use express or any other framework of your choice to make up the server. Or you can use this sample to quickly setup a server.
~ Dockerfile for Node Server
Create a Dockerfile
in the server folder. And make sure you have a dev
script in you package.json
. If you have different script for running your server, you can change the CMD
instruction in the Dockerfile
below.
FROM node:lts-slim
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
EXPOSE 5000
# You can change this
CMD [ "npm", "run", "dev" ]
# Running with docker-compose
Docker-compose helps us to combine and run mutiple Dockerfile into a single network container. Make a file named docker-compose.yml
in the root of the project and copy the following code.
version: "3"
services:
frontend:
container_name: awesome_web
build:
context: ./client
dockerfile: Dockerfile
image: vikasraj/awesome_web
ports:
- "3000:3000"
volumes:
- ./client:/usr/src/app
backend:
container_name: awesome_server
build:
context: ./server
dockerfile: Dockerfile
image: vikasraj/awesome_server
ports:
- "5000:5000"
volumes:
- ./server:/usr/src/app
Finally, we'll have a folder structure somewhat like this.
> awesome_project
> client # This is our react front-end
> node_modules
> src
- Dockerfile
- package.json
> server # This is our Node.js server
> node_modules
- index.js
- Dockerfile
- package.json
- docker-compose.yml
Lastly, you need to change the proxy
field in the client/package.json
like
{
"proxy" : "http://backend:5000"
}
backend
is the name of our backend service in the compose file
Now we can run our project by running following command. This will create docker images and volumes which will run in the containers.
$ docker-compose up
If you want to build your images before starting your containers.
$ docker-compose up --build
This can be a tedious approach to work with as you must have the knowledge of docker and docker-compose. But It has some advantage:
- One setup for all development workflow.
- Docker can be used for any programming language.
- Production Deloyment can be a breeze, if you use docker in your DevOps.
- No npm package required (though replaced by docker).
Top comments (33)
Hi Vikas,
Thanks for this! Was much needed. The docker images are running independently on ports 3000(client) and 5000(server). However, I am not able to communicate from the server in the client.
I have added a proxy:"http:localhost:5000" in the client package.json, still no response. How to fix this?
I think there is a typo. It should be
http://localhost:5000
Yeah sorry. That's what I meant. That's what I have I have put in the client package.json
I don't know what's the issue. If you can share the code. I can look and fix it.
github.com/boffti/cfu
Your app looks fine. Please check that the
PORT
variable is set 5000 as you are also loading variables from.env
fileYeah bro, everything seems to be in order but I am not able to make requests to the Server container. Proxy is set correctly, PORT variables are set correctly. Dunno what seems to be the issue. It's working fine during localhost without the containers. Its also deployed on Heroku where it's working fine.
What machine do you use for development?
Hey found the solution man. Instead of "proxy": "localhost:5000", I replaced localhost with the Server's Docker service name from the docker-compose file. Now it looks something like this "proxy": "cfy_server:5000". It's working.
Bro. I seriously forgot about that. Awesome you found the issue.
Thanks for the heads up. I updated the post. I am amazed that no one encountered this issue before.
I came across this article because I am looking for a semi-unrelated react/express docker-compose issue that you solved via the above eg.
http://docker-compose-service_name:xxxx
.No one anywhere said I needed to use my service name instead of
localhost
, but you did. Thank you for your post!PS I included for entertainment my docker-compose file because they look almost identical, yet I didn't even come hear for help with that file; it was already finished. I came only for the package for the proxy fix.
Hi Vikas,
Thank you for a great series of articles. This has been helpful to me since I am a beginner in React and Node, but I have some experience with Docker.
I tried the basic setup from your article. But when I make changes to the frontend, I cannot see it in the browser.
I tried to add
volumes
indocker-compose.yml
but when I start usingdocker-compose up --build
it says that dependencies are not installed.Oh, I get it. Before doing compose you need to install dependecies in both i.e. client and server project.
Because in Dockerfile there is nothing related to installation. All the magic happens in the compose file. It mounts the local directory inside the container will all the packages i.e. node_modules.
I hope this helps. 🤟
Thank you for the reply. Does this mean I have to install
npm
in local host machine?Would there be a way to bypass this if that is the case?
Yes, you need install on the local machine.
If you don't wanna do this. Then you have to modify the Dockerfile to add the installation process inside the container.
Hi Vikas,
Thank you for this excellent writeup. I have a question, I built a React app using Express/Mongo/React/Node and I am using 'concurrently'. As you said, I have a messy root folder because my Express server.js file is in the root of my project and the React app resides in the 'client' folder inside the root. I would like to Dockerize this project using your approach which will separate everything and allow me to use the docker-compose.yml to combine everything. I am new to Docker and I was wondering what is the best method for me to start going about Dockerizing this project since it is ready to be deployed in its current state. It seems as though you have quite a bit of knowledge with this procedure.
Thanks in advance!
Stephen
If you don't know docker then don't introduce any docker stuff in your app. If ain't broke, don't fix it 😊. You should first learn good amount of docker. Bcz sometimes docker can be your enemy.
If you know docker then,
First step would be to strip out any concurrently stuff.
Optional but if you want a nice folder structure then you should look at yarn workspaces
If your app is open source I would be very happy to contribute 🤞🏻.
And I promise, learning docker will be a big plus for you.
Thank you very much for your reply Vikas! It is a personal project and I am just launching an early alpha to test functionality, so that is why I was considering dockerizing it. Both to force me to learn docker and also to help in the deployment. I will probably do as you said and not use it for this project and I will wait for another project and then use docker from the beginning.
Thank you very much for your input!
Stephen
It is really nice thing that you want to force yourcelf to learn docker. I really appreciate that.
And you can definitely dockerize your current app. You just have learn enough to get yourself a start. And make mistake.
Then you can later improvize on that learnings and mistakes. It the same how i did it.
Thanks you very much for this :). one question tho, Currently I've been updating my react app and still doing docker-compose down and up for it to apply changes. How will docker compose up automatically read the changes I've made in my react app? Thank you again! :)
You don't have to do compose up and down every time you make a chnage. Containers will automatically detect changes because of attached
volumes
indocker-compose.yml
what if I wanted to add a database? how would I dockerize that? sorry. im noob. but thank you for this though. really helps. :)
docker-compose will look like this with database. Here i am using mongo.
And make sure to change the mongo environment variable when connecting your nodejs app with the database. The
database
in the connection string represent the service name of your database in the docker-compose.yml.I hope this helps you.
thank you my dude. much love on this one. last question though, lets say the database is mysql? is it the same setup as nosql? and the folder for db is level with client and server? thanks man. appreciate the help.
I dont know know much about SQL. You can go to the dockerhub and search for mySql image. You can read more there.
The code works perfectly. But when I omit the docker volume part in the docker compose file, I get the proxy error(ecconrefused). I can't really understand how is volume helping the react app to access the server. Any idea on this? I have been working on this for the past week and yet haven't been able to find out the exact reason why? It would be really helpful if you could help?
Hi Vikas, I have a project with this strcutre, nodejs + reacjs as client, what script should I use to build both projects and deploy them in a server without docker? Thank you in advance for your kind effort!
thanks man.
adding that proxy to package.json saved my day
that proxy is necessary because, react and express will be running on different ips in docker engine, right?
What if i want Backend by Golang instead of node.js ? what will be the changes ?
Sadly, I don't work with golang. Just search through Golang image repo on docker hub. I am sure you will find something.
Not a issue, i have found way to connect golang and reactjs using rest api, it's my project, i will post it here soon :)
I like this template and the use of volumes: instead of COPY in the Dockerfile. How would you express as the server?
This template is unopinionated about server framework. You just need to expose a connection port in this case it is
5000
. You can use use any framework of your choice whether it is Express or GraphQl.