Running Node.js/Typescript application with MongoDB in Docker container

Dcokerize Node.js/Typescript/MongoDB app

In this article we are going to follow a step by step approach to Dockerize a Node.js/Typescript application with MongoDB

In this article we are going to explore how to Dockerize a Node.js/Typescript which uses MongoDB as its database. Docker, a platform for creating, deploying, and running applications in containers, is becoming a staple in modern web development. We are going to run Node.js/Typescript application and MongoDB in separate containers and we will combine this multi container application using docker compose.

Prerequisites

  • A basic understanding of Node.js, TypeScript, and MongoDB.
  • Docker and Docker Compose installed on your machine.

If you need help for installing Docker and Docker Compose please checkout the links below

Docker Installation

Windows Installationhttps://docs.docker.com/desktop/install/windows-install/

Ubuntu Installationhttps://docs.docker.com/engine/install/ubuntu/

Mac Installation https://docs.docker.com/desktop/install/mac-install/

Docker Compose

Installation : https://docs.docker.com/compose/install/

Step -1 Setting up Node.js/Typescript application

Create and enter directory for the application

mkdir nodejs-ts-mongo-app && cd nodejs-ts-mongo-app

Step-2 Create Package.json

npm init -y

Step-3 Install the required packages

npm install express mongoose config
npm install -D typescript ts-node @types/node @types/express @types/config

Step-4 Create tsconfig.json file

tsc --init 

or

npx tsc --init

Either of this above command will create the tsconfig.json on your project directory

Modify your tsconfig.json file and remove the unnecessary dependencies . The final tsconfig file should look like the following

{
  "compilerOptions": {
    "target": "es5",                          
    "module": "commonjs",                    
    "lib": ["es6"],                     
    "allowJs": true,
    "outDir": "build",                          
    "rootDir": "src",
    "strict": true,         
    "noImplicitAny": true,
    "esModuleInterop": true,
    "resolveJsonModule": true
  }
}

The rootDir has been setup as “src” because we are going to create all our application codes inside the “src” folder of our application

Step-5 Create Source code directory and app.ts

Create a directory “src” under your project directory and create a file app.ts under the directory. After creating the app.ts file put the following code inside the app.ts file

import express, {Application} from 'express'
const app:Application = express();
const PORT = 4000;

app.listen(PORT,()=>{
    console.log(`Server started at port ${PORT}`)
})

Step-6 Modify package.json

In our package.json file under script we will add some code like the following

"dev": "node build/app.js",
"build":"tsc"

Your package.json should look like this

{
  "name": "nodejs-ts-mongo-app",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "type":"commonjs",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "dev": "node build/app.js",
    "build":"tsc"
  },
  "keywords": [],
  "author": "Anupam <Anupam Roy> (https://codingcharcha.com/)",
  "license": "ISC",
  "dependencies": {
    "config": "^3.3.9",
    "express": "^4.18.2",
    "mongoose": "^7.4.3"
  },
  "devDependencies": {
    "@types/config": "^3.3.0",
    "@types/express": "^4.17.17",
    "@types/node": "^20.5.0",
    "ts-node": "^10.9.1",
    "typescript": "^5.1.6"
  }
}

You may not see some specific entries like “author” . You can ignore that

Step-7 Creating Dockerfile for Node.js

Dockerfiles are text documents that allow you to build images for Docker. Create a file ‘Dockerfile’ and place it under the root of the current project and write the following lines of code

# Use official Node.js image as base
FROM node:latest
# Set working directory 
WORKDIR /usr/src/app
# Copy package.json and package-lock.json
COPY package*.json ./
# Install app dependencies
RUN npm install
# Copy all source files
COPY . .
# Transpile Typescript to Javascript
RUN npm run build
# Command to run the app
CMD ["npm", "run","dev"]

Step-8 Creating the docker-compose file

