Stop Running Your Project Locally
Intro
The typical development workflow for a React application would be as follows, as an example:
npx create-react-app my-app
cd my-app
npm install
npm run start
Well, nothing wrong with that. Your latest Node 18 project might be fully compatible with Node 16 that is currently installed. You don’t notice a thing and move on.
Why
As the number of your project grows and you run into compatibility issues. You then install multiple versions of node
and npm
and switch between versions using nvm
as needed.
You’re doing it wrong!
As a best practice, if your project builds static files, your development environment should match the production CI/CD build environment. If your project runs inside a Docker container on production, the development environment should run in the same Docker container.
Say no to locally spun up ad-hoc environments.
How
In this article, I am building a React application and I know the CI/CD will build static files with a Docker image. Therefore, I want my development environment to match the CI/CD build environment to avoid any deviation and unforeseeable bugs caused by the environment differences.
Install Docker if you don’t already have.
First, create a Dockerfile.dev
to define your development environment. As you can tell, it copies the whole project into the Docker container's app
directory and run npm install
there. You won’t see node_modules
being installed locally.
FROM public.ecr.aws/amazonlinux/amazonlinux:2023
RUN dnf update -y
RUN dnf install -y nodejs
# All of above should match your production build environment
WORKDIR /app
COPY ./ ./
RUN npm install
EXPOSE 4040
CMD [ "npm", "run", "start" ]
Next, create a helper script run-dev.sh
as follows and run it to start the Docker container. Open your browser on https://localhost:4040
to view your application. That’s it.
#!/bin/bash
DOCKER_APP_NAME=my-app-dev
# Remove old image
docker image rm -f $DOCKER_APP_NAME || true
# Build new image
docker build -t $DOCKER_APP_NAME -f Dockerfile.dev .
# Run new container, redirect logs to current console `-t`,
# allow process to exit gracefully using CTRL + C `--init`
# remove container when it exits `--rm`,
# map host port 4040 to container port 4040 `-p 4040:4040`
# map current src directory to container's /app/src directory `-v $(pwd)/src:/app/src`
# give container a name `--name $DOCKER_APP_NAME`
docker run -t --init --rm -p 4040:4040 -v $(pwd)/src:/app/src \
--name $DOCKER_APP_NAME $DOCKER_APP_NAME
The helper script should be self-explanatory. It will build a new Docker image and container every time, it maps container’s app/src directory to the local src directory for hot-reloading, and you will continue to see the logs in your console.
This project will be built with AWS CodeBuild using image aws/codebuild/amazonlinux2-x86_64-standard:5.0 which I know has the identical Docker image as defined in Dockerfile.dev
.
Benefits
- Your development environment will be OS agnostic
- Development and production environments match
- Ability to build legacy projects without installing any packages locally
- Projects can be easily shared within team
Hope this helps.