API with NestJS #1. Controllers, routing and the module structure

JavaScript NestJS TypeScript

This entry is part 1 of 14 in the API with NestJS

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.

NestJS Github stars

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.

A thing worth looking into in the above repository is the   file. I highly recommend adding the   and   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   repository contains our first controller. Let’s create a more robust one:

posts.controller.ts

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   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  ,  and  . They tell Nest to create a handler for a specific endpoint for HTTP requests. The above controller creates a following set of endpoints:

Returns all posts

Returns a post with a given id

Creates a new post

Replaces a post with a given 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   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   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  . Otherwise, we get access to the  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   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

updatePost.dto.ts

The arguments of a handler function

Let’s look into the arguments of the handler function a bit more.

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.

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.

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  class that attaches the routing to the app.

Another big advantage of NextJS is that it provides us with an elegant way of handling the Request and Response objects. Decorators such as   and   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 every time and use the   function. NestJS also makes it easy to handle errors in our application, and we explore it in the upcoming parts of this series.

When using NestJS, we can also manipulate the Request and Response objects directly. Handling responses ourselves strips us from some of the advantages of NestJS though.

There is also a difference in how we can handle dependencies in pure Express and NestJS.

In the above Express controller, we create a new   directly in the  . Unfortunately, it breaks the Dependency inversion principle from the SOLID principles. One of the issues that can cause is some trouble with writing tests.

NestJS, on the other hand, cares about compliance with the Dependency inversion principle a lot by implementing Dependency Injection.

Services

The   repository also contains our first service. A job of a service is to separate the business logic from controllers, making it cleaner and more comfortable to test. Let’s create a simple service for our posts.

posts.service.ts

While the above logic is straightforward, there are a few noteworthy lines there.

We use the built-in   class to throw errors that NestJS can understand. When we throw  , it gets propagated to the global exception filter, and a proper response is sent to the client. We explore this topic more in the upcoming parts of this series.

NestJS error

The   decorator tells Nest that this class is a provider. Thanks to that, we can add it to a module.

Modules

We use modules to organize our application. Our   and   are closely related and belong to the same application domain. Therefore, it is appropriate to put them in a module together.

By doing so, we organize our code per feature. This is especially useful as our application grows.

posts.module.ts

Also, every application needs a root module. It is a starting point for Nest when building the application.

app.module.ts

The module contains:

  • imports
    imported modules – NestJS uses the   thanks to importing it in our
  • controllers
    controllers to instantiate
  • providers
    providers to instantiate – they may be used at least across this module
  • exports
    a subset of providers that are available in other modules

Summary

By doing all of the above, our src directory ends up like that:

In this article, we’ve just got started with Nest. We’ve figured out what is a Controller and how to handle elementary routing in our application. We’ve also briefly touched the topic of Services and Modules. In the upcoming parts of this series, we will spend quite some time discussing the application structure in NestJS.

All of the above knowledge is just the tip of the NestJS iceberg. Hopefully, it convinces you that it is worth looking into this framework as it provides lots of value. There is a lot to say about features that Nest delivers, such as neat error handling and dependency injection. We will also look into the PostgreSQL database and how to use it both through ORM and SQL statements.

You can expect it in this series, and more, so stay tuned!

Series NavigationAPI with NestJS #2. Setting up a PostgreSQL database with TypeORM >>
Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments