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 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')exportclassEntryController{@Get()index():Entry[]{constentries:Entry[]=this.entriesService.findAll();returnentries;}
We’ll go over the details of routing and handling requests in the Routing and Request Handling chapter.
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()exportclassAuthenticationService{constructor(privatereadonlyuserService:UserService){}asyncvalidateUser(payload:{string;password:string;}):Promise<boolean>{constuser=awaitthis.userService.findOne({where:{payload.email}});return!!user;}}
We’ll talk more about dependency injection in the the Dependency Injection chapter.
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:[],})exportclassAppModuleimplementsNestModule{}
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],})exportclassEntryModuleimplementsNestModule{}
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.
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';asyncfunctionbootstrap() {constapp=awaitNestFactory.create(AppModule);awaitapp.listen(3000);}bootstrap();
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*aspassportfrom'passport';import{UserService}from'../../modules/user/user.service';@Injectable()exportclassAuthenticationMiddlewareimplementsNestMiddleware{constructor(privateuserService:UserService){}asyncresolve(strategy:string):Promise<MiddlewareFunction>{returnasync(req,res,next)=>{returnpassport.authenticate(strategy,async(...args:any[])=>{const[,payload,err]=args;if(err){returnres.status(HttpStatus.BAD_REQUEST).send('Unable to authenticate the user.');}constuser=awaitthis.userService.findOne({where:{payload.email}});req.user=user;returnnext();})(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:[]})exportclassAppModuleimplementsNestModule{publicconfigure(consumer:MiddlewareConsumer){constuserControllerAuthenticatedRoutes=[{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 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()exportclassCheckLoggedInUserGuardimplementsCanActivate{canActivate(context:ExecutionContext):boolean|Promise<boolean>|Observable<boolean>{constreq=context.switchToHttp().getRequest();returnNumber(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')exportclassUserController{constructor(privatereadonlyuserService:UserService){}@Get(':userId')@UseGuards(CheckLoggedInUserGuard)show(@Param('userId')userId:number){constuser:User=this.userService.findById(userId);returnuser;}
In this chapter we covered Nest.js controllers, providers, modules, bootstrapping, and middleware. In the next chapter we will go over Nest.js authentication.