CRUD API using NestJS, MongoDB and Docker
Tags: docker, mongodb, typescript, webdev
Published: 2021-11-26 14:38:30

In this article, we will set some CRUD API using:

- NestJS (NodeJS Framework)
- Mongoose (ODM that helps you to create a Schema based on the Model)
- MongoDB (NoSQL Database)
- Docker (Platform to deploy applications using containers)


GitHub Repository: https://github.com/FrancescoXX/nest-docker

___
Prerequisite: NodeJs installed, Docker (Docker desktop on windows/Mac Machines, Docker installed natively on Linux)

But you can follow way better if you:
- Know how to run a "hello world" application using NestJS
- Know what MongoDB Atlas is and how it works
- Have an idea of what Mongoose is
- Know what a Dockerfile and a Dockerfile is

## NestJS

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1611143647173/upHLKKOgs.png)

NestJs is a Node.js framework for building efficient and scalable applications, with full support to Typescript. It has a CLI you can use to generate projects and block easily and It uses express under the hood but has many interesting features like the building blocks:

- Modules
- Providers
- Controllers
- Services


___

## Mongoose

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1611144288219/UhL2jsSdK.png)

Mongoose is an ODM (Object Data Modeling) javascript library for MongoDB and Node.

It is used to manage data relationships, uses Schemas and helps you to connect to a mongo db using mongo DRIVER

___

## MongoDB


![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1611144597161/QGFJuh1z9.png)

MongoDB is a NoSQL database. It stores documents and by using a Json-like object structure it's very easy to use with a Node application

In this example, we will use MongoDB Atlas, which is a cloud solution and it's perfect for an example. But of course, we could use it locally, even better if inside a docker container.
___

## DOCKER

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1610521677853/SGKWuYsvU.png)

Docker is a platform to build run and share application using the idea of containers. If you want a brief introduction, here is a short video

[![IMAGE ALT TEXT HERE](https://cdn.hashnode.com/res/hashnode/image/upload/v1610522007247/lu2KkUl9j.png)](https://www.youtube.com/watch?v=YOUTUBE_VIDEO_ID_HERE)

https://youtu.be/eN_O4zd4D9o
___

### Project Setup

Install NestJS CLI

NestJS comes with an easy to use CLI, that you can install globally (or locally) and it helps you to get started and use NestJS

```
npm install -g @nestjs/cli
```

Create a new nest project (a new folder will be created)

```
nest new nest-mongo-docker
```

Open the project with your favorite IDE. 
If you have VS Code, you can type

```
code nest-mongo-docker
```

Install dependencies
```
npm install mongoose  @nestjs/mongoose 
```

To check if it's running, just type

```
nest start
```

And check localhost:3000


![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1611134919429/2TF1RYaJ1.png)

If you see something like that, you can keep going :)

From the '/nest-mongo-docker' folder, using the nest CLI, we can generate files for the resource we want to create:

- a controller
- a service
- a module 

```bash
nest generate controller users
```

```bash
nest generate service users
```

```bash
nest generate module users
```

in the `src/users` folder and let's create a user.module.ts file. From the command line, you can type:
```
touch src/users/user.model.ts
```

Our project structure should look like this:

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1611136117342/0Eb9sEdO_.png)
___

### Model

in the users.model.ts, we can create a model for the resource we want to use.
We will use a 'user' resource, with name, surname, and points, of the type string, string, and number respectively. Please note that here we are not using Typescript types, but plain JavaScript ones.

We don't need to add the "id" primary key in the schema, because it will be generated by Mongoose 

```typescript
import * as mongoose from 'mongoose';

export const UserSchema = new mongoose.Schema({
  name: { type: String, required: true },
  surname: { type: String, required: true },
  points: { type: Number, required: true },
});

export interface User extends mongoose.Document {
  id: string;
  name: string;
  surname: string;
  points: number;
}
```

### Service
We will use a Nest Service, which will be called by the controller functions.

Using the Mongoose library, we can easily work with objects in the Mongo Database

We can also import "NotFoundException" to have better error Handling in our application.

users.service.ts

```typescript
import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { User } from './user.model';

@Injectable()
export class UsersService {
  constructor(@InjectModel('User') private readonly userModel: Model<User>) {}

  /**
   * Create a new user
   * @param name
   * @param email
   * @param password
   */
  async create(name: string, email: string, password: string): Promise<User> {
    const createdUser = new this.userModel({ name, email, password });
    return createdUser.save();
  }

  /**
   * Find all users
   */
  async findAll(): Promise<User[]> {
    return this.userModel.find().exec();
  }

}

```

___
## Controller

Let's work on the Controller. NestJs forces us to create a solid structure for our HTTP requests, and it uses decorators for HTTP verbs.

We will create 2 endpoints
- Get All Users
- Create a new User (a Post request passing the new users value in the body)

in the users.controller.ts

