Chapter 2. Overview

In this chapter we’ll take an overview of Nest.js and look at the core concepts that you’ll need to build a Nest.js application.

Controllers

Controllers in Nest are responsible for handling incoming requests and returning responses to the client. Nest will route incoming requests to handler functions in controller classes. We use the @Controller() decorator to create a controller class.

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

@Controller('entries')
export class EntryController {
    @Get()
    index(): Entry[] {
        const entries: Entry[] = this.entriesService.findAll();
        return entries;
    }

We’ll go over the details of routing and handling requests in the Routing and Request Handling chapter.

Providers

Providers in Nest are used to create services, factories, helpers, and more that can be injected into controllers and other providers using Nest’s built-in dependency injection. The @Injectable() decorator is used to create a provider class.

The AuthenticationService in our blog application, for example, is a provider that injects and uses the UsersService component.

@Injectable()
export class AuthenticationService {
    constructor(private readonly userService: UserService) {}

    async validateUser(payload: {
        email: string;
        password: string;
    }): Promise<boolean> {
        const user = await this.userService.findOne({
            where: { email: payload.email }
        });
        return !!user;
    }
}

We’ll talk more about dependency injection in the the Dependency Injection chapter.

Modules

A Nest.js application is organized into modules. If you’re familiar with modules in Angular, then the module syntax Nest uses will look very familiar.

Every Nest.js application will have a root module. In a small application, this may be the only module. In a larger application, it makes sense to organize your application into multiple modules that split up your code into features and related capabilities.

A module in Nest.js is a class with a @Module() decorator. The @Module() decorator takes a single object that describes module using the following properties.

Property Description
components The components to be instantiated that may be shared across this module and exported to be available to other modules
controllers The controllers that are created by this module
imports The list of modules to import that export components that are requires in this module
exports The list of components from this module to be made available to other modules

In our example application, the root Module is named AppModule and the application is split up into a number of sub-modules that handle the major parts of the application such as authentication, comments, database access, blog entries and users.

@Module({
    components: [],
    controllers: [],
    imports: [
        DatabaseModule,
        AuthenticationModule.forRoot('jwt'),
        UserModule,
        EntryModule,
        CommentModule,
        UserGatewayModule,
        CommentGatewayModule
    ],
    exports: [],
})
export class AppModule implements NestModule {}

The AppModule imports the modules that are needed for the application. The root module in our application doesn’t need to have any exports since no other modules import it.

The root module also doesn’t have any components or controllers, as these are all organized within the sub-modules they are related to. The EntryModule, for example, includes both components and controllers that are related to blog entries.

@Module({
    components: [entryProvider, EntryService],
    controllers: [EntryController],
    imports: [],
    exports: [EntryService],
})
export class EntryModule implements NestModule {}

Modules in Nest.js are singletons by default. This means that you can share the same instance of an exported component, such as the EntryService above, between modules without any effort.

Bootstrapping

Every Nest.js application needs to be bootstrapped. This is done by by using the NestFactory to create the root module and calling the listen() method.

In our example application, the entry point is main.ts and we use the async / await pattern to create the AppModule and call listen():

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

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

Middleware

Nest.js middleware is either a function or a class decorated with the @Injectable() decorator that implements the NestMiddleware interface. Middleware is called before route handlers. These functions have access to the request and response object, and they can makes changes to the request and response object.

One or more middleware functions can be configured for a route, and a middleware function can choose to pass the execution to the next middleware function on the stack or to end the request-response cycle.

If a middleware function does not end the request-response cycle, it must call next() to pass control to the next middleware function or to the request handler if it is the last function on the stack. Failing to do so will leave the request hanging.

The AuthenticationMiddleware in our blog application, for example, is responsible for authenticating a user that is accessing the blog.

import {
    MiddlewareFunction,
    HttpStatus,
    Injectable,
    NestMiddleware
} from '@nestjs/common';
import * as passport from 'passport';
import { UserService } from '../../modules/user/user.service';

@Injectable()
export class AuthenticationMiddleware implements NestMiddleware {
    constructor(private userService: UserService) {}

    async resolve(strategy: string): Promise<MiddlewareFunction> {
        return async (req, res, next) => {
            return passport.authenticate(strategy, async (...args: any[]) => {
                const [, payload, err] = args;
                if (err) {
                    return res
                        .status(HttpStatus.BAD_REQUEST)
                        .send('Unable to authenticate the user.');
                }

                const user = await this.userService.findOne({
                    where: { email: payload.email }
                });
                req.user = user;
                return next();
            })(req, res, next);
        };
    }
}

If authentication fails, a 400 response is sent back to the client. If authentication is successful, then next() is called and the request will continue down the middleware stack until it reaches the request handler.

Middleware is configured on routes in the configure() function of a Nest.js module. For example, the AuthenticationMiddle above is configured in the AppModule as shown here.

@Module({
    imports: [
        DatabaseModule,
        AuthenticationModule.forRoot('jwt'),
        UserModule,
        EntryModule,
        CommentModule,
        UserGatewayModule,
        CommentGatewayModule,
        KeywordModule
    ],
    controllers: [],
    providers: []
})
export class AppModule implements NestModule {
    public configure(consumer: MiddlewareConsumer) {
        const userControllerAuthenticatedRoutes = [
            { path: '/users', method: RequestMethod.GET },
            { path: '/users/:id', method: RequestMethod.GET },
            { path: '/users/:id', method: RequestMethod.PUT },
            { path: '/users/:id', method: RequestMethod.DELETE }
        ];

        consumer
            .apply(AuthenticationMiddleware)
            .with(strategy)
            .forRoutes(
                ...userControllerAuthenticatedRoutes,
                EntryController,
                CommentController
            );
    }
}

You can apply middleware to all routes on a controller, as is done for the EntryController and CommentController. You can also apply middleware to specific routes by their path, as is done for the subset of routes from the UserController.

Guards

Guards are classes that are decorated with the @Injectable() decorator and implement the CanActivate interface. A guard is responsible for determining if a request should be handled by a route handler or route. Guards are executed after every middleware, but before pipes. Unlike middleware, guards have access to the ExecutionContext object, so they know exactly what is going to evaluated.

In our blog application, we use the CheckLoggedInUserGuard in the UserController to only allow a user to access and access their own user information.

import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Observable } from 'rxjs';

@Injectable()
export class CheckLoggedInUserGuard implements CanActivate {
    canActivate(
        context: ExecutionContext
    ): boolean | Promise<boolean> | Observable<boolean> {
        const req = context.switchToHttp().getRequest();
        return Number(req.params.userId) === req.user.id;
    }
}

The @UseGuards decorator is used to apply a guard to a route. This decorator can be used on a controller class to apply the guard to all routes in that controller, or it can be used on individual route handlers in a controller as seen in the UserController:

@Controller('users')
export class UserController {
    constructor(private readonly userService: UserService) { }

    @Get(':userId')
    @UseGuards(CheckLoggedInUserGuard)
    show(@Param('userId') userId: number) {
        const user: User = this.userService.findById(userId);
        return user;
    }

Summary

In this chapter we covered Nest.js controllers, providers, modules, bootstrapping, and middleware. In the next chapter we will go over Nest.js authentication.