Perfect Nest.js with Docker Compose and MongoDB Mongoose

Posted July 5, 2024
Perfect Nest.js with Docker Compose and MongoDB Mongoose

This is a deep dive tutorial to Running Nest.js and MongoDB with Docker and Docker Compose. Nest.js lets you use Mongoose. We’ll use Nest.js with MongoDB alongside Mongoose. We’ll then configure the Wholde Nest.js MongoDB App with Docker and Docker Compose.

⚠️ Please note: This tutoril will need Docker installed and running on your computer. You will learn:

  • How to package Nest.js with MongoDB using Mongoose.
  • Running Nest.js MongoDB Docker Containers.
  • Connecting Nest.js to a MongoDB Docker service.
  • How to access a Nest.js MongoDB server with Mongo Express and Docker only.

Ready? Let’s spin MongoDB and Nest.js with Docker like PROs.

Setting up Nest.js with MongoDB and Docker

First, we’ll need a working Nest.js app. I hope you have one. If not, run the following commands:

# Make sure you have Nest.js CLI
npm i -g @nestjs/cli
# Set Up a New app
nest new nest-docker

You will now point your app to this new nest-docker directory:

cd nest-docker

Nest.js will require you to have Mongoose installed as a dependency. We’ll use the following command. Run it as such:

npm i mongoose @nestjs/mongoose

This app is fine, up to this point. Let’s dive into the Docker setup.

Packaging Nest.js and MongoDB with Docker

To run any app with Docker, we will need a Dockerfile—a Dockerfile layout all the commands and instructions Docker will need to move this Nest.js app to Docker.

First, create a Dockerfile in your new nest-docker directory:

touch Dockerfile

A Dockerfile needs to be optimized. And in this case, we’ll ensure that. For this case, we’ll use Docker with a multistage build approach. In the previous blog, we covered the Strategies to Slim and Reduce Docker Image Size. The multistage build is one of them. It will ensure we have a minimalist and optimized packaged Nest.js Docker Image.

Go to the created Dockerfile and lay out the following:

###################
# The Build Stage
###################

FROM node:20-alpine AS build
# Docker working dir for Nest.js
WORKDIR /app
# Copy both .json files
# The * lets package-lock.json and package.json get copied
COPY package*.json .
## Install the packages on your package.json file
RUN npm install
# Copy over all the Nest.js source code
COPY . .
# Create the build
RUN npm run build

###################
# The Production Stage
###################

FROM node:20-alpine AS prod
# Setting up the production space
WORKDIR /app
COPY package*.json .
# Only run dependencies need for production
RUN npm install --only=production
# Copy dist from the build stage
COPY --from=build /app/dist  ./dist
# State the port you need Nest.js to use
ENV PORT=3000
EXPOSE ${PORT}
# Command to run Nest.js on Docker
CMD ["npm","run", "start:prod"]

This Dockerfile gets Nest.js into an image. We’ll:

  • Let the Build Stage package the build of Nest.js.
  • Let the production stage optimize the build and only use the build for production to run the image.

To optimize this image further, we will create a .dockerignore and remove the files and folder that Docker doesn’t need to copy:

touch .dockerignore

Then we add:

# Ignored these files and directories at any application level
**/README.md
**/.dockerignore
**/Dockerfile
**/docker-compose.yml
**/npm-debug.log


# Ignored these files and directories at any application level
**/.git/
**/dist/
**/coverage/
**/node_modules/
**/.next/
**/tmp/

Adding MongoDB Mongoose with Docker Compose

The next step is to prepare the MongoDB server. Because we’re running this example on Docker, we’ll need a way to access the server. MongoDB Express will act at the MongoDB UI in this case.

Docker Compose compose lay ots the instruction Dpcker will need to package all the services of your application. It runs the image from Docker Hub or the custom one. Like the one we’ve set up using Dockerfile for Nest.js.

In this first case, we’ll use Docker Compose to create access to MongoDB. Docker Compose will look up your directory and check if you have a docker-compose.yml file. This means we must create this file within the same path as the Dockerfile. Do so using this command:

