Skip to content

Latest commit

 

History

History
2663 lines (2067 loc) · 84.5 KB

File metadata and controls

2663 lines (2067 loc) · 84.5 KB

Chapter-2 Getting Started with nestJS

Creating a Basic Controller

Controllers are one of the most important building blocks of NestJS applications as they handle request.

Let’s generate a Controllers with Nest CLI by running

$ nest generate controller
//or
$ nest g co

Since were working with our amazing new app called iluvecoffe, let’s call our first controller coffees

$ nest g co
? What name would you like to use for the controller? coffees
CREATE src/coffees/coffees.controller.spec.ts (499 bytes)
CREATE src/coffees/coffees.controller.ts (103 bytes)
UPDATE src/app.module.ts (340 bytes)

As we can see in our terminal, Nest automatically created a Controller and a corresponding test file (.coffees.controller.spec.ts) for us.

Also, we can see it updated module in our AppModule. If we open up the app.module.ts,

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { CoffeesController } from './coffees/coffees.controller';

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

We’ll see that the CLI automatically added this new CoffeesController to the controller:[] array.

Note, that if we didn’t want to generate a test file, we could have simply passed the --no-spec flag like so.

$ nest g co --no-spec

Now, taking a look back at our project structure, we can see the files we just created ended up in a directory based on the name we selected, in our case: /src/coffees/

├── nest-cli.json
├── package.json
├── package-lock.json
├── README.md
├── src
│   ├── app.controller.spec.ts
│   ├── app.controller.ts
│   ├── app.module.ts
│   ├── app.service.ts
│   ├── coffees                             <<<
│   │   ├── coffees.controller.spec.ts      <<<
│   │   └── coffees.controller.ts           <<<
│   └── main.ts
├── test
│   ├── app.e2e-spec.ts
│   └── jest-e2e.json
├── tsconfig.build.json
└── tsconfig.json

If we want to generate something within a specific folder, just type in the directory or directories which slashes / prior to the Controllers name,

$ nest g co --no-spec modules/abc

For example, Nest generate Controllers module/abc, will be placed within /src/module/abc.

If you are not sure if the generator will place the file in the right directory, use --dry-run flag to see the simulated output form the CLI,

$ nest generate controller modules/abc --dry-run
CREATE src/modules/abc/abc.controller.spec.ts (471 bytes)
CREATE src/modules/abc/abc.controller.ts (95 bytes)
UPDATE src/app.module.ts (417 bytes)

This won’t actually create any files. So it’s a perfect way of testing any command to see what it will do, and where it might place things.

Back to our newly created CoffeesController.

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

@Controller('coffees')
export class CoffeesController {}

As we previously saw, the basic building blocks of Controllers in NestJS are classes and decorators.

We know that Controllers are something that handle request in our application. But how does the application know which URL accesses 'which' Controller?

You might already spotted it here, but the @Controller() decorator, can be passed a String. This String then passed the metadata needed for Nest to create a routing map. Tying[1] incoming request to this corresponding controller. In the case of our CoffeesController, we can see it has the string of 'coffees' passed to the decorator. Tying the /coffees URL for our application to this controller.

If we open up insomnia or postman and make request a 'GET' request to http://localhost:3002/coffees, we are going to see `404`-error.

{
    "statusCode": 404,
    "message": "Cannot GET /coffees",
    "error": "Not Found"
}

We have the Controller setup, but it’s empty; and as the error message is hinting to us "Cannot GET /coffees"; We haven’t actually set up a 'GET' route in this Controller just yet!.

Lucky for us, Nest has decorators for all the common HTTP verbs, all includes in the @nestjs/common package, making this as easy as can be.

import { Controller, Get } from '@nestjs/common';

@Controller('coffees')
export class CoffeesController {
    @Get()                                                    //[2]
    findAll() {                                               //[1]
        return "This action returns all the coffees.";        //[3]
    }
}

Inside our CoffeesController, let’s create a 'GET HTTP'-handler, using one of the Nest decorators. Start by creating a method inside the controller. The name of the method itself doesn’t matter, but let’s called findAll(); as this request, will be use to fetch all the result for this controller.

Now, let’s decorate this method with the @GET() decorator; Make sure to import it from @nestjs/common.

For now, let’s just add a quick return statement and echo some text back. Let’s return a string that says something like, "This action returns all the coffees."

Within just like above, we mapped our first GET requests within the /coffees route.

Other HTTP verbs will be done in the same fashion; and it placed inside of this Controller, they would be mapped to /coffees as well. Let’s save our progress and see if we can access this GET-route from insomnia or postman.

Great, it works perfectly!. We can see that we got: "This action returns all the coffees." back from the API just like we expected.

Nested URL

Now, what if we wanted to have a nested URL for this specific GET-request? Just as we saw with Controllers, all of the HTTP decorator’s take one parameter, a String; which create a "nested-path" and appends it to the one included form the controller itself.

import { Controller, Get } from '@nestjs/common';

@Controller('coffees')
export class CoffeesController {
    @Get('flavors')                 //<<<<
    findAll() {
        return "This action returns all the coffees.";
    }
}

If we updated our GET request, to @GET('flavors'), we can now access this route via /coffees/flavors. Let’s save our changes, and head back to insomnia and hit this new endpoint /coffees/flavors. Perfect, the routes works at its new nested URL.

Everything we’ve shown so far gives us amazing control and flexibility over our HTTP verbs. Making them easy to read, and uniform throughout our application.

Use Route Parameters

chapter 2 1
Use Route Parameters

Routes with specific paths wont’s works when you need to accept dynamic data as part of you request. Let’s say we made a GET request to /coffees/123, where 123 us dynamic and referring to an ID. In order to define routes with parameters, we can add root parameters tokens to the path of the routes.

This lets us capture these dynamic values at that position in the request-URL, and passed them into the method as parameter.

Let’s learn how all of this works by creating a new endpoint in our CoffeesController for this exact scenario.

import { Controller, Get, Param } from '@nestjs/common';

@Controller('coffees')
export class CoffeesController {
    @Get('flavors')
    findAll() {
        return "This action returns all the coffees.";
    }

    @Get(':id')
    findOne(@Param() id) {
        return "This action returns #${params.id} the coffees.";
    }
}

Let’s create a method called findOne(), and add the Nest @Get() decorator on top. This time, let’s pass in :id inside of the @Get() decorator. This signifies that we’re expecting a dynamic root parameter named "id".

Next, let’s go inside of the findOne() parameters and use a new Nest decorator called @Param(), also form @nest/common and name it “params”. The @Param() decorator let us grab all incoming request parameters and use them inside of the function body of our method.

When we don’t pass anything inside of the @Param() decorator, we receive all request parameters, letting us access ${params.id} from the object.

Constraint Params Objects

import { Controller, Get, Param } from '@nestjs/common';

@Controller('coffees')
export class CoffeesController {
    @Get('flavors')
    findAll() {
        return "This action returns all the coffees.";
    }

    @Get(':id')
    findOne(@Param('id') id: string) {
        return "This action returns #${params.id} the coffees.";
    }
}

Sometimes, we don’t want to access the entire params objects. With the @Params() decorator, we have the options of passing in a String inside of it, to access a specific portion of the params. Let’s enter in 'id' directly inside of the decorator. But let’s make sure we update our @Param() name to id: of type String to reflect these changes.

Let’s save everything, head over to insomnia and access, GET /coffees/123, to see if it’s able to grab '123' from the URL. If we changes this to 10, we can see that it’s entirely dynamic picking up any number we pass in.

Handling Request Body

In this lesson, let’s look at how we can work with POST-request and retrieve request payloads that are typically passed alongside them.

Similar to the @Param() decorator we just learned about, Nest also has a helpful decorator for getting all or specific portions of the request.body know as the @Body() decorator.

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

@Controller('coffees')
export class CoffeesController {
    @Get('flavors')
    findAll() {
        return "This action returns all the coffees.";
    }

    @Get(':id')
    findOne(@Param('id') id: string) {
        return `This action returns #${id} the coffees.`;
    }

    @Post()
    create(@Body() body) {          // <<<
        return body;
    }
}

Let’s add new create() POST method to our CoffeesController, making sure we import both the @Post() and @Body() decorators from @nestjs/common.

Notice: we’re using the @Body() decorator in our method parameters, just like we did with @Param()'s.

