Build a REST API from scratch using NestJS

Rui Coelho
8 min readSep 20, 2020

--

What is NestJS?

As mentioned at NestJS Documentation, Nest (NestJS) is a framework for building efficient, scalable Node.js server-side applications. It uses progressive JavaScript, is built with and fully supports TypeScript (yet still enables developers to code in pure JavaScript) and combines elements of OOP (Object Oriented Programming), FP (Functional Programming), and FRP (Functional Reactive Programming).

Under the hood, Nest makes use of robust HTTP Server frameworks like Express (the default) and optionally can be configured to use Fastify as well!

Nest provides a level of abstraction above these common Node.js frameworks (Express/Fastify), but also exposes their APIs directly to the developer. This gives developers the freedom to use the myriad of third-party modules which are available for the underlying platform.

Installing NestJS

To install NestJS we simple use npm and run:

npm i -g @nestjs/cli

This will install Nest in a global way on our machine.

Create a new project

To start a new project we simply run:

nest new project-name

It is also possible to install the TypeScript starter project with Git:

git clone https://github.com/nestjs/typescript-starter.git project
cd project
npm install

You can also manually create a new project from scratch by installing the core and supporting files with npm (or yarn). In this case, of course, you’ll be responsible for creating the project boilerplate files yourself.

npm i --save @nestjs/core @nestjs/common rxjs reflect-metadata 

Getting Started

The project directory will be created, node modules and a few other boilerplate files will be installed, and a src/ directory will be created and populated with several core files.

App Controller

Basic controller sample with a single route

App Module

The root of the application

Main

The entry file of the application which uses the core function NestFactory to create a Nest application instance.

Let’s Code

In our example we will start by deleting app.controller.ts and generate a new controller using Nest Cli:

nest g controller items

This command will generate src/items/items.controller.ts , our items controller. In order to create endpoints on our controller we use decorators such as @Get, @Post, @Put, @Delete.

Find all items

Let’s start by creating a simple findAll()method and findById() :

We will be back to this methods later, we want this endpoint to return real data using an database and not an hardcoded string. Now if test our endpoint we will be able to check the hardcoded return.

Get All Items — VSCode Rest Client
Get Item By ID

There are several ways to run our application:

npm start
npm run start:dev

Throughout this article we will use the second option since the server listens for new changes and automatically restarts our application without any interference on our part.

Create Items

The POST request is a bit more complex, to add an item we must create a DTO (Data Transfer Object) class with the fields we want. Let’s create our DTO file into scr/items/dto/item.dto.ts

Now let’s import out DTO into our Controller and start write our POST endpoint:

For now we will just return our input from json body, later we will be back to insert data on MongoDB.

POST Endpoint Test

Create Providers

As mentioned in NestJS Documentation, “providers are a fundamental concept in Nest. Many of the basic Nest classes may be treated as a provider — services, repositories, factories, helpers, and so on. The main idea of a provider is that it can inject dependencies; this means objects can create various relationships with each other, and the function of “wiring up” instances of objects can largely be delegated to the Nest runtime system. A provider is simply a class annotated with an @Injectable() decorator.”

Let’s start by creting a simple Items Service using NestJS scaffold mechanism by typing on the console:

nest g service items

Our ItemsService is a basic class with one property and two methods. The only new feature is that it uses the @Injectable() decorator. The @Injectable() decorator attaches metadata, which tells Nest that this class is a Nest provider.

Our service uses a Item interface, we can define our interface in a very simple way:

Wrap everything in a Module

Before we go any further this is the ideal time to create a module where we will wrap all the code related to the items. This is very simple to do, we just need to create a file called src/items/items.module.ts

Don’t forget to add the ItemsModule to src/app.module.ts on @Module({imports:[ItemsModule], controllers: [], providers:[]}) .

Deploy MongoDB Instance

To deploy our MongoDB we will use Docker, although other tools like Mongo can be used.

docker pull mongo
docker run -p 27017:27017 --name mongodb -d mongo

