Using docker compose watch with Node.js

mdazhar1038

Md Azharuddin

Posted on May 12, 2024

Using docker compose watch with Node.js

I was recently working on a small side project when I hit a roadblock. I wanted to use a package but was getting some error while installing in my Windows system. So, I decided to use docker as a development environment. Now docker can be really great for this task and if you use volume and bind mounts you can also set up a reload on save pretty easily.

When I was about to do the setup, I remembered that with Docker Compose version 2.22.0, docker compose watch was released and as per this docs page:

Use watch to automatically update and preview your running Compose services as you edit and save your code.

This seemed like the perfect opportunity to try this!
Furthermore, with the release of Node v22, --watch mode was no more an experimental feature, so another cool thing to include 😊

You can check the whole code in this github repo.

Setup

Install Docker

Since I am using windows, I use Docker Desktop which you can install following this guide: Install Docker Desktop on Windows
NOTE: To install Docker Desktop in Windows, you must install WSL2 first for which you may follow this guide: How to install Linux on Windows with WSL

If you use Mac, follow this: Install Docker Desktop on Mac

If you use Linux, you can either install Docker Desktop or you can manually install just Docker Engine and Docker Compose. If you want to install Docker Desktop, check this guide: Install Docker Desktop on Linux

NOTE: Docker Desktop in simple terms is basically a VM + Docker Engine + GUI + extra features like Compose, Kubernetes, Credential helper etc.

Write a simple Express server

  1. Create a new folder compose-watch
  2. Go to compose-watch folder and terminal there
  3. Run npm init -y. This will instantiate a new node project
  4. Install express using npm i express
  5. Create a new folder src in the root of your project
  6. Create a new file src/index.js and add the following content:
import express from "express";

const app = express();

app.get("/", (req, res) => {
  res.json({ msg: "Hello World" });
});

app.listen(3000, () => {
  console.log("Server is listening at http://localhost:3000");
});
Enter fullscreen mode Exit fullscreen mode

Now add scripts in package.json and change type to module as we are using ES6 imports:

{
  "name": "compose-watch",
  "version": "1.0.0",
  "main": "index.js",
  "type": "module",
  "scripts": {
    "start": "node src/index.js",
    "dev": "node --watch src/index.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "description": "",
  "dependencies": {
    "express": "^4.19.2"
  }
}
Enter fullscreen mode Exit fullscreen mode

Run npm run dev to start the server in watch mode.
Now open any browser and type the url http://localhost:3000 and you will get the Hello World response.
Now try changing the Hello World message in src/index.js and you will notice server restarts and now new value appears after calling the url again!

With this we have a working server. So, it's time to dockerize!

Write the Dockerfile

Create a new file in project root called Dockerfile and add the following content:

FROM node:22-alpine
WORKDIR /app
COPY package*.json .
RUN npm install
COPY ./src /app/src
CMD npm run dev
Enter fullscreen mode Exit fullscreen mode

Write the compose file

Create a new file in project root called compose.yml and add the following content:

services:
  server:
    build:
      context: .
    ports:
      - 3000:3000

    develop:
      watch:
        - action: sync
          path: ./src
          target: /app/src

        - action: rebuild
          path: package.json
          target: /app
Enter fullscreen mode Exit fullscreen mode

Time to run!

Now we are ready to test. Run the following command to build the image and start the container in watch mode:

docker compose watch
Enter fullscreen mode Exit fullscreen mode

And that's it!
This will start the server in watch mode. If you make a change in message again, you will be able to see the updated message.

But wait! It seems like watch works only once?
When I try to change the message again, it is not reflecting in the server even though the terminal says

Syncing "server" after changes were detected
Enter fullscreen mode Exit fullscreen mode

I am not sure why exactly this issue happens, but it seems like node native --watch flag does not work properly 😞

Switching to nodemon

  1. Install nodemon using npm i --save-dev nodemon
  2. Change the dev script in package.json to nodemon src/index.js

So, now package.json looks like this:

{
  "name": "compose-watch",
  "version": "1.0.0",
  "main": "index.js",
  "type": "module",
  "scripts": {
    "start": "node src/index.js",
    "dev": "nodemon src/index.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "description": "",
  "dependencies": {
    "express": "^4.19.2"
  },
  "devDependencies": {
    "nodemon": "^3.1.0"
  }
}
Enter fullscreen mode Exit fullscreen mode

Now restart the server using docker compose watch command.
It seems like compose watch works perfectly with nodemon.

If you have used nodemon before with docker volumes, you might know that we need to use polling for nodemon to work properly with docker using the -L flag but we don't need that with compose watch.

So, this is how you can use docker as a development environment (at least for now 😅). If any of you know how to fix the issue with --watch flag, do let me know!

Again, whole code is available in github repo.

Thanks for reading!

💖 💪 🙅 🚩
mdazhar1038
Md Azharuddin

Posted on May 12, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related