To testing if everything’s working, let’s return body in our method, so we can if the payload[2] comes back with the response.

Back to `insomnia. Let’s execute POST request to http://localhost:300/coffees, and pass in some arbitrary key/values for the request body by selecting JSON as our payload format.

We’re going to pass in any sort of JSON shape, so enter whatever you fill like.

{
    "name": "Old Florida Roast",
    "brand": "Salemba Brew"
}

As we can see, the request Body is automatically accessed from within our endpoint method!.

Access Specific Body Request

Sometimes we don’t want to access entire body. If we want to access just a specific portion of it, we can actually pass in a String to the @Body(/* String here */) decorator, just like we do with @Param().

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

@Controller('coffees')
export class CoffeesController {
    @Get('flavors')
    findAll() {
        return "This action returns all the coffees.";
    }

    @Get(':id')
    findOne(@Param('id') id: string) {
        return `This action returns #${id} the coffees.`;
    }

    @Post()
    create(@Body('name') body) {        // <<<
        return body;
    }
}

Let’s test it out by adding the String 'name' inside and save our changes.

Back over to insomnia. Let’s hit the endpoint again. But this time we’ll see that we only get the name value returned. It worked!.

When using this approach, just keep in mind that you may run into potential validation issues by doing this. Because if we access ta specific properties, other properties WON’T be validated. So, use this with CAUTION.

Let’s revert these changes and remove name from our @Body() decorator. Let’s save everything and test the endpoint again in insomnia, just to make sure.

Great, we see the entire Body response being passed back again!.

Response Status Code

You might have notice that all the API request we’ve made so far, when we they’re successful.., automatically sent back status code: 200 fort GET and 201 for POST. But we never set any of that up!.

Well, Nest actually servers back these code by default for successful request. But let’s a look at a few ways we can customize and send back whatever codes we need for any given scenario.

One simple way to statically change this behavior is by adding an @HTTPCode() decorator that will see in a moment at the handler level.

To illustrate this with an example. Let’s say we wanted to deprecate our POST requests and pass the 410 - GONE HTTP status code back to anyone hitting the endpoint. Nest also includes a helpful Enum we’ll be using called HttpStatus, so we don’t have to memorize all the status code numbers.

In our CoffeesController, let’s learn how to put all this into action by applying it to the POST endpoint we just created in the previous lesson. Above create() method let’s add another decorator and import @HttpCode() from @nestjs/common

import { Controller, Get, Param, Post, Body, HttpCode, HttpStatus } from '@nestjs/common';

@Controller('coffees')
export class CoffeesController {

    @Get('flavors')
    findAll() {
        return "This action returns all the coffees.";
    }

    @Get(':id')
    findOne(@Param('id') id: string) {
        return `This action returns #${id} the coffees.`;
    }

    @Post()
    @HttpCode(HttpStatus.GONE)      // <<<
    create(@Body() body) {
        return body;
    }
}

This decorator allows us to set a specific status code for the entire response. Inisde the parentheses, let’s pass in HttpStatus, importing from @nestjs/common as well. When we type period . after it, we could see of the available HTTP status code available to us.

Let’s select GONE, and save our changes. Open up insomnia and hit this POST endpoint to see what we get as response now.

// request 'POST - http://localhost:3002/coffees'
// response, 401 - GONE   //<<<

We can see that we recieved 410 - GONE back from the request now, perfect!.

This decorator we used @HttpStatus() is useful when the status code is static. But when we dive deeper into handling errors in later chapters. We’ll look at other helpers methods and utilize Nest provides to give us even more control.

With Nest, we also have the option of using the underlying library specific response object that are application is using. By default NestJS is using ExpressJS under the hood.

But as we know, our applications could be switched to use Fastify if we wanted as well.

To access these underlying response objects, Nest has a decorator called @Res()[3]. The @Res() decorator can be used within an endpoint method parameters, letting us use the native response handling method exposed by the library.

To learn how to use this, let’s open our CoffeesController and make some changes to findAll() GET method.

import { Controller, Get, Param, Post, Body, HttpCode, HttpStatus, Res } from '@nestjs/common';

@Controller('coffees')
export class CoffeesController {

    @Get('flavors')
    findAll(@Res() response) {
        response.status(200).send(This action returns all the coffees.);.
        // return "This action returns all the coffees.";
    }
}

First, let’s import @Res() decorator here from @nestjs/common and name this parameter response. Since Nest is using ExpressJS by default, and so is our application, we can utilize any method standard to the ExpressJS library with this parameter.

To use these native ExpressJS methods, let’s remove our return line and replace it with response, calling the status method on it. Passing in 200; and lastly, let’s call the send() method passing in the String we were already return.

If we save our changes, and head back to insomnia let’s hit the GET endpoint for /coffees just to make sure everything’s still working.

// request 'GET - http://localhost:3002/coffees'
// response, 200 - OK   //<<<

Perfect, we’re getting the same response and it’s still showing a 200 status code, great!.

As a word of CAUTION!!, although this approach works great and does allow for a little more flexibility in some ways by providing full control of these response object. Like header manipulation, library specific features and so on, it SHOULD BE USED WITH CARE.

In general, your approach is much less clear and does have some disadvantages. Some main disadvantages of this approach, are that you lose compatibility with Nest features that depend on Nest standard response handling, such as: interceptors[4] and the @HttpCode() decorator.

When we use the underlying library response like this, our code can become platform dependent[5] as different libraries might different APIs on the response object.

Using this native response also makes our code harder to test, since we’ll have to mock the response object as well.

As a best practice it is recommended to us the Nest standard approach when dealing with response whenever possible.

Let’s make sure we revert all of these changes we made in this chapter, saving it everything before continuing on to the next chapter.

Handling Update and Delete Requests

chapter 2 2
Handling Update and Delete Request

So far in this course, we’ve only made handlers for create and read operations. In this lesson.Let’s look at how we can handle other common operations like UPDATE and DELETE.

There are two different HTTP methods we can use for UPDATE, PUT and PATCH.

A PUT operation replaces the entire resource, because of this we need to have the entire object within the request payload.

A PATCH operation is different. In that, it only modifies a resource partially allowing us to update even just a single property of a resource if we’d like.

Let’s add a PATCH endpoint to our CoffeesController to see it in action.

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

@Controller('coffees')
export class CoffeesController {

    @Get()
    findAll() {
        return "This action returns all the coffees.";
    }

    @Get(':id')
    findOne(@Param('id') id: string) {
        return `This action returns #${id} the coffees.`;
    }

    @Post()
    @HttpCode(HttpStatus.GONE)
    create(@Body() body) {
        return body;
    }

    @Patch(':id')
    update(@Param('id') id: string, @Body() body) {
        return `This action returns #${id} the coffees.`;
    }

    @Delete(':id')
    remove(@Param('id') id: string) {
        return `This action removes #${id} coffee`;
    }
}

Update Operation

To get started. Let’s make a new method on our controller and call it update(), making sure to add the @Patch() decorator on top. Inside of the @Patch() decorator Let’s make sure to pass in the String of ':id' indicate what coffee we’re going to update.

Since a @Patch() operation does a partial update of a single resource. It requires both an id and a payload representing all of the possible values for a given resource. For this we need to take advantage of both @Param() and @Body() decorators.

Let’s jump into the method signature and grab the incoming request parameters via our @Params() decorator, passing in 'id' inside of it naming this parameter id:, which is of course type String.

Our second parameter is going to be the request body. So let’s grab it via the @Body() decorator and let’s call this parameter body for now.

You can see that we are passing in both the `id` param to indicate what entity to update, and the request payload that we’ll use to update that existing resource with.

Let’s just add a little String return statement so we can test if everything’s working so far, great.

Delete Operation

Next. Let’s look at the DELETE operation.

Let’s create a new method in our CoffeesController and call it remove() with the @Delete() decorator on top also from @nestjs/common. Just like `@Patch() we need to make sure we’re passing in an :id for delete operations so we can indicate which exact item needs to be deleted.

Just like before in the parameters of this method. Let’s make sure to utilize The @Param() decorator so that we can grab the 'id' from the incoming request. Lastly let’s return a String just like before that says something like, This action removes #${id} coffee.

Let’s save all of our changes and see if everything works.

Now if we open up insomnia, and do a PATCH request for /coffee/123, 123 being an id, with the Body as JSON format,

{
    "name": "Old Florida Roast",
    "brand": "Salemba Brew"
}

We should see, This action updates #123 coffee.

Let’s change the method to DELETE request, and push send again. This time we should see This action removes #123 coffee.

Implement Pagination With Query Parameters

chapter 2 3
implement pagination with query parameters

In most cases applications, we need to be able to interact with large data sets. For example, let’s imagine our database’s `coffee" table has 'every' brand of coffee on the planet!. Without pagination, a simple search for all coffees could return millions of rows over the network. This is exactly where pagination comes in!.

With pagination, we can split this massive data response into manageable chunks or pages, returning only what’s really needed for each specific response. Whether that’s 10, 50, 100 or however many result we want, with each one of those responses.

As a best practice, we want to use PATH parameter to identify a specific resource while using query parameters to filter or sort that resource.

Nest has a helpful decorator for getting all or a specific portion of the query parameters called @Query(). Which works similar to @Param() and @Body(), which we’ve already seen. Let’s modified findAll() method, and put the new @Query() decorator to use.

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

@Controller('coffees')
export class CoffeesController {

    @Get()
    findAll(@Query() paginationQuery) {
        const { limit, offset } = paginationQuery;
        return `This action returns all the coffees. Limit ${limit}, offset: ${offset}`;
    }

    ...
    ...

}

Inside our findAll() method, let`s add a parameter called paginationQuery and decorate it with the @Query() decorator importing it form @nestjs/common.

Now, inside our method, let’s take advantage of object destructuring to get limit and offset from paginationQuery. Both of which we will be expecting to come in with every request. We don’t have a Type for this parameter yet, but don’t worry, we’ll be covering that in later videos.

Now, for testing purposes, let’s make some changes to our return statement here, so that it send back these limit and offset variables with the response.

Now, let’s make sure we save all of our changes, and head back over to insomnia.

Back in insomnia, let’s execute a GET request to this updated /coffee endpoint and pass limit and offset as part of the URL as query parameters. For example, we’re going to pass in limit=20 and offset=10.

// request 'GET - http://localhost:3002/coffees?limit=20&0ffset=10'
// response, 200 OK

This action return all the coffee.  Limit 20, offset: 10

If we make the request, our response should comeback with, "This action return all the coffee. Limit 20, offset: 10" - just as we expected!.

Creating a Basic Service

Services are very important parts of Nest applications. As they help us separate our Business logic from our Controller. Separating our business logic into Services, helps make this logic reusable throughout multiple parts of our applications.

To create a Service using the Nest CLI - simply enter:

$ nest generate service
// or
$ nest g s

Let’s isolate our Coffees business logic and create a CoffeesService for our application.

$ nest g s
? What name would you like to use for the service? coffees
CREATE src/coffees/coffees.service.spec.ts (467 bytes)
CREATE src/coffees/coffees.service.ts (91 bytes)
UPDATE src/app.module.ts (416 bytes)

So, when generating our Service, let’s enter "coffees" for the name. The CLI will generate a service and a corresponding test file, as well as automatically including this services to the providers:[] Arrays of the closest Module.

In NestJS, each service is a "providers". But what do we mean by a "provider"[6]?

Well, the main idea of a provider is, that it can inject dependencies. This means that objects can create various relationship to each other, and the logic of wiring up instances of objects together, can all be handled by the Nest runtime-system, as opposed to trying to create and manage this type of dependencies injection[7] yourself.

//coffes.services.ts
import { Injectable } from '@nestjs/common';

@Injectable()
export class CoffeesService {}

So, what do these providers look in Nest? Well, like other things we’ve seen in Nest, providers are just a class annotated with a decorator called @Injectable().

Our CoffeesService that we just created, will be responsible for data storage and retrieval[8]; and is designed to be used by the CoffeesController or anything else that might need this functionality.

So, how can weuse dependency injection in Nest? Well, to inject the provider, we can simply use constructors!.

Let’s open up our CoffeesController and define a constructor().

// coffee.controller.ts
import { Controller, Get, Param, Body, Post, HttpCode, HttpStatus, Res, Patch, Delete, Query } from '@nestjs/common';

@Controller('coffees')
export class CoffeesController {

    constructor() {             // <<<
        }

    @Get()
    findAll(@Query() paginationQuery) {
        const { limit, offset } = paginationQuery;
        return `This action returns all the coffees. Limit ${limit}, offset: ${offset}`;
    }

    @Get(':id')
    findOne(@Param('id') id: string) {
        return `This action returns #${id} the coffees.`;
    }

    @Post()
    @HttpCode(HttpStatus.GONE)
    create(@Body() body) {
        return body;
    }

    @Patch(':id')
    update(@Param('id') id: string, @Body() body) {
        return `This action returns #${id} the coffees.`;
    }

    @Delete(':id')
    remove(@Param('id') id: string) {
        return `This action removes #${id} coffee`;
    }
}

Nest handled dependency injection for us. This is achieved by looking at the Type of whatever we pass into a constructor’s parameters.

Let’s try this out by injecting our CoffeesService right here. Let’s start by typing in:

constructor(private readonly coffeesService:  CoffeesService) {}

All right, so let’s break everything down, so that we understand each piece here.

First, notice the use of the private access modifier syntax here. These TypeScript shorthand allows us to both declare and initialize the CoffeesService member immediately in the same location. As well as making it only accessible within the class itself, hence "private".

Next, we utilized the keyword "readonly". This is more so a best practice, but this helps us ensure that we aren’t modifying the service referenced, and in fact, only accessing things from it.

Next, we are simply naming our parameter here, calling it coffeesService, just to make it very clear, and readable for others.

In Nest, thanks to TypeScript capabilities, it’s extremely east to manage dependencies, because they are resolved simply by their Type. This why we have :CoffeesService. Nest will resolve the CoffeesService by creating and returning an instance of CoffeesService to our CoffeesService; or in the normal case of singleton[9], returning the-existing-instance if it has already been requested elsewhere. This dependencies is resolved and passed to your controllers constructor or assigned to the indicated property here.

Now, that we have our dependency set up, let’s shift our focus back to or CoffeesService itself. Typically in applications, providers and services handle business logic as well as interactions with data source.

We’re going to keep things simple things for now, and work with a property within our CoffeesService that can obtain some mock data.

Let’s open up our CoffeesService | coffee.service.ts file, and add a "coffees" property, which we’ll pretend is our data source. Don’t worry, we’ll be working with real database in the future lesson.

// coffee.service.ts
import { Injectable } from '@nestjs/common';

@Injectable()
export class CoffeesService {
    private coffees = [];
}

For now, let’s use this 'Array of coffees' as our "databases", to READ, UPDATE, and DELETE items from. But let’s take it up a notch and create a "Resource Entity" for these items so that we know what Type they are.

First, Let’s create an /entities directory in our /src/coffees/ folder. Inside of the /entities folder, let’s create a new file called "coffee.entity.ts".

Since this is going to take the shape of our resource entity, let’s add a few properties to make it feel like something we’d get from a real database.

// coffee.entitiy.ts
export class Coffee {
    id: number;
    name: string;
    brand: string;
    flavors: string[];
}

Let’s create and export a class named Coffee and give it several properties, 'id' of Type Number, 'name' and 'brand', both of Type String, and lastly, 'flavors' which we will make an string[] (Array of String).

Let’s go back to CoffeesService and update our resource, to utilize this new Entity Class.

// coffee.service.ts
import { Injectable } from '@nestjs/common';
import { Coffee } from './entities/coffee.entity';

@Injectable()
export class CoffeesService {
    private coffees: Coffee[] = [
        {
            id: 1,
            name: 'Salemba Roast',
            brand: 'Salemba Brand',
            flavors: ['chocolate', 'vanilla'],
        },
    ];
}

So, let’s make sure we set coffees to Type Array of Coffees. We can also predefine a single Entity within the Array, just for testing purposes. Now, that our pseudo data-source is set up, let’s create some CRUD operations around this, to bring some life and business logic to our CoffeesService.

When we say CRUD operations, we’re talking about the big four: Create, Read, Update, and Delete.

As, this would be a lot of code to go through, we’re going to paste in a full implementation of everything. But, if you’re following along at the bottom of this lesson plan, you’ll see the same fully populated CoffeesService you can use in your application.

