DEV Community

Cover image for 1-liner Docker commands for React project
V
V

Posted on

1-liner Docker commands for React project


Contents


Intro

Let's say you're primarily a backend developer who works with docker frequently, and you don't want to have nvm and multiple node versions installed. You still want to checkout a frontend project, and maybe start a node/react frontend to see how your system connects, or maybe perform some static analysis on your IDE, these are some good one-liner docker commands to quickly get it up and running. No node/npm needed in your host, no custom Dockerfile or docker build process required.


Obtain node_modules Dependencies

After pulling your project from source control, cd /your/project/folder, run the following docker command in a terminal:

$
docker run --rm -it -v "$(pwd):/app" -w "/app" -u $UID node:12-alpine yarn install
Enter fullscreen mode Exit fullscreen mode

Note: I'm using yarn here, if you're using npm, simply replace yarn install with npm i (shorthand) or npm install

outputs:

yarn install v1.22.5
[1/4] Resolving packages...
[2/4] Fetching packages...
...
Enter fullscreen mode Exit fullscreen mode

You can notice node_modules being populated in your host:

$
ls -la | grep node_modules
Enter fullscreen mode Exit fullscreen mode

outputs

drwxr-xr-x 1050 user user  36864 Mar  9 20:27 node_modules
Enter fullscreen mode Exit fullscreen mode

The idea is to run a container while volume-mounting our host folder to the container. As we obtain node_modules using yarn install from within the container, with volume mounting, we effectively persist it into our host workspace folder, which allows us to perform static analysis afterwards.

Let's break down this command so next time you can remember it instead of having to pull out your notes.

  • node:12-alpine: Name of the Dockerfile which we will pull from Docker hub in order to run our container. You can check out other tags for node images on: https://hub.docker.com/_/node/
  • --rm and -it:
    • --rm tells docker to clean up the trailing container after yarn install completes and the script succeeds.
    • -it indicates interactive mode, which connects stdin/stdout of the container, redirecting input/output from/to your terminal shell.
  • -v, -w and -u:
    • -v "$(pwd):/app": We're mounting the project's folder into /app inside the container. A common way to build Dockerfile for a node app (for example for CI/CD or containerised deployments) is to use ADD or COPY directive in your Dockerfile. However we're directly mounting the host files to avoid rebuilding the container at every change, for ease of development.
    • -w "/app": This parameter sets the container WORKDIR to /app, the same directory we mounted our code into, in order to make every command (e.g. yarn install or yarn start) we docker run to point at the folder.
    • -u $UID: With volume mounting, commands we execute inside the container's mounted volume will generate files that are then persisted back on the host's project folder. With -u set, we execute the command as the host's user uid, so we have full control over our host's node_modules.

Start Development Scripts

Developments Server

To start development server and test your app, run:

$
docker run --rm -it -v "$(pwd):/app" -w "/app" -u $UID -p 3000:3000 -e CHOKIDAR_USEPOLLING=true --env-file="$(pwd)/.env" node:12-alpine yarn start
Enter fullscreen mode Exit fullscreen mode

outputs:

Compiled successfully!

You can now view react-docker in the browser.

  Local:            http://localhost:3000
  On Your Network:  http://172.17.0.2:3000

Note that the development build is not optimized.
To create a production build, use yarn build.
Enter fullscreen mode Exit fullscreen mode

And our app is up and running:
App