This instance is not password protected and can be accessed directly through localhost.

Setup Mongoose

Nest supports two methods for integrating with the MongoDB database. You can either use the built-in TypeORM module described here, which has a connector for MongoDB, or use Mongoose, the most popular MongoDB object modeling tool. In this chapter we’ll describe the latter, using the dedicated @nestjs/mongoose package.

Start by installing the required dependencies:

npm install --save @nestjs/mongoose mongoose
npm install --save-dev @types/mongoose
npm install --save @nestjs/config

Once the installation process is complete, we can import the MongooseModule into the root AppModule :

Since we don’t want to put our hardcoded connection string, we also have to add our Config Service to imports. After that we need to create a .env file on our root. The .env should look like this:

MONGOURI=mongodb://localhost:27017
PORT=3000

Let’s load a configuration file that loads our environment variables (src/config/configuration.ts):

So we have our connection to the Mongo database configured.

Load Port Configuration

Once we have created a configuration service, we will then load the port we want our application to be listening on. To achieve this we just have to change our src/main.ts file:

Models

With Mongoose, everything is derived from a Schema. Each schema maps to a MongoDB collection and defines the shape of the documents within that collection. Schemas are used to define Models. Models are responsible for creating and reading documents from the underlying MongoDB database.

Schemas can be created with NestJS decorators, or with Mongoose itself manually. Using decorators to create schemas greatly reduces boilerplate and improves overall code readability.

Let’s define ItemSchema :

The @Schema() decorator marks a class as a schema definition. It maps our Item class to a MongoDB collection of the same name, but with an additional “s” at the end - so the final mongo collection name will be items.

The @Prop() decorator defines a property in the document. For example, in the schema definition above, we defined three properties: name, description , and quantity. The schema types for these properties are automatically inferred thanks to TypeScript metadata (and reflection) capabilities.

Now let’s add our Schema to Items Module(src/items/items.module.ts):

Inject Models

Since we are using Dependency Injection, our service has to inject our models. The injection is done in the constructor as follows:

Using Models

Now we have to replace everything we have hardcoded with our models using mongoose. In our service once the mongoose returns a promise our type of return will be a promise of items.

We also have to update our controller by calling our service now and returning the correct promises. Don’t forget to make the controller asynchronous.

Now we can test our API and check that everything is working:

Get All Items
Get Item By Id
Post Request

We can also create methods for deleting and changing information, for that we have to add to our controller:

In our Items Service we have to add the methods corresponding to these actions:

We can now test our new endpoints:

PUT Request
Delete Request

If you are not sure that the item has been deleted we can always invoke the endpoint that returns us the list of all items:

Get All Items

Logger Service

Nest comes with a built-in text-based logger which is used during application bootstrapping and several other circumstances such as displaying caught exceptions (i.e., system logging). This functionality is provided via the Logger class in the @nestjs/common package. You can find more information in the official documentation.

To implement the Logger we will start by creating a service with NestCLI:

 nest g service logger

After that we just have to add the scope to our logger service and we are finished with this component.

Transient providers are not shared across consumers. Each consumer that injects a transient provider will receive a new, dedicated instance.

We wrap everything in a module like we did with our item module:

So we have finished configuring the logger, we now have to add the LoggerModule to our imports in src/app.module.ts , something like @Module({imports:[LoggerModule, ...], controllers:[...], providers:[...]}) . Finally, we just have to add to the modules where we belong to use our logger, in the case of our example we just want to use in the endpoints related to the items:

In order to be able to use the logger, we have to use dependency injection as in our Item Service:

We can now test and verify that everything is working:

Logger Test

And so we arrived at the basic functioning of a REST API using NestJS.

Conclusions

NestJS is a good working tool that allows us to use concepts like Dependency Injection on ExpressJS without having to install directly from other frameworks like InversifyJS.
The development using this framework is simple and intuitive and, in addition, its documentation is very well defined and allows easy learning.

Source Code

The code for the entire project is available on my github.

--

--