// coffee.service.ts
import { Injectable } from '@nestjs/common';
import { Coffee } from './entities/coffee.entity';

@Injectable()
export class CoffeesService {
    private coffees: Coffee[] = [
        {
            id: 1,
            name: 'Salemba Roast',
            brand: 'Salemba Brand',
            flavors: ['chocolate', 'vanilla'],
        },
    ];

    findAll() {
        return this.coffees;
    }

    findOne(id: string) {
        return this.coffees.find(item => item.id === +id);
    }

    create(createCoffeeDto: any) {
        this.coffees.push(createCoffeeDto);
    }

    update(id: string, updateCoffeeDto: any) {
        const existingCoffee = this.findOne(ind);

        if (existingCoffee) {
            // update the existing entity
        }
    }

    remove(id: string) {
        const coffeeIndex = this.coffees.findIndex(item => item.id === +id);

        if (coffeeIndex >= 0) {
            this.coffees.splice(coffeeIndex, 1);
        }
    }
}

In above code, we can see we’ve added interactions with our data-source that help us, findAll(), findOne(), create(), update(), and remove() Coffees. These are, of course, sample implementation "without" a real database. But you got the idea.

Services are where the meat of our business logic should be held, along with any interactions with data-source.

Now that we have everything setup, let’s pop back over to our CoffeesController and utilize this new methods.

import { Controller, Get, Param, Body, Post, HttpCode, HttpStatus, Res, Patch, Delete, Query } from '@nestjs/common';
import { CoffeesService }from './coffees.service';

@Controller('coffees')
export class CoffeesController {

    constructor(private readonly coffeesService:  CoffeesService) {}

    @Get()
    findAll(@Query() paginationQuery) {
        const { limit, offset } = paginationQuery;
        return this.coffeesService.findAll();
        // return `This action returns all the coffees. Limit ${limit}, offset: ${offset}`;
    }

    @Get(':id')
    findOne(@Param('id') id: string) {
        return this.coffeesService.findOne(id);
        // return `This action returns #${id} the coffees.`;
    }

    @Post()
    @HttpCode(HttpStatus.GONE)
    create(@Body() body) {
        return this.coffeesService.create(body);
        // return body;
    }

    @Patch(':id')
    update(@Param('id') id: string, @Body() body) {
        return this.coffeesService.update(id, body);
        // return `This action returns #${id} the coffees.`;
    }

    @Delete(':id')
    remove(@Param('id') id: string) {
        return this.coffeesService.delete(id);
        // return `This action removes #${id} coffee`;
    }
}

Let’s replace all of our empty methods to utilize our CoffeesService and call the relevant method.

For each method in our controller let’s remove the mock return String and instead call the corresponding CoffeesService making sure the pass in the necessary parameters.

return this.coffeesService.findAll();

For the findAll() method, let’s ignore the pagination for the time being. We’ll be using those in the future lesson.

return this.coffeesService.findOne(id);

For findOne() controller. Let’s call, findOne() method from CoffeesService and make sure to pass in our 'id'.

return this.coffeesService.create(body);

For the create() method on controller, let’s call create() method from CoffeesService and pass in 'body'

return this.coffeesService.update(id, body);

For update() method on controller, let’s call update() method form CoffeesService passing in both parameters 'id' and 'body'

return this.coffeesService.remove(id);

Last but not least, for remove() method on controller, let’s call the remove() method on CoffeesService and pass down the 'id'.

Test Providers

With everything in place, let’s save our progress and see if our provider gets called from the routes we just updates.

Now, over an insomnia, let’s test some of these endpoints to make sure everything is wired up properly.

First, let’s try a GET request to /coffees/1, which is our findOne() method.

// request 'GET - http://localhost:3002/coffees/1'
// response, 200 OK
{
    "id": 1,
    "name": "Salemba Roast",
    "brand": "Salemba Brand",
    "flavors": ["chocolate", "vanilla"]
}

Great, we’ve got data back!.

Now, let’s test to findAll() method, and make a GET request for /coffees,

// request 'GET - http://localhost:3002/coffees'
// response, 200 OK
{
    "id": 1,
    "name": "Salemba Roast",
    "brand": "Salemba Brand",
    "flavors": ["chocolate", "vanilla"]
}

We should get an Array back of all these coffees. So far so good.

Next, let’s test the delete() functionality and make a DELETE - request to /coffees/1

// request 'DELETE - http://localhost:3002/coffees/1'
// response, 200 OK

No body returned for response

Great, we’ve got 200 back, it’s working too!.

Lastly, let’s make sure that the coffee is really gone and make a GET - request for ID 1 again, to /coffees/

// request 'GET - http://localhost:3002/coffees'
// response, 200 OK

No body returned for response

Perfect, we got an empty Array back, it really got deleted, our CoffeesService completely works!.

Send User-Friendly Error Message

chapter 2 4
Send user-Friendly Error Message

So far, we’ve looked at when everything goes right in our applications. But what about when we get applications errors? What if an API request fail or times-out? What if the database cannot find the resource we’re looking for?

A lot things can go wrong in complex applications!. But lucky for us, Nest can help easily send back any type of user friendly error message we want. With Nest we’ve few options to choose form: [1] Throwing an Exception, [2] Using library specific response objects; We can [3] even create "Interceptors" and leverage "exception filters", which we’ll be covering later on in this course.

Let’s see an HTTpException in action by opening up our CoffeesService and applying it within our findOne() method. Throwing a different status code for a very common scenario.

// coffees.service.ts
import { Injectable, HttpException } from '@nestjs/common';
import { Coffee } from './entities/coffee.entity';

@Injectable()
export class CoffeesService {
    ....
    ....

    findOne(id: string) {
        const coffee = this.coffees.find(item => item.id === +id);

        if (!coffee) {
            throw new HttpException(`Coffee #${id} not found`, HttpStatus.NOT_FOUND);
        }

        return coffee;
    }
}

In this scenario, let’s say w want to throw an Error whenever the user tries to fetch a coffee, that DOESN’T exist in our data-source. Let’s start by assigning the value coming back from coffees.find() here to a variable and naming coffee.

Next, let’s add an 'if' statement, for when coffee is not defined. Inside our 'if', let’s add throw new HttpException() importing it from @nestjs/common. HttpException() here take two parameters: [1] One being a String for the Error response message, [2] and the other being: the Status Code we want to send back.

For our Error mesage, let’s just pass "Coffee #${id} not found". For our status-code, let’s use the HttpStatus utility Enum, we used in a previous chapter. So we don’t look up or memorize all the different status code, and select: NOT_FOUND.

After our if statement, we know that w have a coffee, so let’s just return it back with the response.

If we save our changes, open insomnia and try to make a GET - request to /coffees/2, for example,

// request 'GET - http://localhost:3002/coffees/2'
// response, 404 NOT FOUND

{
    "statusCode": 404,
    "message": "Coffee #2 not found"
}

We’ll see that our code worked, and the response came back with a 404 status code. Since there is NO coffee with the 'id' of 2.

Helper Http Exception

Note that Nest also has helper methods for all of the common error responses[10]. These are useful and you know exactly which could you need to send back and prefer a simpler and more readable approach. These include helper classes like: NotFoundException, InternalServerErrorException, BadRequestExpection, and many more.

Let’s clean up the HttpException we just made, and pass back one of these simplified ones.

// coffees.service.ts
import { Injectable, HttpException, NotFoundException } from '@nestjs/common';
import { Coffee } from './entities/coffee.entity';

@Injectable()
export class CoffeesService {
    ....
    ....

    findOne(id: string) {
        const coffee = this.coffees.find(item => item.id === +id);

        if (!coffee) {
            throw new NotFoundException(`Coffee #${id} not found`);
        }

        return coffee;
    }
}

Let’s change our code from HttpException and replace it with: NotFoundException also from @nestjs/common, Let’s make sure to remove this second parameter here HttpStatus.NOT_FOUND, since we don’t need it anymore. This helper already passes back the correct status-code for us.

// request 'GET - http://localhost:3002/coffees/2'
// response, 404 NOT FOUND

{
    "statusCode": 404,
    "message": "Coffee #2 not found",
    "error": "Not Found"
}

