6. Dockerized app
Tags: docker, nestjs, mongodb, backend
Published: 2023-11-07 03:26:33

## Introduction:

In this tutorial, we'll take an important step in our journey toward microservices architecture by Dockerizing our existing NestJS reservations application. Dockerizing your application allows you to create a portable and reproducible environment, which is crucial when running multiple microservices together. We will also set up Docker Compose to manage the orchestration of multiple containers. Let's dive in.

## Prerequisites:

Before you begin, make sure you have Docker installed on your machine. If you haven't already installed Docker, you can research how to install it and then come back to this tutorial

## Dockerizing Your NestJS Application:

Dockerizing your NestJS application involves creating a Dockerfile, which provides instructions on how to build and run a container. This Dockerfile serves as a template for your application.

**Step 1: Create a Dockerfile:**

In your NestJS reservations project directory, create a new file named Dockerfile. This file will define how your application container should be built.
to do this run the following command on your terminal 
`touch apps/reservations/Dockerfile`
then inside this file just put the following code which I'm going to mention below in sequence

**Step 2: Specify the Base Image:**

In the Dockerfile, use the FROM keyword to specify the base image. For a Node.js application, it's common to use the Node Alpine image, which already includes Node.js.

```
# Use Node Alpine as the base image for development
FROM node:alpine as development
```

**Step 3: Set the Working Directory:**
Use the WORKDIR command to set the working directory inside the container.

```
WORKDIR /usr/src/app
```

**Step 4: Copy Package Files and Install Dependencies:**
Copy the **package.json** and package-lock.json files into the container and then install the dependencies.

```
COPY package.json package-lock.json ./
RUN npm install -g && npm install

```
**Step 5: Copy the Application Code:**
Copy your entire application code into the container.

```
COPY . .
```
**Step 6: Build the Application:**
Run the build command to build your NestJS application. This will compile the TypeScript code into JavaScript.

RUN npm run build

**Step 7: Create a Production Image:**
Now, create a production image using a multi-stage build. This will create a smaller image with only production dependencies.

```
# Create a production image
FROM node:alpine as production
ARG NODE_ENV=production
ENV NODE_ENV=${NODE_ENV}
WORKDIR /usr/src/app
COPY package.json package-lock.json ./
RUN npm install --only=production

```

**Step 8: Copy Files from the Development Stage:**

Copy the files from the development stage to the production stage. This includes the application code and the build output.

```
COPY --from=development /usr/src/app/dist ./dist
COPY --from=development /usr/src/app/package.json ./package.json

```

**Step 9: Expose the Port and Set the Command:**

```
CMD [ "node", "dist/apps/reservations/main" ]
```

your Dockerfile should look like this

```
# Use Node Alpine as the base image for development
FROM node:alpine as development

WORKDIR /usr/src/app

COPY package.json package-lock.json ./
RUN npm install -g && npm install

COPY . .

RUN npm run build

# Create a production image
FROM node:alpine as production

ARG NODE_ENV=production
ENV NODE_ENV=${NODE_ENV}

WORKDIR /usr/src/app

COPY package.json package-lock.json ./
RUN npm install --only=production

COPY --from=development /usr/src/app/dist ./dist
COPY --from=development /usr/src/app/package.json ./package.json

CMD [ "node", "dist/apps/reservations/main" ]
```

**Step 8: Creation docker ignore**
The ".dockerignore" file is used to specify which files and directories should be excluded when building a Docker image. It serves several important purposes:

- Reducing Image Size
- Faster Build Process
- Security
- Avoiding Conflicts

let's create docker ignore at the root of our directory to do this run the following code on your terminal:

`touch .dockerignore`

then populate it with the following code

```
node_modules
dist
npm-debug.log
Dockerfile
.git
.gitignore
*.md
```

**Testing the Dockerized Application:**
You can now build and run your Docker container. 
to to that let's first go to the reservation directory

To do this, use the following commands:

```
cd apps/reservations
```
then type the command 
```
ls
```
hit enter if you see a Dockerfile then we are good to go

you can now build your Dockerfile by running the following command

```
docker build ../../ -f Dockerfile -t reservations

```
**Code Explanation** 

**docker build ../../:** This part of the command specifies the build context. The ../../ path points to the directory where Docker will look for the Dockerfile and other files needed for building the image. The build context includes all the files and directories in that location.

**-f Dockerfile:** This part of the command specifies the Dockerfile to use for building the image. The -f flag is followed by the name of the Dockerfile (in this case, "Dockerfile"). This is useful when you have multiple Dockerfiles in your project, and you want to specify which one to use.

**-t reservations:** This part of the command specifies the name and optionally a tag for the Docker image that will be created. In this case, the image will be named "reservations."

if you run now the following command:
`docker run reservations`

it will throw an error like this

```
Error: Config validation error: "MONGODB_URI" is required
```
we got this error because the Docker container isn't going to be able to access MongoDB, which is running locally on our machine.
let's fix it in the next section (Docker Compose)

**Docker Compose for Microservices Orchestration:**
When you start working with multiple microservices, managing them individually can be cumbersome. Docker Compose simplifies this task by allowing you to define and run multi-container Docker applications. Here's how to set it up:

**Step 1: Create a Docker Compose File:**
In your project's root directory, create a docker-compose.yaml file. This file will define the services and their configurations.

first, make sure that you are back to your root directory, remember that a while ago we went to **apps/reservations**
if you already in the root directory then just run the following command in your terminal:

```
touch docker-compose.yaml
```

**Step 2: Define Services:**
In the docker-compose.yaml file, define the services you want to run.
populate it with the following code:

```
services:
  reservations:
    build:
      context: .
      dockerfile: apps/reservations/Dockerfile
      target: development
    command: npm run start:dev
    ports:
      - "3000:3000"
    volumes:
      - .:/usr/src/app

```
in this code, we specify that the reservations service should use the Dockerfile from the reservations app. We use the target argument to build from the development stage. We also set environment variables and volumes to enable hot-reloading.

**Step 3: Define a MongoDB Service:**
For a complete microservices setup, you'll likely have a database like MongoDB. You can define a MongoDB service (update your docker-compose.yaml file as follows):

```
services:
  reservations:
    build:
      context: .
      dockerfile: apps/reservations/Dockerfile
      target: development
    command: npm run start:dev
    ports:
      - '3000:3000'
    volumes:
      - .:/usr/src/app
  mongo:
    image: mongo

```
we just added a mongo image as our database service

**Update .env MONGODB_URI**
Open your .env file and change the value of MONGODB_URI. It should now point to our newly created service, and it should look like this:

```
MONGODB_URI=mongodb://mongo:27017/reservation
```
We have updated 'localhost' to 'mongo' since we are now using the Docker image for the MongoDB service."

## Step 5: Running the Microservices:
To build and start your microservices using Docker Compose, execute the following command in the root of your project:

```
docker-compose up
```
This command will build and start all the services defined in your docker-compose.yaml file.

## Conclusion:

By Dockerizing your NestJS application and using Docker Compose, you have set the foundation for creating and running microservices. You can easily scale and manage your microservices environment while ensuring that your applications run consistently across different environments. This is a crucial step as you begin to develop and deploy more complex applications using a microservices architecture.

In the next tutorial, we'll dive into creating your first microservice, starting with an authentication service. Stay tuned for more exciting developments!