Use containers for Node.js development
Prerequisites
Complete Containerize a Node.js application.
Overview
Once your application runs in a container, the next step is making the container part of your everyday development workflow. Code changes should show up quickly, and services your app depends on, like databases, should run right alongside it.
In this section, you'll adapt the Dockerfile for local development by renaming the builder stage to dev and pointing Compose at it. You'll also update the application to connect to a PostgreSQL database, add a database service to compose.yaml, persist data in a named volume, enable Compose Watch so changes in your editor are picked up without a manual rebuild, and set up Node.js debugging so you can attach VS Code or Chrome DevTools to the running container.
Update the application
You'll update your application to connect to a PostgreSQL database. Continue working in your nodejs-docker-example directory.
Replace src/index.ts and package.json with the following contents. The file browser shows only the files that change in this step.
NoteThe application won't run yet after this step. It tries to connect to a PostgreSQL database that doesn't exist. The next two sections add the database service and the Docker configuration needed to run everything together.
Update Docker assets
Replace Dockerfile and compose.yaml with the following.
About these changes
The builder stage from containerize is renamed to dev and gains EXPOSE 3000 and CMD ["npm", "run", "dev"], which runs tsx watch for hot-reload. The deps and runner stages are unchanged.
In compose.yaml, the new target: dev line tells Compose to build and run the dev stage during development. Unlike the production image, the development image includes tsx and other dev tooling. If you need a shell in a running production container, use
Docker Debug instead.
The build step runs tsc, which compiles each TypeScript file into a corresponding JavaScript file. esbuild is a popular alternative that bundles everything into a single output file and builds significantly faster. To switch, replace the tsc call in package.json with an esbuild command and update the COPY --from=dev path in the runner stage to match esbuild's output.
Add a local database and persist data
You can use containers to set up local services, like a database. In this section, you'll update the compose.yaml file to define a database service and a volume to persist data, and add a db/password.txt file that holds the database password.
NoteTo learn more about the instructions in the Compose file, see Compose file reference.
Now, run the following docker compose up command to start your application.
$ docker compose up --build
Now test your API endpoint. Open a new terminal and make a request to the server using the curl commands.
Create an object with a POST request:
$ curl -X 'POST' \
'http://localhost:3000/heroes/' \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"name": "my hero",
"secret_name": "austing",
"age": 12
}'
You should receive the following response:
{
"id": 1,
"name": "my hero",
"secret_name": "austing",
"age": 12
}Now make a GET request:
$ curl http://localhost:3000/heroes/
You should receive the same response because it's the only object in the database.
Press ctrl+c in the terminal to stop your application.
Automatically update services
Use Compose Watch to automatically update your running Compose services as you edit and save your code. For more details about Compose Watch, see Use Compose Watch.
Open your compose.yaml file in an IDE or text editor and add the highlighted Compose Watch instructions.
Run the following command to run your application with Compose Watch.
$ docker compose watch
In a terminal, curl the application to get a response.
$ curl http://localhost:3000
{"message":"Hello World"}
Any changes to the application's source files on your local machine will now be immediately reflected in the running container.
Open nodejs-docker-example/src/index.ts in an IDE or text editor and update the Hello World string by adding a few more exclamation marks.
- res.json({ message: 'Hello World' });
+ res.json({ message: 'Hello World!!!' });
Save the changes to src/index.ts and then wait a few seconds for the application to reload. Curl the application again and verify that the updated text appears.
$ curl http://localhost:3000
{"message":"Hello World!!!"}
Press ctrl+c in the terminal to stop your application.
Debug your application
tsx watch supports the Node.js inspector protocol, so you can attach a debugger from VS Code or Chrome DevTools and set breakpoints directly in your TypeScript source files.
Update the dev script in package.json to start the inspector. The --inspect=0.0.0.0:9229 flag tells Node.js to listen for debugger connections on all network interfaces at port 9229. Using 0.0.0.0 rather than localhost is necessary so the debugger is reachable from outside the container. Also expose the debug port in compose.yaml, and add a .vscode/launch.json file that tells VS Code how to attach to the running inspector.
Rebuild and restart with the updated configuration:
$ docker compose up --build
When the inspector is ready, you'll see a line like the following in the logs:
Debugger listening on ws://0.0.0.0:9229/...VS Code
With .vscode/launch.json in place, attach the debugger using the Debug panel.
Open the Debug panel (Ctrl+Shift+D on Windows and Linux, Cmd+Shift+D on Mac), select Attach to Docker Container, and press F5. You can now set breakpoints in your TypeScript source files under src/.
Chrome DevTools
You can also use the built-in Node.js inspector in Chrome without any editor setup.
Open Chrome and go to
chrome://inspect.Select Configure and add
localhost:9229.When your Node.js target appears in the list, select inspect.
Troubleshoot the debugger
If the debugger doesn't connect, verify the container is running and the port is mapped correctly:
$ docker compose ps
$ docker compose logs server
The logs should include a line like:
Debugger listening on ws://0.0.0.0:9229/...If that line is missing, confirm the dev script in package.json includes --inspect=0.0.0.0:9229 and that 9229:9229 appears in the ports list for the server service in compose.yaml.
For more details about Node.js debugging, see the Node.js debugging guide.
Summary
In this section, you set up a Compose file with a local database and persistent storage, set up Compose Watch to automatically sync code changes, and configured a debugger that attaches from VS Code and Chrome DevTools.
Related information:
Next steps
In the next section, you'll learn how to run tests using Docker.