If we hit the API again, we’ll see it passess a response with the same Error message.

Forgot Handle Exception Scenario

But, what about scenarios where we forgot handle an exception in our application code? Let’s say an exception that isn’t an HttpException.

Well, luckliy, Nest aoutomatically catches these exception for us with a built in exception layer. This layer even send back an appropriate user friendly response for us.

// coffees.service.ts
import { Injectable, HttpException, NotFoundException } from '@nestjs/common';
import { Coffee } from './entities/coffee.entity';

@Injectable()
export class CoffeesService {
    ....
    ....

    findOne(id: string) {
        throw "A Random Error"
        const coffee = this.coffees.find(item => item.id === +id);

        if (!coffee) {
            throw new NotFoundException(`Coffee #${id} not found`);
        }

        return coffee;
    }
}

Let’s test this out by forcing a JavaScript error in our GET request here, and add a random "throw error" message. Let’s enter in a message like: "A Random Error";

If we head back to insomnia and hit the endpoint again,

// request 'GET - http://localhost:3002/coffees/2'
// response, 500 Internal Server Error

{
    "statusCode": 500,
    "message": "Internal server error"
}

We’ll see Nest automatically will send back a 500- Internal Server Error for us.

If we look at our terminal,

[Nest] 2242990   - 03/23/2021, 2:56:14 PM   [ExceptionsHandler] A Random Error +3391ms

We could see the error-message we set outputting "A random Error". This is super helpful for that might be very deep within our code, or even third party library we may be using. This helps, make sure that every error "bubbles up", and all errors come through no matter what!.

Encompass Business-Domain In Modules

chapter 2 5
Encompass business Domain In Module

In NestJS, Modules are strongly recommended as an effective way to organize your application components.

For most Nest application an ideal architecture should employ multiple modules, each encapsulating a closely related set of capabilities. To illustrate this with an example, let’s imagine that we were creating functionality around a Shopping Cart. If our applications has a Shopping Cart Controller, and Shopping Cart Service. Both of these belong to the same application domain as they are very closely related. This would be perfect example of when it might make sense to group parts of our application together and move them into their own feature module.

For iluvcoffee application, so far we’ve had everything in one big module. Let’s encapsulate some of the work we’ve done so far with CoffeesController and CoffeesService and bring them together into their own CoffeesModule.

To generate a Nest module with CLI simply run:

$: nest generate module
//or
$: nest g mo

Simply run nest g module followed by the name of the module.

$ nest g mo
? What name would you like to use for the module? coffees
CREATE src/coffees/coffees.module.ts (84 bytes)
UPDATE src/app.module.ts (487 bytes)

Since we’re building a coffees - module. We’re going to type in coffees for our name and push enter.

// app.module.ts
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { CoffeesController } from './coffees/coffees.controller';
import { CoffeesService } from './coffees/coffees.service';
import { CoffeesModule } from './coffees/coffees.module';	// <<<

@Module({
    imports: [CoffeesModule], 					// <<<
    controllers: [AppController, CoffeesController],
    providers: [AppService, CoffeesService],
})
export class AppModule {}

The CLI will now generate a module-class, and automatically add this module to the imports:[] Array of the closest module, the closest and only module in our application is going to be our AppModule. The CLI automatically added our CoffeesModule import here for us.

Okay, so let’s open up our newly-created CoffeesModule / coffees.module.ts,

// coffees.module.ts
import { Module } from '@nestjs/common';

@Module({})
export class CoffeesModule {}

Looking at our empty CoffeesModule, we can see that a NestJS Module is simply a class annotated with the @Module() decorator, this decorator provides metadata that Nest uses to organized the application structure.

The @Module() decorator takes a single Object whose properties describe the module, and all of its context. Modules contain FOUR main things:

  • Controllers.
    Which you can think of as our API - roots that we want this module to instantiate.

  • Exports,
    Here, we can list Providers within this current - module that should be available anywhere THIS module is "imported"

  • Imports,
    Just as we say in AppModule. The imports:[] Array gives us the ability to list-other-modules that THIS module "requires" are now fully available HERE within this Module as well!.

  • Providers,
    With this providers:[] Array. We’re going to list our Service that need to be instantiated by Nest @Injector(). Any Providers here will be available only within THIS module itself, unless added to the exports:[]" Array we saw above!.

Great. So now that we understand the basic concepts of Nest - Modules. Let’s group some of our previous CoffeesController, and CoffeesService files to be a part of this (CoffeesModule) particular module.

This will help us practice grouping and modularizing our application functionality.

Let’s start by including these two files within our new CoffeesModule.

// coffees.module.ts
import { Module } from '@nestjs/common';
import { CoffeesController } from './coffees.controller';
import { CoffeesService } from './coffees.service';

@Module({                               // <<<
    controller:[CoffeesController],
    providers: [CoffeesService]
})
export class CoffeesModule {}

Inside of our @Module() decorator, let’s first add the controllers: [] - property. This property expects an Array, and inside of the Array, let’s add our CoffeesController, by importing it from ./coffees.controller.ts. Let’s add providers:[] which also an Array, and include our CoffeesService here.

Perfect!. Something important to remember here, We originally had CoffeesController and CoffeesService as part of our overall AppModule. So let’s make sure we remove those reference from the root - module (app.module.ts). This will help prevent us from running into any unexpected issue as they would be instantiated TWICE if we didn’t do this!.

// app.module.ts
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { CoffeesModule } from './coffees/coffees.module';

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

As we can see, a feature module simply organizes code relevant for a specific feature. Helping us keep code organized, and establishing clear boundaries for our application and its features. This also helps us manage complexity and develop with SOLID principles, especially as the size of the application or our team grows.

Let’s save our application so far, fire up some insomnia and make sure everything’s working still.

Excellent, everything still working as expected, and we’ve managed to encapsulate our coffee - logic into its own dedicated NestJS - Module.

Introduction to Data Transfer Objects

A "Data Transfer Object", also known as a DTO. Is an Object that is used to encapsulate data and send it from one application to another. DTO’s helps us define the interfaces or input and output within our system.

For example, let’s imagine we have a POST - request, and with DTO’s we can define the shape or inteface for what we’re expecting to recieve for our Body.

So far in this course, we’ve used the @Body() decorator in our POST and PATCH endpoints. But we have no idea we’re expecting the payload to be. This is exactly where DTO’s come in.

To generate a DTO, we can use the Nest - CLI to simply generate a basic class for us via Nest generate class (followed by the name). To help tie everything together. Let’s generate a CreateCoffeeDto class for our POST endpoint within our CoffeesController.

$ nest g class coffees/dto/create-coffee.dto --no-spec
CREATE src/coffees/dto/create-coffee.dto.ts (32 bytes)

In our terminal, let’s generate this by entering nest g class coffees/dto/create-coffee.dto with --no-spec flag at the end to avoid generating a test file.

Note
As side note, remember that we’re able to generate files inside of whatever folder we want, as long as we list the directory BEFORE the file name.

This is why the name of our file here is ALSO the directory. We also added .dto at the end, as a naming convention best practice. Allowing us to quickly see and know what this file is.

The Nest - CLI is going to generate a plain class that we can use as our DTO (Data Transfer Object). Just to keep things really clean and organized, we’ve decided to create this file within a dedicated "/dto" directory in our /coffees - folder. This is a great application convention to get into not only for other DTO’s you might create, but you can similarly group your Interfaces, Entities, and many other similar items in their own folders, all grouped within their associated module.

Tip
With this convention behavior on your belt, it’s will help keep your code even more organized, clean and easier to understand.
// create-coffee.dto.ts
export class CreateCoffeeDto {}

Looking at this newly created file, we’re going to be using CreateCoffeeDto as an EXPECTED Input Object shape for our CoffeesController POST - request. This DTO, will help us be able to do things, like make sure the request payload has everything we require before running further code.

When we create a new - coffee in our application, what properties do we need to have here for this DTO?

Let’s look at our coffee.entity.ts as an example for what our mock resource looks like and what properties we might need.

// coffee.entity.ts
export class Coffee {
    id: number;
    name: string;
    brand: string;
    flavors: string[];
}

We won’t need to pass in an "id" when creating a coffee. That’s something would be generated by our database - once we set that up in future chapter. But the rest of these properties here are perfect for our DTO.

