Debugging ExpressJS app with Docker and Visual Studio Code DevContainers

Navneet Karnani
MandrakeTech
Published in
6 min readJan 14, 2021

--

When I started my journey with NodeJS and ExpressJS, one of the things I saw in the trainings and blogs were instructions on installing NodeJS runtime. Although its trivial, but coming from the world of Enterprise application development where deployment to customer and being able to recreate the exact environment is critical, I wondered if there is an application of Docker in this workflow.

As I explored Docker, and Docker Compose, I saw that the starting point would have been easier if the instructions for using those with NodeJS and Express were available. But then, as I continued to explore, I did not find anything that could walk me through setting up VS Code, my editor of choice, for developing my application while still using breakpoints, as I would, with applications developed locally on my machine.

This is where the hunt to make something work, and make it easy for others to use, started. After exploring various options, I zoomed in on a workflow, that would get me started quickly. Like everything in life, there is some work required. So, let’s begin.

Here are things we will do in this blog:

  • Create an Express application
  • Create a Docker and Docker-Compose environment
  • Run and Debug in VS Code

The prerequisites for reading further are:

  • Docker For Desktop installed ( docker.com/products/docker-desktop )
  • Visual Studio Code installed
  • NodeJS installed ( Yeah! I know. Only to generate the app, never again )
  • An Internet connection

I am going to assume no understanding of Docker, and an going to skip all attempt at explaining how the steps work, in this post. The details on that will come as follow up, because it drive focus away from the intent of this article.

At the end of the steps, you will have a generated Express template, Dockerised, and a VS Code environment for debugging the application.

I am not getting into details of using Docker in production for this app, just development.

Supporting code, with follow along commit history, is available on Github

The benefits of using Docker are to not need local installation of tools. Ironically, in the process I am about to demonstrate, the first step requires that NodeJS be installed. But this is only the first time. You wont need it after this, ever.

To generate the Express app, run the command:

You will see the output as this:

You don’t need to do anything to run npm install yet. This is why it was an irony that you need

As a matter of best practice, I suggest initialising a local git repository, just in case you need to roll back to this point. So, here are the steps:

git init . git add . git commit -m "new express app (generated)"

Start VS Code in the selected directory by running: code .

Switch to the Extensions tab in VS Code, and ensure you have the following extensions installed (both published by Microsoft):

  • Docker ( ms-azuretools.vscode-docker )
  • Remote Containers ( ms-vscode-remote.remote-containers )

You must have a minimum of these in the VS Code environment, and really, after what we do today, you won’t need any installed here either. You will discover why, as we progress.

  • Start up the “Command Palette” in VS Code using Cmd+Shift+P (Ctrl+Shift+P on Windows) or from the “View” menu
  • Invoke the action named “Docker: Add Docker Files to Workspace”
  • Use the default values for the next few prompts: — Application Platform: Node JS — Port that the application listens on: 3000 — Include optional Docker Compose files: Yes

The git tab on VSCode will tell you that 6 files have been created, ready to be added to git.

Again, commit the files, just in case you need to roll back here. So, use VS Code, add the files, and commit using the comment “Add generated Docker files”

As you notice, we have not yet run the magical command npm install

But hang on a little longer.

Let us generate the last bit of files, before we edit them. So, back to the “Command Palette”, this time we want to run the command: “Remote-Containers: Add Development Container Configuration Files”.

For the next prompt How would you like to create your container configuration? use the following value: From docker-compose.debug.yml

You will see a prompt to reopen to develop in container, but not yet. Need some manual steps.

But before we go there, add the files to git, with the comment, “add generated devcontainer config”

First step is to delete the .vscode directory and its contents. These files were generated by the Docker extension. We don't need them, and we want to keep focus on our task. If you need them, they are in the git history for you.

Delete, and commit.

Follow these steps:

  • Create a copy of the Dockerfile and name it Dockerfile.debug
  • Edit the Dockerfile.debug and do the following: -- Remove the-alpine in the first line. We need a full environment for debugging -- Change the lineRUN npm install --production --silent && mv node_modules ../ toRUN npm install

The file will look something like this:

FROM node:12.18 ENV NODE_ENV production WORKDIR /usr/src/app COPY ["package.json", "package-lock.json*", "npm-shrinkwrap.json*", "./"] RUN npm install COPY . . EXPOSE 3000 CMD ["npm", "start"]

You may change the version of Node in the FROM command, and make sure you do it in the Dockerfile too, so that you are consistent.

Again, not getting into the production usage of this app in this post.

The idea behind using npm install inside the container is to ensure the right module binaries are available for that environment. We do not want to use the node_modules for your local machine there. So, we need to update the docker-compose.debug.yml file.

I am posting the full line here. I have changes the build section, and added volumes. git will show you the lines that changed. YAML files are very sensitive to the indentation, so don't want you to spend hours figuring out why it doesn't work.

As you notice in the files we have worked with yet, specifically the Dockerfile.debug and docker-compose.debug.yml, there are specific references to the location of the source in the container. ( /usr/src/app ). So we need to make sure that VS Code will recognise that as the location to work with. Let's do that.

  • Open the .devcontainer/devcontainer.json file
  • Find workspaceFolder and change the value from /workspace to /usr/src/app

And, we also need to update the location that the local sources will be mounted on-to:

  • Open the .devcontainer/docker-compose.yml file
  • Look for the volumes section
  • The comment there clearly calls out: Update this to wherever you want VS Code to mount the folder of your project
  • So, change /workspace to /usr/src/app

Here are the file changes:

As always, commit to git.

No, I am not going to say those magic words. We are done. You can, at this time, click on the green icon at the bottom left of the VS Code window, or use the ‘Command Palette’ and “Reopen in Container”.

VS Code is helpful to show the logs, if you are interested, when you click the appropriate link in the message shown during the deployment.

You are now ready to go.

Ok, there is a one more thing. If you change the package.json, and hence need to update the node_modules, and, for some reason, it does't help, you can open the Command Palette and run "Rebuild and Reopen in Container".

If there are extensions you like to use, in VS Code, those can be added to the “extensions” property in the .devcontainer/devcontainer.json file.

As you will know, you can open the package.json, and see the “Debug” prompt there to be able to debug your application, in-place.

Have fun !!!

About the Author: Navneet Karnani is a Full Stack Ployglot veteran, and explores technology to build great products, always striving to extract more productivity from the tools he uses.

--

--

Navneet Karnani
MandrakeTech

Freelancer,Tech Veteran (Generalist, Polyglot, Backend/Frontend/Distributed). Celebrating 35 yrs of coding, 23 yrs in industry. Java, Javascript, Typescript.