Components

Almost everything is a component - Service, Repository, Provider etc. and they might be injected to controllers or to another component by constructor.

In previous section, we built a simple controller - UsersController. This controller has an access to our data (I know, it's a fake data, but it doesn't really matter here). It's not a good solution. Our controllers should only handle HTTP requests and delegate more complex tasks to services. This is why we are going to create UsersService component.

In real world, UsersService should call appropriate method from persistence layer e.g. UsersRepository component. We don't have any kind of database, so again - we will use fake data.

import { Component } from '@nestjs/common';
import { HttpException } from '@nestjs/core';

@Component()
export class UsersService {
    private users = [
        { id: 1, name: "John Doe" },
        { id: 2, name: "Alice Caeiro" },
        { id: 3, name: "Who Knows" },
    ];
    getAllUsers() {
        return Promise.resolve(this.users);
    }
    getUser(id: number) {
        const user = this.users.find((user) => user.id === id);
        if (!user) {
            throw new HttpException("User not found", 404);
        }
        return Promise.resolve(user);
    }
    addUser(user) {
        this.users.push(user);
        return Promise.resolve();
    }
}

Nest Component is a simple class, with @Component() annotation.

As might be seen in getUser() method we used HttpException. It is a Nest built-in Exception, which takes two parameters - error message (or full object) and status code. It is a good practice to create domain exceptions, which should extend HttpException (more about it in "Advanced/Error Handling" section).

Our service is prepared, let's use it in UsersController from previous article.

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

    @Get()
    getAllUsers(@Response() res) {
        this.usersService.getAllUsers()
            .then((users) => res.status(HttpStatus.OK).json(users));
    }

    @Get('/:id')
    getUser(@Response() res, @Param('id') id) {
        this.usersService.getUser(+id)
            .then((user) => res.status(HttpStatus.OK).json(user));
    }

    @Post()
    addUser(@Response() res, @Body('user') user) {
        this.usersService.addUser(user)
            .then((msg) => res.status(HttpStatus.CREATED).json(msg));
    }
}

As shown, UsersService will be injected into constructor.

It is incredibly easy to manage dependencies with TypeScript, because Nest will recognize your dependencies just by type! So this:

constructor(private usersService: UsersService)

Is everything what you have to do. There is one important thing to know - you must have emitDecoratorMetadata option set to true in your tsconfig.json.

If you are not TypeScript enthusiast and you work with plain JavaScript, you have to do it in this way:

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

@Controller('users')
@Dependencies(UsersService)
export class UsersController {
    constructor(usersService) {
        this.usersService = usersService;
    }

    @Get()
    getAllUsers(@Response() res) {
        this.usersService.getAllUsers()
            .then((users) => res.status(HttpStatus.OK).json(users));
    }

    @Get('/:id')
    getUser(@Response() res, @Param('id') id) {
        this.usersService.getUser(+id)
            .then((user) => res.status(HttpStatus.OK).json(user));
    }

    @Post()
    addUser(@Response() res, @Body('user') user) {
        this.usersService.addUser(user)
            .then((msg) => res.status(HttpStatus.CREATED).json(msg));
    }
}

Simple, right?

In this moment, application will not even start working.

Why? Because Nest doesn't know anything about UsersService. This component is not a part of ApplicationModule yet. We have to add it there:

import { Module } from '@nestjs/common';
import { UsersController } from './users.controller';
import { UsersService } from './users.service';

@Module({
    controllers: [ UsersController ],
    components: [ UsersService ],
})
export class ApplicationModule {}

That's it! Now, our application will run, but still one of routes doesn't work properly - addUser (POST /users). Why? Because we are trying to extract request body (req.body.user) without body-parser express middleware. As you should already know, it is possible to pass express instance as a second argument of NestFactory.create() method.

Let's install plugin:

$ npm install --save body-parser

Then setup it in our express instance.

import * as express from 'express';
import * as bodyParser from 'body-parser';
import { NestFactory } from '@nestjs/core';
import { ApplicationModule } from './modules/app.module';

const instance = express();
instance.use(bodyParser.json());

const app = NestFactory.create(ApplicationModule, instance);
app.listen(3000, () => console.log('Application is listening on port 3000'));

The end!

Async / await

Nest is compatible with async / await feature from ES7, so we can quickly rewrite our UsersController:

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

    @Get()
    async getAllUsers(@Response() res) {
        const users = await this.usersService.getAllUsers();
        res.status(HttpStatus.OK).json(users);
    }

    @Get('/:id')
    async getUser(@Response() res, @Param('id') id) {
        const user = await this.usersService.getUser(+id);
        res.status(HttpStatus.OK).json(user);
    }

    @Post()
    async addUser(@Response() res, @Body('user') user) {
        const msg = await this.usersService.getUser(user);
        res.status(HttpStatus.CREATED).json(msg);
    }
}

Looks better right? There you can read more about async / await.

results matching ""

    No results matching ""