Let’s go ahead and copy the name, brand, and flavors and paste inside of our CreateCoffeeDto.

// create-coffee.dto.ts
export class CreateCoffeeDto {
    name: string;
    brand: string;
    flavors: string[];
}

Now, that our DTO is all set, let’s save our changes and head over to the POST - request within our CoffeesController.

// coffee.controller.ts
import { Controller, Get, Param, Body, Post, HttpCode, HttpStatus, Res, Patch, Delete, Query } from '@nestjs/common';
import { CoffeesService }from './coffees.service';

@Controller('coffees')
export class CoffeesController {
    ...
    ...

    @Post()
    create(@Body() body) {                          // <<<
        return this.coffeesService.create(body);    // <<<
    }

    ...
    ...
}

Right now, we’re using @Body() here, but we don’t have a "Type" for our payload. This is exactly where our new DTO comes in!.

// coffee.controller.ts
import { Controller, Get, Param, Body, Post, HttpCode, HttpStatus, Res, Patch, Delete, Query } from '@nestjs/common';
import { CoffeesService }from './coffees.service';
import { CreateCoffeeDto } from './dto/create-coffee.dto';

@Controller('coffees')
export class CoffeesController {
    ...
    ...

    @Post()
    create(@Body() CreateCoffeeDto: CreateCoffeeDto) {            // <<<
        return this.coffeesService.create(CreateCoffeeDto);      // <<<
    }
    ...
    ...
}

Let’s change the property name here from body to CreateCoffeeDto, and set it to the Type of CreateCoffeeDto as well.

One last thing here. Let’s make sure we change any instances of "body" in the method to CreateCoffeeDto and we’re all set.

We now have full Type safety within our method, letting us know exactly what to expect for a payload.

As we could see, DTO’s are just simple - Objects, they don’t contain any business logic, methods or anything that require testing. We are just trying to create the shape or Object - Interface of what our data transfer - object is.

One other great best practice with DTO’s is, marking all of the properties as readonly to help maintain immutability. So let’s go back and that to each one of our properties real quick.

// coffee.entity.ts
export class CreateCoffeeDto {
    readonly id: number;
    readonly name: string;
    readonly brand: string;
    readonly flavors: string[];
}

Adding the keyword readonly right before the name of each properties.

Great, so you might have noticed so far, that our CreateCoffeeDto is almost identical to our coffee.entity. This my seem redundant, for the time being. But, that’s just because we don’t have an external data-source just yet. We’re also dealing with a mock - Entity for the time being. We’ll see how different the two of these are, when we dive into real entities later in this course.

To really help drive all of this home, let’s also create another DTO - class for our PATCH - request. Let’s call it UpdateCoffeeDto. Just like before, we’ll generate the class via the Nest - CLI with,

$ nest g class coffees/dto/update-coffee.dto --no-spec
CREATE src/coffees/dto/update-coffee.dto.ts (32 bytes)

Remember, we generate update-coffee.dto with --no-spec flag.

Since UPDATE and CREATE operations typically require all of the same class fields with the same types. Let’s copy and paste al the properties from our Create - DTO so we can bring them over to our Update - DTO.

// update-coffee.dto.ts
export class UpdateCoffeeDto {
    readonly id?: number;
    readonly name?: string;
    readonly brand?: string;
    readonly flavors?: string[];
}

We’ll look at ways to avoid this type of repetitiveness in the next video as well.

One big difference that we need for our update - DTO is, that we want all the properties here to be "optional". With TypeScript, you can easily achieve this with the question-mark ?. So let’s make sure we add the ? before the ':' (colon) for each property here.

Let’s pop back over to our CoffeesController, head to the PATCH - request, and let’s add this new Type for our method signature here.

// coffee.controller.ts
import { Controller, Get, Param, Body, Post, HttpCode, HttpStatus, Res, Patch, Delete, Query } from '@nestjs/common';
import { CoffeesService }from './coffees.service';
import { CreateCoffeeDto } from './dto/create-coffee.dto';

@Controller('coffees')
export class CoffeesController {
    ...
    ...

    @Patch(':id')
    update(@Param('id') id: string, @Body() body) {         // <<<
        return this.coffeesService.update(id, body);        // <<<
    }

    ...
    ...
}

Just like before, let’s replace 'body' property with UpdateCoffeeDto and set the Type to updateCoffeeDto: as well.

// coffee.controller.ts
import { Controller, Get, Param, Body, Post, HttpCode, HttpStatus, Res, Patch, Delete, Query } from '@nestjs/common';
import { CoffeesService }from './coffees.service';
import { CreateCoffeeDto } from './dto/create-coffee.dto';
import { UpdateCoffeeDto } from './dto/update-coffee.dto';

@Controller('coffees')
export class CoffeesController {
    ...
    ...

    @Patch(':id')
    update(@Param('id') id: string, @Body() updateCoffeeDto: UpdateCoffeeDto) {         // <<<
        return this.coffeesService.update(id, updateCoffeeDto);                         // <<<
    }

    ...
    ...
}

Now, That we’re using UpdateCoffeeDto, with this optional properties, we can pass in any combination of properties for our payload, which is exactly what we want with a PATCH - request.

As we learned of previous lesson, a PATCH - request can update any portion of a resource, no matter how small. With newly added DTO, we have the power of Type - safety and full flexibility here.

We’ve only begun to scratch the surface of the power of DTO’s. In the next section, let’s see how we can validate this input data, and so much more.

Validate Input Data With Data Transfer Objects

chapter 2 6
Validate Input Data with DTO

As we learned in the last lesson, Data Transfer Object or DTO’s are useful in creating a bit of Type - safety within our application. DTO’s let us create a definition for the shape of the data that’s coming into the body of an API requests.

But we don’t know who or what is calling these requests. How can we make sure the data that’s coming in, is in the correct shape? Or if it’s missing required fields?

It’s a common best practice for any back-end, to validate the correctness of data being sent into our application, and it’s even more ideal if we can automatically validate these incoming requests.

NestJS provides ValidationPipe[11] to solve this problems. The ValidationPipe provides a convenient way of enforcing validation rules for all incoming client - payloads.

You can specify these rules by using a simple annotation in your DTO. Before we start using it, Let’s setup our entire application to use the ValidationPipe

Let’s open our main.ts file, We add the following line,

app.useGlobalPipes(new ValidationPipe());

Making sure, to import it from @nestjs/common.

// main.ts
import { NestFactory } from '@nestjs/core';
import { ValidationPipe } from '@nestrjs/common';
import { AppModule } from './app.module';

async function bootstrap() {
    const app = await NestFactory.create(AppModule);
    app.useGlobalPipes(new ValidationPipe());           // <<<
    await app.listen(3002);
}
bootstrap();
Note
that are many other ways of binding global Pipes that will dive into, in later chapters.

Next, we’re going to have to install two packages in root project. Let’s open our terminal,

$ npm i --save class-validator class-transformer

We install class-validator[12] and class-transformer[13]. We’ll be continuing ahead, as we already have these installed. But if you’re following along, just pause in a second, and come back when it’s finished.

With everything installed, and our ValidationPipe in place, we can start adding validation - rules to our DTO now.

Let’s open up our CreateCoffeeDto,

// create-coffee.dto
import { IsString } from 'class-validator';

export class CreateCoffeeDto {
    @IsString()
    readonly name: string;

    @IsString()
    readonly brand: string;

    @IsString({each: true})
    readonly flavors: string[];
}

Since most of the item here are String’s. Let’s start importing IsString from class-validator. Now, let’s add a @IsString() decorator before the name and brand properties, making them required. For flavors let’s us @IsString() with an Object {each: } set to true. {each: true} indicates that expected value is an Array of String’s.

class-validator has a lot of other great options, make sure to check out the documentation[14] for lots of other great available decorators.

So, how did these decorator’s (@IsString()) help us? Well, we can see that with the help of class-validator. We’ve given instruction for our DTO and set up rules, for things like: 1, What is required, and 2, What is type do we expect certain property to be.

Test Validation Pipe

Now that we have these validation - rules in places, if a request hits our endpoint with an invalid - property in the request - body, the application will automatically respond with a 400 - BadRequest code, giving us automatic feedback and a way to test our payloads effortlessly.

Let’s test how these validation - rules work by passing in a few different key - values and see how endpoint react.

