The ABCs of NestJS: A Beginner’s guide with MongoDB(Mongoose).
Tags: nestjs, newbie, mongodb, beginners
Published: 2022-05-08 11:36:06

## What is NestJS?

NestJS is a modern NodeJS framework that makes use of popular NodeJS frameworks such as Express and Fastify under the hood. NestJS was largely inspired by Angular, and as a result, it employs an Angular-style module system. NestJS is written in TypeScript, although it also supports native JavaScript.

## Prerequisites

To follow this tutorial, you must meet the following requirements

- Competence in PostMan or any other API testing tool.
- Basic Knowledge of NodeJS and Express apps.
- Basic knowledge of TypeScript.
- Competence in MongoDB(Mongoose).

The following should be installed on your system 

- NodeJS v.14 and above.
- Visual Studio Code(Recommended) or any other IDE.
- PostMan or any other API testing Tool.

## Common Terminologies used in NestJS;

Here are some of the most regularly used terms in NestJS that you'll encounter a lot in this article.

### Interfaces

An interface is a type definition. As a result, it is utilized as a type checker/enforcer in functions, classes, etc. 

```tsx
interface humanInterface{
  name:string;
  gender:string;
  age:number;
}

const kevin: humanInterface={
  name:'Kevin Sunders',
  gender:'Male',
  age: 25,
}
```

The `humanInterface` above performs strict type checking on the `kevin` object. Typescript would throw an error if you added another field or changed the type of any of the object properties.

### Controllers

Controllers are in charge of receiving incoming requests and responding to the client. A controller collaborates with its associated service.

### Services