Apart from the config parameters applied similarly to our yarn install script, we add a few more for development purposes.

  • -p 3000:3000: I'm testing with a react project. Since the default react-scripts start runs on port 3000, I want to map that port to my host's port, which could be arbitrary instead of 3000 (say for example you're running multiple front-end projects). This makes the development server accessible via localhost:3000.
  • -e CHOKIDAR_USEPOLLING=true: With this option, node from within the container will be able to watch any change in the project's mounted files, and reload the app accordingly with the configured webpack inside react-scripts. Take out this option if you don't want live polling for file changes.
  • --env-file="$(pwd)/.env: react as well as many other front-end libraries want to make use of environment variables, for example for different build target, different feature flags, etc. This option will forward all the variables declared in your project's folder's .env file to the container environment variables, which can be convenient for testing. Take this option out if you don't use environment variables in your setup.

Notice how react-scripts is letting us know the development server is accessible via http://172.17.0.2:3000/. This is simply the container service provided ip address by docker default network. We don't have to concern ourselves with this because we'll never access it from within the container. And since the host port is mapped to the container's port, we can access it on our host computer browser via localhost:3000.

Running jest with --watchAll

If you use react-scripts or jest, with the following configs in package.json

...

  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
Enter fullscreen mode Exit fullscreen mode

or

...

  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "jest --coverage",
Enter fullscreen mode Exit fullscreen mode

If you would like to run test watch, simply use these configs:

$
docker run --rm -it -v "$(pwd):/app" -w "/app" -u $UID -e CHOKIDAR_USEPOLLING=true --env-file="$(pwd)/.env" node:12-alpine yarn test --watchAll
Enter fullscreen mode Exit fullscreen mode

outputs:

yarn run v1.22.5
$ react-scripts test --watchAll
Enter fullscreen mode Exit fullscreen mode
 PASS  src/App.test.tsx
  ✓ renders learn react link (37 ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        2.349 s
Ran all test suites.

Watch Usage
 › Press f to run only failed tests.
 › Press o to only run tests related to changed files.
 › Press q to quit watch mode.
 › Press p to filter by a filename regex pattern.
 › Press t to filter by a test name regex pattern.
 › Press Enter to trigger a test run.
Enter fullscreen mode Exit fullscreen mode

Note: --watch, while giving better performance isn't possible without git installed, which is the case for the pre-built node alpine image on dockerhub. For a more feature-rich containerised set-up, refer to this great article by Michael Herman: Dockerizing a React App.


Optional Improvements

Shell Script Wrapper

You can notice how the parameters can be set similarly for each of our yarn/npm command. Thus, it would be reasonable to wrap them in a bash script, accept arbitrary arguments, so we can call the containerised yarn with any subcommand or parameters.

If you use the one-liner command, write this script, name it something like docker-yarn.sh:

#!/bin/bash

args="$@"
echo "yarn $args"
docker run --rm -i -v "$(pwd):/app" -w "/app" -u $UID -e CHOKIDAR_USEPOLLING=true --env-file="$(pwd)/.env" node:12-alpine yarn $args
Enter fullscreen mode Exit fullscreen mode

Note: Substitute yarn for npm if you use npm.

Note: I'm using -i instead of -it for husky's git hooks explained below.

chmod and run:

$
chmod ug+x docker-yarn.sh
Enter fullscreen mode Exit fullscreen mode
$
./docker-yarn.sh install
Enter fullscreen mode Exit fullscreen mode

Git Hooks with husky

If you have a pre-commit hook like husky installed, this will be what your project likely has in package.json:

  "devDependencies": {
    "husky": "^5.1.3"
  },
Enter fullscreen mode Exit fullscreen mode

and say .husky/pre-push:

#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

yarn run lint && yarn test --watchAll=false
Enter fullscreen mode Exit fullscreen mode

Since you don't want to change your devDependencies (other team members might depend on it), and you want your npm to be dockerised, you can create a shell script similar to the above section (Bash script wrapper) which wraps a dockerised yarn/npm, then alias/symlink it as npm for your host machine.

For demonstration, I'll put it inside /var/scripts/docker-yarn.sh. Then pick a directory that is included in your PATH.

$
echo $PATH
Enter fullscreen mode Exit fullscreen mode

outputs

...:/usr/local/sbin:/usr/local/bin:...
Enter fullscreen mode Exit fullscreen mode

One of mine includes /usr/local/bin, so I'll be symlink the yarn wrapper there.

$
sudo ln -s /var/scripts/docker-yarn.sh /usr/local/bin/yarn
Enter fullscreen mode Exit fullscreen mode

To confirm:

$
which yarn
Enter fullscreen mode Exit fullscreen mode

outputs:

/usr/local/bin/yarn
Enter fullscreen mode Exit fullscreen mode

and:

$
cat $(which yarn)
Enter fullscreen mode Exit fullscreen mode

outputs:

#!/bin/bash

args="$@"
echo "yarn $args"
docker run --rm -i -v "$(pwd):/app" -w "/app" -u $UID -e CHOKIDAR_USEPOLLING=true --env-file="$(pwd)/.env" node:12-alpine yarn $args
Enter fullscreen mode Exit fullscreen mode

To make sure it is working, run a yarn command with arguments. Here I'm testing by installing a package, and running yarn test --watch

Installing package:

$
yarn add date-fns
Enter fullscreen mode Exit fullscreen mode

outputs

yarn add date-fns
yarn add v1.22.5
[1/4] Resolving packages...
[2/4] Fetching packages...
Enter fullscreen mode Exit fullscreen mode

Double check:

$
ls -la node_modules | grep date-fns
Enter fullscreen mode Exit fullscreen mode

outputs

drwxr-xr-x  209 user user  12288 Mar  9 22:02 date-fns
Enter fullscreen mode Exit fullscreen mode

Next command:

$
yarn test --watch
Enter fullscreen mode Exit fullscreen mode

outputs

yarn test --watch
yarn run v1.22.5
$ react-scripts test --watch
Enter fullscreen mode Exit fullscreen mode
PASS  src/App.test.tsx
  ✓ renders learn react link (52 ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        3.283 s
Ran all test suites.

Watch Usage
 › Press f to run only failed tests.
 › Press o to only run tests related to changed files.
 › Press q to quit watch mode.
 › Press p to filter by a filename regex pattern.
 › Press t to filter by a test name regex pattern.
 › Press Enter to trigger a test run.
Enter fullscreen mode Exit fullscreen mode

And finally our git hooks. Commit:

$
git commit -m "fix: wrapper script"
Enter fullscreen mode Exit fullscreen mode

outputs

yarn run lint
yarn run v1.22.5
$ eslint --fix
Done in 0.41s.
[master f8e398c] fix: wrapper script
 Date: Wed Mar 10 20:37:36 2021 +1100
 1 file changed, 3 insertions(+), 4 deletions(-)
Enter fullscreen mode Exit fullscreen mode

push:

$
git push origin master
Enter fullscreen mode Exit fullscreen mode

outputs

yarn run lint
yarn run v1.22.5
$ eslint --fix
Done in 0.41s.
yarn test --watchAll=false
yarn run v1.22.5
$ react-scripts test --watchAll=false
PASS src/App.test.tsx
  ✓ renders learn react link (46 ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        2.769 s
Ran all test suites.
Done in 4.06s.
Counting objects: 3, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 413 bytes | 413.00 KiB/s, done.
Total 3 (delta 1), reused 0 (delta 0)
remote: Resolving deltas: 100% (1/1), completed with 1 local object.
To github.com:veevidify/react-docker.git
   ec7162b..f8e398c  master -> master
Enter fullscreen mode Exit fullscreen mode

Repo

Repository for reference: https://github.com/veevidify/react-docker


Final Thoughts

If you work primarily with front-end projects like react, I would recommend using nvm instead of containerised, since any issue would be much easier to troubleshoot. However, this article aimed to show you how powerful docker can be, and with some basic understanding of image, container and volume, with a couple of clever configs, we can achieve almost any environment or setup.

Top comments (0)