First, let’s call the POST - Coffees endpoint, set our body to JSON and enter in something like {"name": "Shipwreck Roast"}

// request 'POST - http://localhost:3002/coffees'

// Body - raw: JSON
{
    "name": "Shipwreck Roast"
}

// response, 400 - Bad Request
{
    "statusCode": 400,
    "message": [
        "brand must be a string",
        "each value in flavors must be a string"
    ],
    "error": "Bad Request"
}

It looks like it worked, our API responded with 400 Bad Request, and it even gave us a message Array, letting us know exactly what was wrong with *each one* of our fields. Our API knew that brand must be a String, and flavors, is an Array of String’s.

We didn’t pass in either one of these with our request. But we can see that it was expecting them in your payload.

Next let’s make another request, but this time let’s pass in two additional properties. First, let’s pass in "brand": and enter in a random String for the value. Secondly, let’s pass in "flavors": [1, 2], but for the value let’s pass in an Array of Numbers, and see how our API reacts this time.

// request 'POST - http://localhost:3002/coffees'

// Body - raw: JSON
{
    "name": "Shipwreck Roast",
    "brand": "Shipwreck brand",
    "flavors": [1, 2]
}

// response, 400 - Bad Request
{
    "statusCode": 400,
    "message": [
        "each value in flavors must be a string"
    ],
    "error": "Bad Request"
}

Once again, the validation we set up works perfectly. We can see that each value of `"flavors"', does in fact need to be a String, and we got an error message letting us know just that.

Let’s remove those numbers, and add a random String "flavors" like "caramel", for example, hit send.

// request 'POST - http://localhost:3002/coffees'

// Body - raw: JSON
{
    "name": "Shipwreck Roast",
    "brand": "Shipwreck brand",
    "flavors": ["caramel"]
}

// response, 201 - Created

No 'body' returned for 'response'

There you go, our validation finally passed and we can see we got a 201 - Created response back.

Cleaning Redundant Code - Smell '@nestjs/mapped-types'

All right, so now that we’ve seen how the basic of validation works, let’s circle back to our UpdateCoffeeDto.

// update.coffee.dto.ts
export class UpdateCoffeeDto {
    readonly id?: number;
    readonly name?: string;
    readonly brand?: string;
    readonly flavors?: string[];
}

If you remember when we created this DTO, we had to copy the values from our CreateCoffeeDto and change some of those properties. This is a bit a redundant code - smell, isn’t it? Let’s see what we could have done better, and how Nest can help simplify this very common task.

NestJS provides several utility functions as part of the package @nestjs/mapped-types[15]. These functions help us quickly perform these types of common transformations.

Back to our terminal, let’s install the @nestjs/mapped-types package in our root directory and see how we can utilize in our code.

$ npm i --save @nestjs/mapped-types

We’ll continue ahead, as we already have this packages installed, but if you’re following along, just pause a second, and come back when it’s finished.

Once finished, let’s head over to our UpdateCoffeeDto and see how we could avoid all this redundant code.

// update.coffee.dto.ts
import { PartialType } from '@nestjs/mapped-types';
import { CreateCoffeeDto } from './create-coffee.dto';

export class UpdateCoffeeDto extends PartialType(CreateCoffeeDto) {}

First, let’s remove all of the code inside our UpdateCoffeeDto, and extends this class with helpers function called "PartialTypes()". Let’s make sure we import PartialTypes from @nestjs/mapped-types.

PartialTypes is expecting a Type to be passed inside of it. So let’s pass in CreateCoffeeDto.

This PartialTypes function is really helpful, because what it’s doing for us is, returning the Type of the class we passed into it, with all the properties set to optional; And just like that no more duplicate code!.

PartialTypes not only marks, all the fields is optional, but it also inherits all the validation - rules via decorators, as well as adds a single additional validation rule to each field the @IsOptional() rule on the fly.

Let’s save our changes, wait for the compilation to finish, and head over to insomnia to test this out.

Since all properties are labeled as optional now, thanks to @nestjs/mapped-types package, let’s make a PATCH - request to /coffees/1, and remove everything BUT the "name" key-value in our request - payload.

// request 'PATCH - http://localhost:3002/coffees/1'

// Body - raw: JSON
{
    "name": "Shipwreck Roast",
}

// response, 200 - OK

No 'body' returned for 'response'

Great. It worked just like what we wanted!. You can see we got 200 - OK response back and no validation errors for the missing properties.

If we try too pass in something incorrect, such as Number or a Boolean for "name": value.

// request 'PATCH - http://localhost:3002/coffees/1'

// Body - raw: JSON
{
    "name": true
}

// response, 400 - Bad Request
{
    "statusCode": 400,
    "message": [
        "name must be a string"
    ],
    "error": "Bad Request"
}

We’ll see that UpdateCoffeeDto really did inherit all the validation rules as we get a 400 - Bad Request here as well.

Handling Malicious Request Data

The validationPipe has many other great features. For example, it can filter out properties that should NOT be received by a method - handler, via "whitelisting". By "whitelisting" acceptable properties, any property NOT included in the "whitelist" is automatically stripped from the resulting - object.

Let’s open main.ts an implement "whitelist",

// main.ts
import { NestFactory } from '@nestjs/core';
import { ValidationPipe } from '@nestjs/common';
import { AppModule } from './app.module';

async function bootstrap() {
    const app = await NestFactory.create(AppModule);

    app.useGlobalPipes(new ValidationPipe({
        whitelist: true
    }));
    await app.listen(3002);
}
bootstrap();

We could enable this by simply entering some options to ValidationPipe. In our main.ts file, Let’s pass in an object inside of ValidationPipe with key-value whitelist: true inside of it.

So why is this helpful for us? Well, let’s say we want to avoid users passing in invalid properties to our CoffeesController POST - request when they’re creating New - Coffees.

This "whitelist" feature will make sure all those unwanted or invalid - properties are automatically stripped out and removed. With the power of DTO’s and validationsPie - "whitelist" feature all of this is now possible to us effortlessly.

To see all of this in action, let’s open up CoffeesService and make sure to return the DTO itself - back to the client in our create() - method.

import { Injectable, HttpException, HttpStatus, NotFoundException } from '@nestjs/common';
import { Coffee } from './entities/coffee.entity';

@Injectable()
export class CoffeesService {
    ...
    ...

    create(createCoffeeDto: any) {
        this.coffees.push(createCoffeeDto);

        return createCoffeeDto;         // <<<
    }

    ...
    ...
}

Let’s save our changes, open in insomnia, and send a POST - request to /coffes endpoint.

Let’s pass some properties from our DTO like "name":, "brand": and "flavors":, but let’s pas an additional key-value that ISN’T part of our DTO!. In our case, we’re going to send something random like "isEnabled": true

// request 'POST - http://localhost:3002/coffees'

// Body - raw: JSON
{
    "name": "Shipwreck Roast",
    "brand": "Shipwreck brand",
    "flavors": ["caramel"],
    "isEnabled": true
}

// response, 400 - Bad Request
{
    "name": "Salemba Brew",
    "brand": "Salemba Brand",
    "flavors": [
        "caramel"
    ]
}

Once we hit send, and look at our response, we’ll see only the properties of our DTO are echoed back to us. The rest were automatically removed by the ValidationPipe - "whitelist" feature we just set up.

forbidNonWhitelisted

In addition to this, the ValidationPipe also gives us the option to STOP a request from being processed if any "non-whitelisted" are present. Throwing an error instead.

Let’s head back to our main.ts file, and add the "forbidNonWhitelisted" - option and set it to true.

// main.ts
import { NestFactory } from '@nestjs/core';
import { ValidationPipe } from '@nestjs/common';
import { AppModule } from './app.module';

async function bootstrap() {
    const app = await NestFactory.create(AppModule);

    app.useGlobalPipes(new ValidationPipe({
        whitelist: true,
        forbidNonWhitelisted: true
    }));
    await app.listen(3002);
}
bootstrap();

This property, in combination with "whitelist", will be enable this functionality right away.

Saving our changes, Let’s open up the previous example with unwanted key-values, and hit send again,

// request 'POST - http://localhost:3002/coffees'

// Body - raw: JSON
{
    "name": "Shipwreck Roast",
    "brand": "Shipwreck brand",
    "flavors": ["caramel"],
    "isEnabled": true
}

// response, 400 - Bad Request
{
    "statusCode": 400,
    "message": [
        "property isEnabled should not exist"
    ],
    "error": "Bad Request"
}

We can see now, that the server - responded with an Error and even told us what properties caused this error to happen!.

Auto-Transform Payloads to DTO instances

When we receive request with payloads. These payloads typically come over the network as plain JavaScript - Objects. This is done by design, to help make everything is performing as possible. But how can we ensure, that the payloads come in the shape we expect them to be?

Let’s take look at our POST - request in our CoffeesController and add a console.log() to see what Type our request - body CreateCoffeeDto is, and let’s also check whether it’s an "instanceof" the CreateCoffeeDto" - class.

// coffees.controller.ts
import { Controller, Get, Param, Body, Post, HttpCode, HttpStatus, Res, Patch, Delete, Query } from '@nestjs/common';
import { CoffeesService }from './coffees.service';
import { CreateCoffeeDto } from './dto/create-coffee.dto';
import { UpdateCoffeeDto } from './dto/update-coffee.dto';

@Controller('coffees')
export class CoffeesController {

    constructor(private readonly coffeesService:  CoffeesService) {}

    ...
    ...

    @Post()
    create(@Body() createCoffeeDto: CreateCoffeeDto) {

        console.log("===>", createCoffeeDto instanceof CreateCoffeeDto);
        return result = this.coffeesService.create(createCoffeeDto);
    }

    ...
    ...
}

Now, let’s save our changes, open up insomnia, and hit this POST - endpoint.

If we open up our terminals now, we’ll see "false" was logged.

[Nest] 91872   - 03/24/2021, 11:29:34 PM   [NestApplication] Nest application successfully started +3ms
===> false

So, it turns out our payload may be in the "shape" of CreateCoffeeDto, but it’s not actually an instance of our CreateCoffeeDto - class just yet.

Lucky for us, ValidationPipe can help us transform this Object into exactly what we’re expecting. To enable this behavior globally, let’s head over to our main.ts file,

// main.ts
import { NestFactory } from '@nestjs/core';
import { ValidationPipe } from '@nestjs/common';
import { AppModule } from './app.module';

async function bootstrap() {
    const app = await NestFactory.create(AppModule);

    app.useGlobalPipes(new ValidationPipe({
        whitelist: true,
        transform: true,
        forbidNonWhitelisted: true
    }));
    await app.listen(3002);
}
bootstrap();

We set the "transform:" - options to true, on our global ValidationPipe. Let’s save our changes and test the endpoint again.

[Nest] 91872   - 03/24/2021, 11:32:34 PM   [NestApplication] Nest application successfully started +3ms
===> true

As we could see, the createCoffeeDto instanceof CreateCoffeeDto - expression showed "true" in our terminal now!.

So, what else can this "transform": - ValidationPipe feture do? This auto - transformation feature also performs primitive Type - conversions for things such as Boolean and Numbers.

If we look at our findOne() - GET method within CoffeesController,

// coffees.controller.ts
import { Controller, Get, Param, Body, Post, HttpCode, HttpStatus, Res, Patch, Delete, Query } from '@nestjs/common';
import { CoffeesService }from './coffees.service';
import { CreateCoffeeDto } from './dto/create-coffee.dto';
import { UpdateCoffeeDto } from './dto/update-coffee.dto';

@Controller('coffees')
export class CoffeesController {

    constructor(private readonly coffeesService: CoffeesService) {}

    ...
    ...

    @Get(':id')
    findOne(@Param('id') id: string) {
        return this.coffeesService.findOne(id);
    }
    ...
    ...
}

It takes one argument which represents and extracted 'id' - path parameter, that we know is of Type "Number". However, by default every path - parameter and query - parameter come over the network as a String.

// coffees.controller.ts
import { Controller, Get, Param, Body, Post, HttpCode, HttpStatus, Res, Patch, Delete, Query } from '@nestjs/common';
import { CoffeesService }from './coffees.service';
import { CreateCoffeeDto } from './dto/create-coffee.dto';
import { UpdateCoffeeDto } from './dto/update-coffee.dto';

@Controller('coffees')
export class CoffeesController {

    constructor(private readonly coffeesService:  CoffeesService) {}

    ...
    ...

    @Get(':id')
    findOne(@Param('id') id: number) {
        return this.coffeesService.findOne(id);
    }
    ...
    ...
}

If we changes the Type of 'id' to "Number". ValidationPipe will try to automatically convert the String - identifier to a Number. Just like that.

Since our CoffeesService - findOne() expects String,

// coffees.service.ts
import { Injectable, HttpException, HttpStatus, NotFoundException } from '@nestjs/common';
import { Coffee } from './entities/coffee.entity';

@Injectable()
export class CoffeesService {

    ...
    ...

    findOne(id: string) {               // <<<
        // throw "A Random Error";
        const coffee = this.coffees.find(item => item.id === +id);

        if (!coffee) {
            // throw new HttpException(`Coffee #${id} not found`, HttpStatus.NOT_FOUND);
            throw new NotFoundException(`Coffee #${id} not found`);
        }

        return coffee;
        // return this.coffees.find(item => item.id === +id);
    }

    ...
    ...

}