A service is a [provider](https://docs.nestjs.com/providers) that stores and retrieves data and is used with its corresponding controller.

### Decorators

A decorator is a function-returning expression that accepts a `target`, `name`, and `property descriptor` as optional arguments. Decorators are written as `@decorator-name`. They are usually attached to class declarations, methods, and parameters.

```tsx
@Get()
   getAll(): Model[] {
    return this.testService.getAll();
  }
```

The `@Get` decorator above marks the code block below it as a `GET` request. More about that later on.

### Module

A module is a part of a program that handles a particular task. A module in NestJS is marked by annotating a class annotated with the `@Module()` decorator. Nest uses the [metadata](https://en.wikipedia.org/wiki/Metadata) provided by the `@Module()` decorator to organize the application structure.

## Installing the CLI

To get started you’ll have to install the NestJS CLI ****with `npm`. You can skip this step if you already have the NestJS CLI installed on your system.

```bash
npm i -g @nestjs/cli
```

This code block above will install the nest CLI globally on your system.

## Creating a new project

To generate a new project run `nest new` followed by your desired project name. For this article, we’ll be writing a simple blog API with CRUD functionality while adhering to RESTful standards.

```bash
nest new Blog-Api
```

This command will prompt you to select a package manager, choose `npm`.

This will then scaffold the entire project structure with a test API endpoint whose port is set to `3000` by default. You can test it at `http://localhost:3000` after running the `npm run start:dev` command which will start the server in watch mode similar to what nodemon does in express apps.

After testing the endpoint, you’ll need to delete some of the default files because you won’t be needing them anymore. To do this;

- open the src folder and inside,
- delete `app.controller.spec.ts`,
- delete `app.controller.ts`,
- delete `app.service.ts`,
- Open `app.module.ts`,
- Remove the reference to `AppController` in the `controllers` array and the imports,
- Remove the reference to `AppService` in the `providers` array and the imports.

You might also need to change the `README.md` to meet your specifications.

Your `app.module.ts` file should look like this,

```tsx
//app.module.ts

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

@Module({
  imports: [],
  controllers: [],
  providers: [],
})
export class AppModule {}
```

### Environmental Variables

As good practice, some sensitive information in your code shouldn't be made public. For example your `PORT` and your `MongoDB URI`.

Let's fix this in your code.

On your terminal run

```bash
npm i dotenv
```

Then create a `.env` file in your directory and add it to your `.gitignore` file. Store your `PORT` variable, you will also have to store your `MongoDB URI` later in the same place. Now replace the exposed `PORT` in your `main.ts` file. To do this, import the `dotenv` package and call the `.config()` method on it.

```tsx
import * as dotenv from 'dotenv';
dotenv.config();
```

This should be your `main.ts` file after you follow the steps above.

```tsx
//main.ts

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import * as dotenv from 'dotenv';
dotenv.config();

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  await app.listen(process.env.PORT);
}
bootstrap();
```

## Generating Modules

To generate a NestJS module using the NestJS CLI run the code snippet below.

```bash
nest generate module blogs
```

This command creates a `blogs` folder that contains a `blogs.module.ts` file and registers `BlogsModule` in your `app.module.ts` file.

## Generating Interfaces

Let’s generate an interface using the NestJS CLI to do the type checking for the object that will represent your blog posts. To achieve this you have to first `cd` into the `blogs` folder because it is recommended that they be stored near the domain objects to which they are associated. 

```bash
cd src/blogs
```

Then run the code snippet below to generate the interface.

```bash
nest generate interface blogs
```

this creates a `blogs.interface.ts` file. This is where we will define our interface. we’ll name the interface `BlogsInterface`.

```tsx
export interface BlogsInterface {
  title: string;
  body: string;
  category: string;
  dateCreated: Date;
}
```

before running any more commands on your terminal, remember to `cd` out of the `src` folder and back into your root folder by running

```bash
cd ../..
```

## Generating Services & Controllers

You’ll need to generate a service class to store and retrieve data and handle all the logic and a controller class to handle all incoming requests and outgoing responses.

### Service

To generate a service run the command below,

```bash
nest generate service blogs
```

This command creates two files the `blogs.service.spec.ts` and the `blogs.service.ts` and registers the service in the `providers` array in the `blogs.module.ts`.

### Controller

To generate a controller run the command below,

```bash
nest generate controller blogs
```

This command creates two files the `blogs.controller.spec.ts` and the `blogs.controller.ts` and registers the controller in the `controllers` array in the `blogs.module.ts`.

With these your blogs structure is almost complete, you just need to make the `BlogsService` accessible to other parts of your program. You can achieve this by creating an `exports` array in the `blogs.module.ts` file and registering the `BlogsService` in that array.

```tsx
//blogs.module.ts

import { Module } from '@nestjs/common';
import { BlogsService } from './blogs.service';
import { BlogsController } from './blogs.controller';

@Module({
  providers: [BlogsService],
  controllers: [BlogsController],
  exports: [BlogsService],
})
export class BlogsModule {}
```

## MongoDB(Mongoose).

Install mongoose by running,

```bash
npm install --save @nestjs/mongoose mongoose
```

After the installation, import `{MongooseModule}` from `'@nestjs/mongoose’` into your `app.module.ts` file. Then grab your `MongoDB URI` and store it in your `.env` file. Repeat the steps to import `dotenv` in the `app.module.ts` file. Then in the `imports` array call the `.forRoot()` method which takes your `MongoDB URI` as an argument on the `MongooseModule`. Similar to the `mongoose.connect()` in regular express apps.

```tsx
@Module({
  imports: [BlogsModule, MongooseModule.forRoot(process.env.MONGODB_URI)],
```

### Creating a Schema.

Let’s create a schema to define the shape of the blogs in our collection. To do this,

- Create a folder inside your `blogs` folder, name it `schemas`,
- Inside the `schemas` folder, create a file and call it `blogs.schema.ts`.

Then,

Firstly, you’ll have to,

- Import the `prop` decorator, the `Schema` decorator, and the `SchemaFactory` from `@nestjs/mongoose`,
- Create a class `Blog` and export it,
- Turn the class into a Schema by placing the `@Schema()` decorator above the class,
- Create a constant `BlogSchema`, assign the return value of calling the `.createForClass(Blog)` with the name of your class as an argument on `SchemaFactory` that you imported earlier.

```tsx
//blogs.schema.ts

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

@Schema()
export class Blog {}

export const BlogSchema = SchemaFactory.createForClass(Blog);
```

Then you’ll need to define the properties of the Schema.

To define a property in the schema you’ll need to mark each of them with the `@prop()` decorator. The `@prop` decorator accepts an options object or a complex type declaration. The complex type declarations could be arrays and nested object type declarations.

```tsx
//blogs.schema.ts

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

@Schema()
export class Blog {
  @Prop({ required: true })
  title: string;

  @Prop({ required: true })
  body: string;
  
  @Prop({ required: true })
  category: string;

  @Prop({ required: true })
  dateCreated: Date;
}

export const BlogSchema = SchemaFactory.createForClass(Blog);
```

Next import `{ Document }` from `'mongoose'`.

Then create a union type with the Schema class and the imported `Document`. Like so,

```tsx
//blogs.schema.ts

import { Document } from 'mongoose';

export type BlogDocument = Blog & Document;
```

Your final `blogs.schema.ts` file should look like this,

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

export type BlogDocument = Blog & Document;

@Schema()
export class Blog {
  @Prop({ required: true })
  title: string;

  @Prop({ required: true })
  body: string;
  
  @Prop({ required: true })
  category: string;

  @Prop({ required: true })
  dateCreated: Date;
}

export const BlogSchema = SchemaFactory.createForClass(Blog);
```

### Registering Schema

You’ll need to import everything into your `blogs.module.ts` file. To achieve this you’ll need to,

- Import `{MongooseModule}` from `'@nestjs/mongoose’`,
- Import `{Blog, BlogSchema}` from `'./schemas/blogs.schema'`
- Create an `imports` array inside the `@module` decorator
- Call the `.forFeature()` method on the `MongooseModule`. This takes in an array containing an object that defines a `name` and a `schema` property which should be set to your `Blog.name` and your `BlogSchema` respectively.

```tsx
@Module({
  imports: [
    MongooseModule.forFeature([{ name: Blog.name, schema: BlogSchema }]),
  ],
```

### Injecting Model

You’ll need to inject the `Blog` model into the `blogs.service.ts` using the `@InjectModel()` decorator. To achieve this you’ll have to

- import `{ Model }` from `'mongoose'`,
- import `{ InjectModel }` from `'@nestjs/mongoose'`,
- Import `{Blog, BlogDocument}` from `'./schemas/blogs.schema’`,
- Create a `constructor` inside the `BlogsService` class,
- Declare a `private` variable and call it `blogModel` and assign a type of `Model<BlogDocument>` to it. All mongoose methods will be called on this variable.

Recall that, `BlogDocument` is the union type of the `Blog` class and the Mongoose `Model` that you created earlier. It is used as the generic type for your variable.

- Decorate `blogModel` with `@InjectModel()` and pass `Blog.name` as an argument.

```tsx
constructor(
    @InjectModel(Blog.name)
    private blogModel: Model<BlogDocument>,
  ) {}
```

### How Routing Works

By now you must have noticed that the  `@Controller` decorator has the string `'blogs'` passed into it. This means that the controller will send all responses and handle all requests made on `http://localhost/3000/blogs`.

Next up you’ll implement the service and controller logic.

## Service and Controller Logic.

It's finally time to implement your CRUD functionality.

Before we get started you’ll need to set up your controller. Start by importing some `HTTP` method decorators into your controller. 

```tsx
//blogs.controller.ts

import {
  Controller,
  Body,
  Delete,
  Get,
  Post,
  Put,
  Param,
} from '@nestjs/common';
```

Then, you’ll need to import the service and register it so you can be able to access it and import the interface for type-checking.

```tsx
//blogs.controller.ts

import { BlogsInterface } from './blogs.interface';
import { BlogsService } from './blogs.service';
```

To register your service create a `constructor` inside the `BlogsController` class and declare a `private readonly` variable `service` and set its type to `BlogsService`.

```tsx
constructor(private readonly service: BlogsService) {}
```

Now that you’re all set up, let’s get started.

### Create

**Service Logic**

Import  `{ BlogsInterface }` from `'./blogs.interface'` and add an `async` function to the `BlogsService` class called `createBlog`, which will take one parameter `blog`, with its type as `BlogInterface`, and its return type as a `Promise` with a generic `<Blog>` type.

```tsx
async createBlog(blog: BlogsInterface): Promise<Blog> {
    return await new this.blogModel({
      ...blog,
      dateCreated: new Date(),
    }).save();
  }
```

**Controller Logic**

In your `BlogsController` class add an `async` function to the class. Call it `createBlog` and mark it with the `@Post` decorator which defines it as a `POST` request.`createBlog` takes one parameter `blog`, with its type as `BlogInterface`. Mark the parameter with `@Body` decorator which extracts the entire `body` object from the `req` object and populates the decorated parameter with the value of `body`.

```tsx
@Post()
  async createBlog(
    @Body()
    blog: BlogsInterface,
  ) {
    return await this.service.createBlog(blog);
  }
```

### Read

Add two `async` methods, One to return a single blog post and the second to return all the blog posts.

**Service Logic**

```tsx
async getAllBlogs(): Promise<Blog[]> {
    return await this.blogModel.find().exec();
  }

  async getBlog(id: string): Promise<Blog> {
    return await this.blogModel.findById(id);
  }
```

**Controller Logic**

```tsx
  @Get()
  async getAllBlogs() {
    return await this.service.getAllBlogs();
  }

  @Get(':id')
  async getBlog(@Param('id') id: string) {
    return await this.service.getBlog(id);
  }
```

The `async` functions are marked with the `@Get` decorator which defines it as a `GET` request.

The second `async` function’s decorator has an argument `':id'`. Which is what you’ll pass into the `@Param` decorator. The parameter is marked with the `@Param('id')` which extracts the `params` property from the `req` object and populates the decorated parameter with the value of `params`.

### Update

Let’s implement the logic for the `PUT` request.

**Service Logic**

```tsx
async updateBlog(id: string, body: BlogsInterface): Promise<Blog> {
    return await this.blogModel.findByIdAndUpdate(id, body);
  }
```

**Controller Logic**

```tsx
@Put(':id')
  async updateBlog(
    @Param('id')
    id: string,
    @Body()
    blog: BlogsInterface,
  ) {
    return await this.service.updateBlog(id, blog);
  } 
```

The `async` function’s second parameter is marked with the `@Body()` decorator which extracts the entire `body` object from the `req` object and populates the decorated parameter with the value of `body`.

### Delete

Let’s implement the logic for `delete` requests.

**Service Logic**

```tsx
async deleteBlog(id: string): Promise<void> {
    return await this.blogModel.findByIdAndDelete(id);
  }
```

The `Promise` generic type is `void` because a `Delete` request returns an empty promise.

**Controller Logic**

```tsx
@Delete(':id')
  async deleteBlog(@Param('id') id: string) {
    return await this.service.deleteBlog(id);
  }
```

## Testing the API

To test this API, you should use an API testing tool. For this article, I’ll be using a popular API testing tool called [Postman](https://www.postman.com). I’ll be using random data about popular topics to test.

### Create

Make a `POST` request to  `http://localhost/3000/blogs` with the following JSON objects, this will add all the data to your database.

```json
{
  "title": "jeen-yuhs",
  "body": "The life of superstar rapper Kanye West is currently streaming on Netflix - and according to our jeen-yuhs review, it's a fascinating watch. -credit:Radio Times",
  "category":"Music"
}

```

```json
{
  "title": "Why You Should Always Wash Your Hands",
  "body": "Germs from unwashed hands can be transferred to other objects, like handrails, tabletops, or toys, and then transferred to another person's hands.-credit cdc.gov",
  "category":"Health"
}

```

```json
{
  "title": "Why You Should Follow me on Twitter",
  "body": "Well, Because I asked nicely",
  "category":"Random"
}
```

You should get a `201` response and the created blog with a date and an `_id` added.

### Read

Make a `GET` request to  `http://localhost/3000/blogs`. This should return a

`200` response with an array of all the data you previously added. Copy the `_id`  property of one of the array objects. 

Make another `GET` request to  `http://localhost/3000/blogs/id` with the previously copied id. This should return a `200` response with the data of the object whose id was used to make the request.

### Update

Make a `PUT` request to  `http://localhost/3000/blogs/id` with the data below. The `id`  should be replaced with the one you copied earlier. This should return a `200` response and updates the object bearing the `id` behind the scenes. if you run another `GET` request you should get the updated object.

```json
{
  "title": "why you Should Cut your Nails",
  "body": "It's important to trim your nails regularly. Nail trimming together with manicures makes your nails look well-groomed, neat, and tidy.- credit:WebMD",
  "category":"Health"
}
```

### Delete

Make a `DELETE` request to `http://localhost/3000/blogs/id`.This should return a `200` response and deletes the object bearing the `id` behind the scenes. if you run another `GET` request you won’t see the deleted object.

## Conclusion

So we’re finally at the end of this article. Let’s recap what you’ve covered.

- What NestJS is,
- Terminologies in NestJS,
- Creating a NestJS app,
- Integrating MongoDB into a NestJS app,
- Manipulating and NestJS app,

That’s quite a lot, congratulations on making it this far.

You can find the code on [github](https://github.com/davidekete/Blog-API).

Good luck on your NestJS journey!