PART 0: Fundamentals of NEST.js

PART 0: Fundamentals of NEST.js

Hi there, this is PART 0 of my NEST.js series. In this chapter I will explain the fundamentals of NEST.js, what it is and explain a bit of how it is structured. Therefore we use the provided CLI to create a brand new project. Afterwards we are going through each generated file and what purpose it has.

What is NEST.js?

Here is what the official documentation says.

Nest (NestJS) is a framework for building efficient, scalable Node.js server-side applications* Under the hood, Nest makes use of robust HTTP Server frameworks like [Express](expressjs.com) (the default) and optionally can be configured to use [Fastify*(github.com/fastify/fastify) as well!

So this means, that a lot of smart people put much effort in building on top of express (or fastify) a set of pre-built capabilities like logging or validation. This is great, because you dont have to built this by yourself, which means that you can focus on what matters, the business logic. Futhermore you can still use node libaries that you are used to, which is awesome, the best of both.

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 ofthird-party modules which are available for the underlying platform.

Your first NEST.js project

We start by installing the CLI provided by NEST npm i -g @nestjs/cli

We use the CLI to create a new project

nest new demo_project

This creates a couple of files.

  • app.controller.ts A basic controller with a single route.
  • app.controller.spec.ts The unit tests for the controller.
  • app.module.ts The root module of the application.
  • app.service.ts A basic service with a single method.
  • main.ts The entry file of the application

main.ts - the starting point

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

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

To create a Nest application instance, we use the core NestFactory class. NestFactory exposes a few static methods that allow creating an application instance. The create() method returns an application object, which will be assigned to the app variable. Afterwards we call the listen function to start the application and pass the port number (3000) as an parameter. Run the command to start the application.

npm run start

Once the application is running, open your browser and navigate to http://localhost:3000/. You should see the Hello World! message. Congratz, you just launched your first NEST.js backend

app.module.ts - The root module of the application

In NEST.js you structure your code as modules. You know this from React/Vue/Angular, where you create components. It is the same idea, but a module in NEST.js is a bit more abstract, because it can contains multiple controllers or services. Just remember, functionalitiy that belongs to the same application domain it's wrapped with a module.

Each application has at least one module, a root module. The root module is the starting point Nest uses to build the application graph - the internal data structure Nest uses to resolve module and provider relationships and dependencies.

In the app.module.ts we register all modules that are used in the application. So if we create a new module called users (using the CLI)

nest generate module users

Make sure it is defined in the imports section of the app.module.ts

import { Module } from '@nestjs/common'
import { UsersModule } from './users/users.module'
import { AppController } from './app.controller'
import { AppImports } from './app.imports'
import { AppService } from './app.service'

@Module({

imports: [
UsersModule,
],
controllers: [AppController, HealthController],
providers: [AppService, TypegooseHealthIndicator],
})
export class AppModule {}

app.controller.ts - A basic controller with a single route

In general, a controller is responsible for handling incoming requests. In addition the controller also does input validation. If you have business logic or resuable functionality, we outsource this in a service class (which we will discuss later). The service class is provided by a dependency injection. NEST is using decorators a lot. In order to tell NEST that we want to make a file to an controller, we use the @Controller() decorator above the class definition.

import { Controller, Get } from  '@nestjs/common';
import { AppService } from  './app.service';

@Controller()
export  class  AppController {
constructor(private  readonly  appService:  AppService) {}

// reachable at localhost:3000/
@Get()
getHello():  string {
return  this.appService.getHello();
}
// reachable at localhost:3000/profile
@Get('profile')
getHello():  string {
return  this.appService.returnProfile();
}
-----------------------------------------------------------------
// make the controller accessible at localhost:3000/cats
@Controller('cats')
export  class  AppCatController {
constructor(private  readonly  appService:  AppService) {}

// reachable at localhost:3000/cats/profile
@Get('profile')
getHello():  string {
return  this.appService.returnProfile();
}

Remember the Hello World when you navigated to localhost:3000? The getHello() Function was responsible for that. Above the getHello()function you see another Decorator, @Get() , which is responsible for handling the GET Requests. What about the other HTTP request methods? Well clearly NEST provides them as well

@Get(), @Post(), @Put(), @Delete(), @Patch(), @Options(), and @Head(). In addition, @All() defines an endpoint that handles all of them.

Check out the code example above to learn more about the routes.

app.service.ts - A basic service with a single method.

A service (declared with the @Injectable() decorator) will be responsible for data storage and retrieval or business logic, and is designed to be used by the Controller, so it's a good candidate to be defined as a provider. 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 be injected as dependency; 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.

import { Injectable } from  '@nestjs/common';
@Injectable()
export  class  AppService {

getHello():  string {
    return  'Hello World!';
    }
}

app.controller.spec.ts - The unit tests for the controller.

How do you assume that your controller is working the way it should? Well NEST also handles this for us. If we create a new module with the CLI, NEST automatically adds a .spec file, a file, where we can define tests. NEST is using JEST and Supertest frameworks for this purpose.

import { Test, TestingModule } from  '@nestjs/testing';
import { AppController } from  './app.controller';
import { AppService } from  './app.service';

describe('AppController', () => {

let  appController:  AppController;

beforeEach(async () => {

const  app:  TestingModule  =  await  Test.createTestingModule({

controllers: [AppController],

providers: [AppService],

}).compile();

appController  =  app.get<AppController>(AppController);

});

describe('root', () => {

it('should return "Hello World!"', () => {

    expect(appController.getHello()).toBe('Hello World!');
});
 }); });

Whats happening here? In JEST there are HOOKS like in React. beforeEach is on of these Hooks. It is called before each test. There an app object is being created like in the main.ts file. From there we can access the Controller we want to test, in this case, the AppController. In JEST we group multiple tests with describe(). As an first parameter we pass a string, defining the group, as an second, we pass an anonymous function. Inside we make use of the it() function. With the it() function we define a specific test. Like the describe() function, the first parameter is used to describe the test, the second an anonymous function to write the test logic. There we make use the expect() func, where we basically check if the getHello() function from the controller is returning 'Hello World'. Go to console and type:

npm run test

jest successful tests JEST will run our test (.spec) files. Yep, everything working as expected, nice. If you want to learn more about JEST and how to write tests, check out the documentation.

Summary

Lets finish this section with a picture. It illustrates a Module, which is responsible for the user handling in our backend. It shows the tasks and the relationships between the different parts we discussed.

Relationship between Module, Controller, Service in NEST:js

Lessons learned

  • NEST.js is built on top of (express/fastify) with batteries included, but offers you the freedom to use node related libaries

  • A module wraps functionality that belongs to each other. With this approach, you automatically organize your code in an effective way.

  • Use Controllers for input validation and request handling, use Services for business logic or ORM related stuff and write tests to make sure that everything works as expected

  • Register your modules in the import section of the root module (app.module.ts) in order to use them

  • Everything in NEST that is uses the @Injectable() decorator, is a provider in the NEST universum

Did you find this article valuable?

Support XamHans Blog by becoming a sponsor. Any amount is appreciated!