linkedin Skip to Main Content
Just announced! We now support spreadsheets
Back to blog

Defining NestJS and How to Get Started

Development

Because of the rapid growth of JavaScript and TypeScript frameworks and libraries, a huge selection of packages is available through popular package management systems such as Node Package Manager (NPM). 

Some packages are created to specifically address one or two particular issues. However, others may try to recreate existing solutions. In this article, we’ll provide an overview of NestJS, one of the leading TypeScript frameworks. We’ll answer questions such as “What is NestJS?” and “How do I get started using it?” to give you a better understanding of what it is and how to use it. 

What is NestJS?

NestJS is a TypeScript framework for developing highly scalable, flexible, and production-ready Node.js back-end applications. At the time of writing, NestJS had over 52,000 stars and 6300 forks on GitHub, and it’s one of the most popular TypeScript frameworks for Node.js. 

In addition, NestJS supports various JavaScript versions such as ES6, ES7, and ES8. As a result, you can create back-end applications using pure JavaScript or by using one of its derivatives, such as TypeScript.  

Features of NestJS

To gain a better understanding of the core features of NestJS, let’s take a closer look at them. 

Uses TypeScript

NestJS uses TypeScript for the composition of back-end applications. TypeScript is a strongly typed programming language that helps to reduce errors related to type checking and inconsistencies when developing large-scale and enterprise applications. This is in contrast to JavaScript, which doesn’t have the same level of type checking. By using TypeScript, NestJS helps to ensure the stability and reliability of code. 

Powerful command line interface

Are you a fan of the terminal or CMD prompt? Don’t worry, NestJS has you covered! It provides a powerful command line interface (CLI) program that can be used to develop applications with NestJS without relying on graphical user interfaces. With just a few simple commands, you can create and manage any part of the NestJS framework, such as databases, modules, controllers, and service files. 

Developer-friendly documentation

The quality of documentation can have a significant impact on the adoption of a framework such as NestJS. If the documentation isn’t well written, comprehensive, and easy to understand, developers may be less likely to use it. Fortunately, NestJS offers detailed, developer-friendly, and straightforward documentation that covers most development questions and provides clear instructions on how to get started with the framework. 

Microservice architecture pattern

NestJS supports microservice architecture development by making it easy to connect to popular microservice tools like Kafka, gPRC, RabbitMQ, etc. 

Supports popular libraries

NestJS makes application development quicker and more efficient by supporting popular tools. It’s configured according to industry standards and best practices, ensuring that developers have access to the best tools available. 

Furthermore, NestJS comes preconfigured with TypeORM, Mongoose, GraphQL, logging, validation, caching, web sockets, and many other features. 

Why learn NestJS?

Learning a new framework such as NestJS can cause doubts to arise. Questions like “How long will this framework last?”, “Are big companies using it?”, and “Can I get a job with it?” make it difficult to decide whether to invest the time needed to learn it. 

Here are a few reasons NestJS is worth learning: 

  • TypeScript and bulletproof architectural patterns make it easy for developers to build enterprise-ready applications that can be scaled and kept up to date with modern technologies.
  • NestJS uses the standards and structures of Angular and makes them better. So if you’re switching from Angular to NestJS, you can probably start using it in a day.
  • It also works with GraphQL, WebSockets, Kafka, and RabbitMQ—all popular enterprise tools—right out of the box. These tools are used to build large microservice applications.
  • NestJS also supports tools like TypeORM, Mongoose, Logging, Validation, Caching, WebSockets, and much more, without extra configuration.
  • Many Fortune 500 companies are now using NestJS to implement their back-end applications. Therefore, it can be easy to find a job with companies using NestJS.

NestJS vs. Express.js

NestJS and Express.js are both used for building web applications and APIs. Both are built on top of the Node.js runtime, which allows you to run JavaScript on the server side. 

NestJS is inspired by Angular and offers a modular structure for organizing code into reusable components. Additionally, NestJS also includes a module system for dividing code into separate modules and built-in support for dependency injection to manage dependencies between different parts of the application. 

Express.js is a minimalist web framework for Node.js. It provides a simple set of functions for building web applications and APIs. It’s designed to be flexible and lightweight, and many other web frameworks and libraries are built on top of it. 

Which framework is best for your project depends on your specific needs and preferences. If you’re building a large, scalable application and want to take advantage of NestJS’s modular structure and dependency injection features, NestJS may be a good choice. If you want a lightweight framework that is easy to get started with and provides a simple set of tools for building web applications and APIs, Express.js may be a better fit. 

Getting started with NestJS

After reading these details about NestJS, it should be clear that it’s a powerful framework worth learning. In this section, we’ll begin our journey with NestJS, from basic concepts to implementation. So, let’s get started! 

NestJS architecture

NestJS uses a three-tier architecture to promote modularity, allowing developers to organize their code into three distinct components. This enables them to create more organized code. 

This three-tier architecture includes the following: 

  • Controllers
  • Services
  • Data access layer