Let’s temporarily work around this to prevent compilation errors.

// coffees.controller.ts
import { Controller, Get, Param, Body, Post, HttpCode, HttpStatus, Res, Patch, Delete, Query } from '@nestjs/common';
import { CoffeesService }from './coffees.service';
import { CreateCoffeeDto } from './dto/create-coffee.dto';
import { UpdateCoffeeDto } from './dto/update-coffee.dto';

@Controller('coffees')
export class CoffeesController {

    constructor(private readonly coffeesService:  CoffeesService) {}

    ...
    ...

    @Get(':id')
    findOne(@Param('id') id: number) {
        console.log("GET ===>" typeof id)
        return this.coffeesService.findOne('' + id);        // <<<
    }
    ...
    ...
}

To test whether this auto transformation feature works, let’s add a single console.log() inside of this findOne() method to test what Type - "id" is now.

Now, if we call this endpoint using insomnia. Let’s call GET - request on /coffees/1

// request 'GET - http://localhost:3002/coffees/1'

// Body - raw: JSON
{}

// response, 200 - OK
{
    "id": 1,
    "name": "Salemba Roast",
    "brand": "Salemba Brand",
    "flavors": [
        "chocolate",
        "vanilla"
    ]
}

We’ll see the typeof id arguments is, in fact, "number"!.

[Nest] 129771   - 03/24/2021, 11:59:50 PM   [NestApplication] Nest application successfully started +3ms
GET ===> number

As we can see, this feature of validation is incredibly helpful. It not only saves us time, but also helps us be more aware of what Types we’re dealing with, whether they are primitive, like Boilean, Number or even our custom - DTO’s.

Caution
Just be aware that this feature may very slightly impact performance.

So, just make sure it works great for your application, and if speed is essential, test you endpoints to make sure that performance difference is negligible[16]


1. binding
2. A payload in API is, the actual data pack that is sent with the GET method in HTTP. It is the crucial information that you submit to the server when you are making an API request. The payload can be sent or received in various formats, including JSON. Usually, the payload is denoted using the "{}" in a query string
4. An interceptor is, a class annotated with the @Injectable() | https://docs.nestjs.com/interceptors#interceptors
5. The capability of writing a computer program that can be compiled on all platforms without the need to modify its source code . But cannot run on just any platform. Platform dependent typically refers to applications that run under only one operating system in one series of computers.
7. Dependency injection implements IoC {Inversion of Control) through composition so is often identical to that of the strategy pattern, but while the strategy pattern is intended for dependencies to be interchangeable throughout an object’s lifetime, in dependency injection it may be that only a single instance of a dependency is used. | http://en.wikipedia.org/wiki/Dependency_injection
8. data collection
9. Class diagram exemplifying the singleton pattern. In software engineering, the singleton pattern is a software design pattern that restricts the instantiation of a class to one "single" instance. This is useful when exactly one object is needed to coordinate actions across the system. The term comes from the mathematical concept of a singleton. | http://en.wikipedia.org/wiki/Singleton_pattern
14. Decorator based frameworks and libraries for Node and browser. | https://docs.typestack.community/
16. meaningless, insignificant, pointless, inconsiderable