touch docker-compose.yml

Let’s now add the instruction into this docker-compose.yml file and spin up a MongoDB Nest.js local Docker development environment as follows:

version: "3.8"
services:

  mongo:
    image: mongo:latest
    container_name: mongo-db-server
    environment:
      MONGO_INITDB_ROOT_USERNAME: 'admin'
      MONGO_INITDB_ROOT_PASSWORD: 'pass'
      MONGO_INITDB_DATABASE: 'test'
    security_opt:
      - seccomp:unconfined
    ports:
      - "0.0.0.0:27017:27017"
    networks:
      - MONGO
    volumes:
      - type: volume
        source: MONGO_DATA
        target: /data/db
      - type: volume
        source: MONGO_CONFIG
        target: /data/configdb
  
  mongo-express:
    image: mongo-express:latest
    container_name: mongo-db-ui
    environment:
      ME_CONFIG_MONGODB_ADMINUSERNAME: 'admin'
      ME_CONFIG_MONGODB_ADMINPASSWORD: 'pass'
      ME_CONFIG_MONGODB_SERVER: mongo
      ME_CONFIG_MONGODB_PORT: "27017"
    ports:
      - "0.0.0.0:8081:8081"
    networks:
      - MONGO
    depends_on:
      - mongo
    restart: unless-stopped

networks:
  MONGO:
    name: mongo_mongo

volumes:
  MONGO_DATA:
    name: MONGO_DATA
  MONGO_CONFIG:
    name: MONGO_CONFIG

This layout creates a complete setup for running MongoDB with Mongo Express in a Docker environment. Let’s highlight some key parts of this layout:

  • Creating MongoDB container (mongo-db-server).
  • Setting initial root username and password.
  • Configuring Mongo Express to connect to the MongoDB server.
  • Creating the services shared custom network (MONGO).
  • Defining volumes MONGO_DATA and MONGO_CONFIG for persistent storage.

Spinning up MongoDB with Docker for Nest.js

The moment you have the above docker-compose.yml file ready, you’ll run the following command to start the servers:

docker compose up --build -d
[+] Running 5/5
 ✔ Network mongo_mongo        Created                                                                                              0.1s 
 ✔ Volume "MONGO_DATA"        Created                                                                                              0.0s 
 ✔ Volume "MONGO_CONFIG"      Created                                                                                              0.0s 
 ✔ Container mongo-db-server  Started                                                                                              0.6s 
 ✔ Container mongo-db-ui      Started                                                                                              0.9s 

Docker should have these two services running as such:

Nest.js with Docker Compose and MongoDB Mongoose

Next, check that Mongo Express can connect to the MongoDB server:

2024-07-05 13:37:59 Waiting for mongo:27017...
2024-07-05 13:38:00 Fri Jul  5 10:38:00 UTC 2024 retrying to connect to mongo:27017 (2/10)
2024-07-05 13:38:01 Fri Jul  5 10:38:01 UTC 2024 retrying to connect to mongo:27017 (3/10)
2024-07-05 13:38:02 Fri Jul  5 10:38:02 UTC 2024 retrying to connect to mongo:27017 (4/10)
2024-07-05 13:38:03 Fri Jul  5 10:38:03 UTC 2024 retrying to connect to mongo:27017 (5/10)
2024-07-05 13:38:04 Fri Jul  5 10:38:04 UTC 2024 retrying to connect to mongo:27017 (6/10)
2024-07-05 13:38:05 No custom config.js found, loading config.default.js
2024-07-05 13:38:05 Welcome to mongo-express 1.0.2
2024-07-05 13:38:05 ------------------------
2024-07-05 13:38:05 
2024-07-05 13:38:05 
2024-07-05 13:38:05 Mongo Express server listening at http://0.0.0.0:8081

Should get a welcome message from MongoDB express and the port it’s listening to. Open http://localhost:8081/ to access it.