Let’s take a deeper look at this architecture. 

Controllers 

Controllers act as a go-between for client requests and responses. Essentially, they’re in charge of handling incoming requests and returning responses to the client via HTTP. A controller receives and processes a specific request for the application via a routing mechanism. NestJS creates controllers with classes and decorators, mapping each class method to a route and receiving a specific request. 

Services

NestJS providers are used to inject services as dependencies, which are then used as the foundation of the application. Services contain business logic and provide methods for performing database CRUD (create, retrieve, update, delete) operations, such as creating, storing, and updating data. Relationships between components, controllers, and other parts of the application are established through the injection of these services. 

Data access layer

The data access layer is responsible for accessing data stored in persistent storage of some kind. It’s located at the lowest level and is responsible for dealing with the database, encapsulating data access details, and providing an access interface for the upper layer. 

Setting up NestJS

Now that we have a good understanding of NestJS, including its design architecture and how it works, it’s time to put this knowledge into practice by creating a simple NestJS project. Specifically, we’ll be building a to-do list app. Are you ready to get started? Let’s dive in! 

Prerequisites

These are the basic requirements to move forward with the to-do app project: 

  • A decent computer with internet connectivity
  • Basic terminal knowledge
  • Node.js installed
  • Knowledge of JavaScript and Node.js
  • Knowledge of Postman

If you meet these requirements, you can proceed with this section.  If you don’t meet all the requirements, you can still follow along, but basic JavaScript knowledge is necessary. 

Follow the steps below to build our to-do list application (API) using NestJS. 

1. Install the CLI

The first step to starting a NestJS application is installing the Nest CLI using the command below. 

$ npm i -g @nestjs/cli
$ nest new nest-todo-appCode language: JavaScript (javascript)

The commands above install NestJS CLI globally and use the nest command to create a new project with the name nest-todo-app

Next, the command will prompt you to choose your preferred package manager. We’ll go with NPM and watch the command do its thing. You can also choose other JavaScript and TypeScript package managers like yarn

2. Run the demo application

By default, creating a project using the NestJS CLI creates a demo app. We can test this app by navigating into our project root directory and running the command below: 

$ npm run start:dev

To keep things tidy, you can delete all app.**.ts TypeScript files except the app.module.ts file. 

You’ll get a Hello World output when you open the project in localhost

3. Create a controller

As previously stated, controllers in NestJS receive incoming HTTP requests from an application front end and return an appropriate response. 

The nest command can be used to create, generate, and modify NestJS controllers, as well as to generate boilerplate code. 

We can create a controller using following command: 

$ nest generate controller todo

The two files, todo.controller.spec.ts and todo.controller.ts, will be created by the command. The first is for writing unit tests and will be ignored because it’s outside the scope of this tutorial. 

The second is a TypeScript file with the @Controller annotation. For our to-do list app, open the todo.controller.ts file and replace the code inside it with the following code: 

// directory - nest-todo-app/src/todo/todo.controller.ts

// we’ll import a couple of packages that we’ll use in our Todo app

import {
  Controller,
  Get,
  Res,
  HttpStatus,
  Param,
  NotFoundException,
  Body,
  Put,
  Query,
  Delete,
  Post,

} from '@nestjs/common';
import { TodoService } from './todo.service';
import { CreateTodoDTO } from './dto/create-todo.dto';
@Controller('todos')
export class TodoController {
  constructor(private todoService: TodoService) {}

  // Create a todo
  @Post('/')
  async create(@Res() res, @Body() createTodoDTO: CreateTodoDTO) {
    const newTodo = await this.todoService.addTodo(createTodoDTO);
    return res.status(HttpStatus.OK).json({
      message: 'Todo has been submitted successfully!',
      todo: newTodo,
    });
  }

  // Fetch a particular todo using ID
  @Get('/:todoID')
  async getTodo(@Res() res, @Param('todoID') todoID) {
    const todo = await this.todoService.getTodo(todoID);
    if (!todo) {
      throw new NotFoundException('Todo does not exist!');
    }
    return res.status(HttpStatus.OK).json(todo);
  }

  // Fetch all todos
  @Get('/')
  async getTodos(@Res() res) {
    const todos = await this.todoService.getTodos();
    return res.status(HttpStatus.OK).json(todos);
  }

  // Edit a particular todo using ID
  @Put('/')
  async editTodo(
    @Res() res,
    @Query('todoID') todoID,
    @Body() createTodoDTO: CreateTodoDTO,
  ) {
    const editedTodo = await this.todoService.editTodo(todoID, createTodoDTO);
    if (!editedTodo) {
      throw new NotFoundException('Todo does not exist!');
    }
    return res.status(HttpStatus.OK).json({
      message: 'Todo has been successfully updated',
      todo: editedTodo,
    });
  }