Though at this point of time we don’t need docker-compose but we will create it so that we can run the app through docker-compose alternatively we can also run the app using just docker commands like “docker build”. Lets create another file docker-compose.yaml and place it under the root of the project

version: '3.7'
services:
  app:
    container_name: app
    build: ./
    ports:
      - 4000:4000

Lets see what each line in the file

‘version’ : Which version of docker-compose is being used

‘services’ : A service is an abstract definition of a computing resource within an application which can be scaled or replaced independently from other components. Services are backed by a set of containers, run by the platform according to replication requirements and placement constraints. A Compose file must declare a services top-level element as a map whose keys are string representations of service names, and whose values are service definitions. A service definition contains the configuration that is applied to each service container.

‘app’ : It is the service for Node.js/Typescript application which contains

‘container_name’ : The name of the docker container that will run the Nodejs/Typescript application

‘build’: It specifies the build configuration for creating a container image from source code. As we have placed our docker-compose file in the root of the Project directory so the path to build will be ‘./’

ports’: ports always be in the form of HOST_PORT:CONTAINER_PORT . The HOST_PORT refers to the port where the service can run internally inside the container. The CONTAINER_PORT on the other hand refers to the port through which other services can access the service running inside the container. In this case both are 4000. But in many cases these two can be different

Step-9 Testing the Node.js/Typescript app through the docker-compose command

In order to run and test our app use the following command

docker-compose up --build

After you test it successfully. Lets move ahead and add MongoDB in the equation.

Step-10 Adding MongoDB to docker-compose as a service

We are going to add MongoDB as a service to the docker-compose file. This time we won’t need a Dockerfile rather we will use MongoDb official image to build the service. After adding the MongoDB in docker-compose the file should look like the following

version: '3.7'
services:
  app:
    container_name: app
    build: ./
    ports:
      - 4000:4000
    links:
      - db
  db:
    container_name: mongodb
    image: mongo:latest
    volumes:
      - ./data-volume:/data/db
    ports:
      - 27017:27017

Here as you can see from the file few things are already known to you. Here we will talk about remaining three properties ‘image’ and ‘volumes’ under ‘db’ service and ‘links’ property under ‘app’ serivce

‘volumes’ : Volumes are persistent data stores implemented by the container engine. Compose offers a neutral way for services to mount volumes, and configuration parameters to allocate them to infrastructure. Here we want the data stored in MongoDB to persistent and not get lost on every start . So in order to make it persistent data-volume has been mapped to data/db

‘image’ : This signifies the image of the particular service

‘links’: It instructs Docker to link containers over a network. When we link containers, Docker creates environment variables and adds containers to the known hosts list so they can discover each other.

Step-10 Connect to MongoDB from Node.js/Typescript app

Create file dbconnect.ts in the root of the project directory and write the following code inside it

import mongoose from "mongoose";

export async function connect(){
    try{
        await mongoose.connect("mongodb://db:27017/mydatabase");
        console.log(`Sucessfully Connected`)
    }catch(err:any){
        console.log(`Some problem happened while connecting to db ${err.message}`);
    }
}

Step-11 Import the connection file in app.ts and call

Now we are going to call the connect function from app.ts . For that we need to add some code to our existing app.ts file. After importing and adding the function to app,ts the modified code should look like the following

import express, {Application} from 'express'
import { connect } from './dbconnect';
const app:Application = express();
const PORT = 4000
app.use(express.json());
(async()=>
    await connect()
)();

app.listen(PORT,()=>{
    console.log(`Server started at port ${PORT}`)
})

Step-12 Finally test the application with MongoDB attached

To run the app through docker-compose first destroy the previous containers by issuing the following command

docker-compose down --remove-orphans

After the container are stopped and destroyed run the following command to run it once again

docker-compose up --build

Now if you see these two messages in the console

‘Server started at port 4000’

‘Successfully connected’.

That means you have able to successfully configure the application to run in docker

Trait Previous post All you need to know about Traits in PHP
Typescript Types Next post All you need to know about “Types” in Typescript