```typescript
import {
  Controller,
  Post,
  Body,
  Get,
  Param,
} from '@nestjs/common';
import { UsersService } from './users.service';

@Controller('users')
export class UsersController {
  constructor(private readonly usersService: UsersService) {}

  @Post()
  async createOneUser(
    @Body('name') name: string,
    @Body('email') email: string,
    @Body('password') password: string,
  ) {
    const generatedId = await this.usersService.createOneUser(
      name,
      email,
      password,
    );
    return { id: generatedId };
  }

  @Get()
  getAllUsers() {
    return this.usersService.getAllUsers();
  }

```
___
## Module

The UsersController and UsersService can be imported as a Module, and in this Module we can define some environment variables, to define the connection with the Mongo DB:

- MONGO_ATLAS_USER
- MONGO_ATLAS_PASSWORD
- MONGO_ATLAS_DB

You can change this, and they must match the user and password you have on The MongoDB ATLAS


in the users.module.ts

```typescript
import { Module } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';
import { UserSchema } from './user.model';
import { UsersController } from './users.controller';
import { UsersService } from './users.service';

@Module({
  imports: [
    MongooseModule.forFeature([{ name: 'User', schema: UserSchema }]),
  ],
  controllers: [UsersController],
  providers: [UsersService],
})
export class UsersModule {}

```

in the app.module.ts

```typescript
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { UsersModule } from './users/users.module';
import { MongooseModule } from '@nestjs/mongoose';

@Module({
  imports: [
    UsersModule,
    MongooseModule.forRoot(
      //Replace this line with the one Cluster > Connect > Connect your Application
      `mongodb+srv://${process.env.MONGO_ATLAS_USER}:${process.env.MONGO_ATLAS_PASSWORD}@cluster0.suflu.mongodb.net/${process.env.MONGO_ATLAS_DB}?retryWrites=true&w=majority`,
    ),
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

```

Remember to replace the line inside the forRoot method accordingly to your application.

To check that, go to Atlas DB site, Cluster > Connect > Connect your application, and copy the line below.

replace username, password and database using JavaScript Template literal. This is useful to pass the values from env variable instead of hardcoding them here.


![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1611140608819/O13JjJaq8.png)


ATLAS DB Setup

https://www.mongodb.com/cloud/atlas/lp/try2?utm_source=google&utm_campaign=gs_emea_italy_search_brand_atlas_desktop&utm_term=mongodbatlas&utm_medium=cpc_paid_search&utm_ad=e&utm_ad_campaign_id=1718986540&gclid=EAIaIQobChMI7-GGl6mq7gIVFbd3Ch2hogE3EAAYASAAEgKNVPD_BwE


DOCKER

let's create 3 files to containerize our nest js application

Dockerfile

```bash
FROM node:14

RUN npm i -g @nestjs/cli

COPY package.json .

RUN npm install

COPY . .

EXPOSE 3000

CMD ["nest", "start"]
```

Please note the 3rd line:

This is specific to NestJs, and we need to add this one in order to operate with the Nest CLI inside the container 

the .dockerignore
```bash
node_modules
dist
.git
.vscode
```

The docker-compose.yml

```yaml
#docker-compose file
version: '3'

services:
  nestapp:
    container_name: nestapp
    image: nestapp:1.0.0
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - "3000:3000"
    env_file:
      - .env
```

the .env file:

Replace the XXXXX with your MongoDB Atlas User and Password
```
MONGO_ATLAS_USER=XXXX
MONGO_ATLAS_PASSWORD=XXXXXX
MONGO_ATLAS_DB=nest-mongo-docker-db
```

Replace <YOURPASSWORDHERE> with your password in MongoDB ATLAS.

You can find it on Atlas Site, under "Database Access"

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1611140321956/blYeo9rPX.png)

Also replace the "image" value with something like:

docklerhubuser/nest-mongo:0.0.1


time to run our application

```bash
docker-compose up -d --build nest_mongo_backend
```

check if the app is up and running using

```bash
docker ps
```

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1611141034363/ttkwltjTN.png)

___
### POSTMAN / MONGODB ATLAS


![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1611141114083/dHcmJFXY8.png)

We will use Postman, but you can use a whenever tool you want

let's make a GET request like this


![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1611141195953/JStPWmJPe.png)

our users are empty.

We can confirm this by checking on Mongo  Atlas

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1611141258601/W1EqSzFDy.png)

Click on "Collections"


![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1611141327011/CEauJwFmb.png)

As you can see, the users list is empty

Let's create 3 users with a POST request, using POSTMAN.
Here is an example of one of the 3

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1611141529719/lVHr79TXJ.png)

Let's check again if we have new users


![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1611141625857/D8Zc9QZQh.png)

Let's check on Atlas

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1611141655899/tiJeumlHY.png)


GitHub Repository: https://github.com/FrancescoXX/nest-docker