We’ll use the following environments to log in:

  • ME_CONFIG_MONGODB_ADMINUSERNAME: ‘admin’
  • ME_CONFIG_MONGODB_ADMINPASSWORD: ‘pass’

Thus:

  • Username is admin.
  • Password is pass.

Nest.js with Docker Compose and MongoDB Mongoose

⚠️ Note: We had the MONGO_INITDB_DATABASE: 'test' environment to create a test database. It Didn’t work on my End. This means I will need to use the already available databases such as local or admin. Check your Express to confirm if you have the same issue and share solutions.

Accessing Dockerized MongoDB with Nest.js and Mongoose

We have all the Docker setups ready and running. Now. Nest.js should be able to connect to Docker. To do so, remember to have Mongoose installed:

npm i mongoose @nestjs/mongoose

Go ahead and create a new Nest.js module. I will use CATS as an example:

nest generate module cats

You will have a src/cats folder. Add a new entity using Mongoose. You’ll create a cat.entity.ts file with cat collections as such:

import { Schema, Prop, SchemaFactory } from '@nestjs/mongoose';
import { Document } from 'mongoose';

@Schema()
export class Cat extends Document {
 @Prop()
 name: string;

 @Prop()
 age: number;
}

export const CatSchema = SchemaFactory.createForClass(Cat);

The same CatsModule on the src\cats\cats.module.ts file should run this entity using MongooseModule.forFeature as follows:

import { Module } from '@nestjs/common';

import { MongooseModule } from '@nestjs/mongoose';
import { Cat, CatSchema } from './cat.entity';


@Module({
 imports: [
    MongooseModule.forFeature([{ name: Cat.name, schema: CatSchema }])
 ],

})
export class CatsModule {}

Finally, create a MongoDB connection using Mongoose at the Nest.js entry point of the src\app.module.ts file:

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { MongooseModule } from '@nestjs/mongoose';
import { CatsModule } from './cats/cats.module';

@Module({
 imports:[
    MongooseModule.forRoot('mongodb://admin:pass@mongo:27017/admin'),
    CatsModule,],
 controllers: [AppController],
 providers: [AppService],
})
export class AppModule {}

Take a keen look at the mongodb://admin:pass@mongo:27017/admin MongoDB URI. You must

  • Add the Password and username as you specified in the docker-compose.yml file.
  • The server is running on Docker. You won’t use localhost but mongo as the service responsible for running MongoDB server on Docker.
  • The MongoDB port 27017 remains the same.
  • You will use the database admin. It was created by the MongoDB server.

Adding Nest.js to Docker Compose/MongoDB

Now, Make sure you add the following Nest.js service to run a Nest.js Container:

version: "3.8"
services:

  nestjs:
    build:
      context: .
      dockerfile: Dockerfile
    container_name: nest-app
    ports:
      - "3000:3000"
    depends_on:
      - mongo
    volumes:
      - .:/usr/src/app
    networks:
      - MONGO

Note that this service:

  • Will run Nestjs API and connect to MongoDB.
  • Will depend on mongo service and share the same MONGO

It’s time to spin the containers. We’ll use the same previous docker compose command:

docker compose up --build -d

Now, Docker should have 3 services running:

[+] Running 3/3
 ✔ Container mongo-db-server  Started                                                                                              1.7s 
 ✔ Container nest-app         Started                                                                                              1.7s 
 ✔ Container mongo-db-ui      Started                                                                                              1.7s 

Nest.js with Docker Compose and MongoDB Mongoose

The nest-app service should have a successful MongoDB connection with MongooseModule:

Nest.js with Docker Compose and MongoDB Mongoose

Finally, check the admin database using the Mongo Express. Cats collection should be ready as such:

Nest.js with Docker Compose and MongoDB Mongoose

Now, go further and create a fledge Nest.js CRUD RESTfull API with Docker and MongoDB (Mongoose) on this Easy Nest.js Mongoose Example Tutorial with MongoDB

Perfect Nest.js with Docker Compose and MongoDB Mongoose

Written By:

Joseph Chege