8 min read
Original source

Controllers, routing and the module structure

NestJS is a framework for building Node.js applications. It is somewhat opinionated and forces us to follow its vision of how an application should look like…

NestJS is a framework for building Node.js applications. It is somewhat opinionated and forces us to follow its vision of how an application should look like to some extent. That might be viewed as a good thing that helps us to keep consistency across our application and forces us to follow good practices. NestJS uses Express.js under the hood by default. If you’re familiar with my TypeScript Express series and you’ve enjoyed it, there is a great chance you will like NestJS too. Also, knowledge of the Express framework will come in handy. According to Risingstars, Nest is the fastest-growing Node.js technology in terms of stars on Github in 2019 An important note is that the documentation of NestJS is comprehensive, and you would benefit from looking it up. Here, we attempt to put the knowledge in order, but we also sometimes link to the official docs. We also refer to the Express framework to highlight the advantages of using NestJS. To benefit from this article more, some experience with Express might be useful, but not necessary. If you want to look into the core of Node.js, I recommend checking out the Node.js TypeScript series. It covers topics such as streams, event loop, multiple processes and multithreading with worker threads. Also, knowing how to create an API without any frameworks such as Express and NestJS makes us apprieciate them even more. Getting started with NestJS The most straightforward way of getting started is to clone the official TypeScript starter repository. Nest is built with TypeScript and fully supports it. You could use JavaScript instead, but here we focus on TypeScript.git clone git@github.com:nestjs/typescript-starter.gitA thing worth looking into in the above repository is the  tsconfig.json file. I highly recommend adding the  alwaysStrict and  noImplicitAny options The above repository contains the most basic packages. We also get the fundamental types of files to get us started, so let’s review them. All of the code from this series can be found in this repository. Hopefully, it can later serve as a NestJS boilerplate with some built-in features. It is a fork of an official typescript-starter. Feel free to give both of them a star. Controllers Controllers handle incoming requests and return responses to the client. The  typescript-starter repository contains our first controller. Let’s create a more robust one: posts.controller.ts import { Body, Controller, Delete, Get, Param, Post, Put } from '@nestjs/common'; import PostsService from './posts.service'; import CreatePostDto from './dto/createPost.dto'; import UpdatePostDto from './dto/updatePost.dto'; @Controller('posts') export default class PostsController { constructor( private readonly postsService: PostsService ) {} @Get() getAllPosts() { return this.postsService.getAllPosts(); } @Get(':id') getPostById(@Param('id') id: string) { return this.postsService.getPostById(Number(id)); } @Post() async createPost(@Body() post: CreatePostDto) { return this.postsService.createPost(post); } @Put(':id') async replacePost(@Param('id') id: string, @Body() post: UpdatePostDto) { return this.postsService.replacePost(Number(id), post); } @Delete(':id') async deletePost(@Param('id') id: string) { this.postsService.deletePost(Number(id)); } } post.interface.ts export interface Post { id: number; content: string; title: string; }The first thing that we can notice is that NestJS uses decorators a lot. To mark a class to be a controller, we use the  @Controller() decorator. We pass an optional argument to it. It acts as a path prefix to all of the routes within the controller. Routing The next set of decorators connected to routing in the above controller are  @Get(),  @Post(),  Delete(),  and  @Put(). They tell Nest to create a handler for a specific endpoint for HTTP requests. The above controller creates a following set of endpoints:GET /posts Returns all posts GET /posts/{id} Returns a post with a given id POST /posts Creates a new post PUT /posts/{id} Replaces a post with a given id DELETE /posts/{id} Removes a post with a given id By default, NestJS responds with a 200 OK status code with the exception of 201 Created for the POST. We can easily change that with the  @HttpCode() decorator. When we implement an API, we often need to refer to a specific element. We can do so with route parameters. They are special URL segments used to capture values specified at their position. To create a route parameter, we need to prefix its name with the  : sign. The way to extract the value of a route parameter is to use the  @Param() decorator. Thanks to it, we have access to it in the arguments of our route handler. We can use an optional argument to refer to a specific parameter, for example  @Param('id'). Otherwise, we get access to the params object with all parameters. Since route parameters are strings and our ids are number, we need to convert the params first. We can also use pipes to transform the route params. Pipes are built-in feature in NestJS and we cover them later. Accessing the body of a request When we handle POST and PUT in the controller above, we also need to access the body of a request. By doing so, we can use it to populate our database. NestJS provides a  @Body() decorator that gives us easy access to the body. Just as in the TypeScript Express series, we introduce the concept of a Data Transfer Object (DTO). It defines the format of the data sent in a request. It can be either an interface or a class, but using the latter gives us more possibilities and we explore them later on. createPost.dto.ts class CreatePostDto { content: string; title: string; } updatePost.dto.ts class UpdatePostDto { id: number; content: string; title: string; } The arguments of a handler function Let’s look into the arguments of the handler function a bit more.async replacePost(@Body() post: UpdatePostDto, @Param('id') id: string) { return this.postsService.replacePost(Number(id), post); }By using the method argument decorators, we tell Nest to inject particular arguments into our methods. NestJS is built around the concept of Dependency Injection and Inversion of Control. We elaborate on in a lot as we go through various features. Dependency Injection is one of the techniques that implement Inversion of Control. If you want read a bit more about IoC, check out Applying SOLID principles to your TypeScript code An important note is that reversing their order yields the same result, which might seem counter-intuitive at first.async replacePost(@Param('id') id: string, @Body() post: UpdatePostDto) { return this.postsService.replacePost(Number(id), post); } Advantages of NestJS over Express NestJS gives us a lot of things out of the box and expects us to design our API using controllers. Express.js, on the other hand, leaves us more flexibility but does not equip us with such tools to maintain the readability of our code. We are free to implement controllers on our own with Express. We do it in the TypeScript Express series.import { Request, Response, Router } from 'express'; import Controller from '../../interfaces/controller.interface'; import PostsService from './posts.service'; import CreatePostDto from './dto/createPost.dto'; import UpdatePostDto from './dto/updatePost.dto'; export default class PostsController implements Controller { private path = '/posts'; public router = Router(); private postsService = new PostsService(); constructor() { this.intializeRoutes(); } intializeRoutes() { this.router.get(this.path, this.getAllPosts); this.router.get(`${this.path}/:id`, this.getPostById); this.router.post(this.path, this.createPost); this.router.put(`${this.path}/:id`, this.replacePost); } private getAllPosts = (request: Request, response: Response) => { const posts = this.postsService.getAllPosts(); response.send(posts); } private getPostById = (request: Request, response: Response) => { const id = request.params.id; const post = this.postsService.getPostById(Number(id)); response.send(post); } private createPost = (request: Request, response: Response) => { const post: CreatePostDto = request.body; const createdPost = this.postsService.createPost(post); response.send(createdPost); } private replacePost = (request: Request, response: Response) => { const id = request.params.id; const post: UpdatePostDto = request.body; const replacedPost = this.postsService.replacePost(Number(id), post); response.send(replacedPost); } private deletePost = (request: Request, response: Response) => { const id = request.params.id; this.postsService.deletePost(Number(id)); response.sendStatus(200); } }Above, we can see a similar controller created in pure Express. There are quite a few notable differences. First, we need to handle the routing of the controller ourselves. We don’t have such convenient decorators that we can depend on to do it for us. The way NestJS works here resembles a bit the Spring Framework written for Java. In the TypeScript Express series, we use an Application class that attaches the routing to the app.class Application { // ... private initializeControllers(controllers: Controller[]) { controllers.forEach((controller) => { this.app.use('/', controller.router); }); } // ... }Another big advantage of NextJS is that it provides us with an elegant way of handling the Request and Response objects. Decorators such as  @Body() and  @Params() help to improve the readability of our code. One of the most useful things Nest has to offer is how it handles responses. Our route handlers can return primitive types (for example, strings), promises, or even RxJS observable streams. We don’t need to handle it manually ev

Controllers, routing and the module structure | NestJS.io