  // Delete a todo using ID
  @Delete('/delete')
  async deleteTodo(@Res() res, @Query('todoID') todoID) {
    const deletedTodo = await this.todoService.deleteTodo(todoID);
    if (!deletedTodo) {
      throw new NotFoundException('Todo does not exist!');
    }
    return res.status(HttpStatus.OK).json({
      message: 'Todo has been deleted!',
      todo: deletedTodo,
    });
  }
}Code language: TypeScript (typescript)

The code above performs a basic CRUD operation using the following logic: 

  • For making a new to-do entry, we have the Create method, which uses the POST method.
  • We have the getTodo and getTodos methods, which both use the GET method to get a single to-do, or to get all of them respectively.
  • Next, we have the editTodo method, which uses the PUT method to change or update an existing to-do.
  • The last method is deleteTodo, which uses the DELETE method and lets us get rid of our to-do.

The most important thing is that the service class controls every operation. This keeps business logic separate from actual data manipulation. 

4. Create a service

NestJS services handle any complex business logic data manipulations for a specific purpose and give an appropriate response to the controller. 

We can create services using the following command: 

$ nest generate service todo

Once again, open todo.service.ts and modify the code with the following: 

// directory - nest-todo-app/src/todo/todo.service.ts

import { Injectable } from '@nestjs/common';
import { CreateTodoDTO } from './dto/create-todo.dto';

// Creates a Todo interface to show exactly the attribute of our Todo
interface Todo {
  readonly id: number;
  readonly title: string;
  readonly description: string;
  readonly isDone: boolean;
}

@Injectable()
export class TodoService {

// Creates a Todo array with one Todo
  private todos: Todo[] = [
    {
      id: 1,
      title: 'Test todo',
      description: 'This is a demo Todo application',
      isDone: true,
    },
  ];

// Creates a new todo (Add todo to array)
  async addTodo(createTodoDTO: CreateTodoDTO): Promise<Todo> {
    this.todos.push(createTodoDTO);

// return last added item
    return this.todos.at(-1);
  }

// Returns a single todo with ID
  async getTodo(todoID: number): Promise<Todo> {
    const post = this.todos.find((todo) => todo.id === todoID);
    return post;
  }

// Returns all todos available
  async getTodos(): Promise<Todo[]> {
    return this.todos;
  }

// Deletes a todo by ID and add a new one (Update process)
  async editTodo(postID: number, createTodoDTO: CreateTodoDTO): Promise<Todo> {
    await this.deleteTodo(postID);
    this.todos.push(createTodoDTO);

// return last added item
    return this.todos.at(-1);
  }

// Deletes a todo from the array
  async deleteTodo(todoID: number): Promise<any> {
    const todoIndex = this.todos.findIndex((todo) => todo.id === todoID);
    return this.todos.splice(todoIndex, 1);
  }
}
Code language: TypeScript (typescript)

Because we’re not working with a real database, the code above implements a basic CRUD operation using simple JavaScript array manipulations. 

5. Create a module

Modules in NestJS organize your project into features and separate different features for easy structuring. A module is a class that has been annotated with the @Module() decorator. It aids in the organization of the application structure. 

The command below creates a module named todo

$ nest generate module todoCode language: JavaScript (javascript)

If you look into the app.module.ts (root module), you’ll see how NestJS wires and sets up everything by importing your controllers’ providers. 

import { Module } from '@nestjs/common';

import { AppController } from './app.controller';

import { AppService } from './app.service';

import { TodoController } from './todo/todo.controller';

import { TodoService } from './todo/todo.service';

import { TodoModule } from './todo/todo.module';

@Module({

  imports: [TodoModule],

  controllers: [AppController, TodoController],

  providers: [AppService, TodoService],

})

export class AppModule {}Code language: TypeScript (typescript)

6. Create a data transfer object

Finally, we need to create a data transfer object (DTO) to help define how data is sent across the network and how data is posted from the application to the database. 

We can do this by creating a directory named “DTO” inside our todo folder, and then creating the file. We’ll call it create-todo.dto.ts

// directory - nest-todo-app/src/todo/dto/create-todo.dto.ts

export class CreateTodoDTO {

  readonly id: number;

  readonly title: string;

  readonly description: string;

  readonly isDone: boolean;

}Code language: TypeScript (typescript)

Testing the NestJS framework

We’ve finished creating our NestJS application, and now it’s time to test it. 

First, check that your development server is still running, or run the following command to restart it. 

$ npm run start:dev

Now, open your Postman and test your endpoints by performing a few CRUD operations as defined in todo.service.ts. Your to-do list API should work great if there are no errors. 

Conclusion

You can already see the potential of NestJS to revolutionize and transform the way we develop back-end applications. Its versatility, flexibility, and modularity make it an attractive choice. Don’t miss out—jump on the NestJS bandwagon! For more insights on how NestJS works, please visit the official NestJS documentation


This post was written by Boris Bambo. He is a data & machine learning engineer fascinated by technology, education, and business. Feel free to connect with him on LinkedIn.