NestJS architecture step by step guide.

NestJS provides a modular architecture that allows the creation of loosely coupled and easily testable components.

· 8 min read
Mary Maina

Mary Maina

I am a frontend engineer passionate about building intuitive user interfaces. Tools: React, Next.JS, TypeScript, React Native, NodeJs

topics

Overview

NestJs is an open-source, progressive Node.js framework for building robust, efficient, and scalable server-side applications. NestJS offers abstraction to a certain extent while directly exposing APIs to developers.NestJs architecture significantly draws inspiration from Angular.

Nestjs Analogy

But first, let's use an Analogy to understand the concepts of NestJS

Let us imagine we are building a house. In traditional construction, you will be required to manually handle pouring concrete, framing walls, installing plumbing and wiring, and so on. This particular approach is not efficient since it requires a lot of manual labor and changes can be difficult to implement.

Now, let us look at building a modern house that reflects a modular building system. Each house component (floors, walls, electrical wiring, water system, etc.) comes pre-built with such a system. This setup abstracts away common tasks allowing you to connect the components rather than building from scratch.

In this analogy, NestJS works similarly to the modular building system. It provides pre-built components and patterns like modules, controllers, services, and middleware that you can easily connect to build your application. These components are abstracted away allowing developers to to build and maintain complex applications easily.

How Nest borrows inspiration from Angular:

  1. Modular Structure: Nest has a modular structure for building applications, similar to Angular's modules. Modules in Nest provide separation of concerns making it possible to build high-performance applications.
  2. Middleware: Nest middleware functions are similar to Angular interceptors, in that incoming requests and outgoing responses can be intercepted before or after interacting with the controller. This enables operations such as authentication and error handling.
  3. Dependency Injection: Dependency Injection is a design pattern used to manage the services required by different components and modules of an application to perform tasks. Both Nest and Angular use dependency injection by allowing components to be loosely coupled.
  4. Decorators and Metadata: Nest makes extensive use of decorators and metadata, similar to Angular. Decorators are used to annotate classes, methods, and properties to provide additional functionality or metadata, such as defining routes, middleware, or providers. This declarative approach enhances readability and reduces boilerplate code.
  5. Lifecycle Hooks: Nest provides lifecycle hooks that are conceptually similar to Angular's lifecycle hooks. These hooks allow developers to execute code at specific points during the lifecycle of a component, controller, or module.

NestJS concepts further explained:

  • Providers: Providers are classes annotated with the @Injectable() decorator which are responsible for interacting with external services, handling business logic, and managing data. Providers can be injected into other components, such as controllers or other providers, to facilitate modular and reusable code.
  • Modules: Each NestJS application typically consists of multiple modules, each serving a specific purpose or feature set. Modules encapsulate related functionality and provide a clear structure for organizing code. The @Module() decorator provides metadata that Nest makes use of to organize the application structure.
  • Controllers: Controllers are responsible for handling incoming HTTP requests and generating appropriate responses. They are classes annotated with the @Controller() decorator and contain handler methods annotated with specific HTTP request method decorators (such as @Get(), @Post(), etc.). To create a basic controller, we use classes and decorators. Decorators associate classes with required metadata and enable Nest to create a routing map
  • Middleware: The middleware sits between a client and the controller allowing logic to be executed before or after the controller handles invoked methods. Custom Nest middleware is either implemented as a function or in a class with an @Injectable()

The Setup and Installation

To get started ensure you have NestCLI installed. If not use the following command to install it.

npm i -g @nestjs/cli

To create your first project type in

nest new <project-name>

You will be prompted to pick the name of your project and select a package manager. For this project will be using npm. Below is our project structure.

Image
  • main.ts: This is the starting point of our application, where Nest will begin building the dependency graph
  • app.module.ts : This is the root module of our application. In the module, we can define controllers and providers, and we can import and export providers.
  • app.controller.ts : A controller serves as a route handler, providing the context for one or more related routes.
  • app.service.ts : Encapsulates business logic and functionality shared across multiple parts of the application

We shall create a folder called tasks and create a controller, module, and service following nest architecture. Use the CLI to execute the following commands.

  • Generate a task controller: $ nest g controller task
  • Generate a task service: $ nest g service task
  • Generate a task module nest g module task

This is how our updated folder structure will look:

Image

The @Injectable() decorator marks the TaskService class as a provider, this means it can be injected as a dependency into other components like controllers.

Task service

This task service class manages a list of tasks with operations to add a new task and retrieve all tasks.

//task.service.ts

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

@Injectable()
export class TaskService {
  private tasks: { id: number; description: string }[] = [];
  createTask(description: string) {
    const newTask = {
      id: this.tasks.length + 1,
      description,
    };
    this.tasks.push(newTask);
    return newTask;
  }
  getTasks() {
    return this.tasks;
  }
}

Task controller

This TaskController serves as the interface for HTTP clients to interact with the task management functionality provided by TaskService. It defines routes to create new tasks and retrieve existing tasks.

//task.controller.ts
import { Injectable } from '@nestjs/common';

@Injectable()
export class TaskService {
  private tasks: { id: number; description: string }[] = [];
  createTask(description: string) {
    const newTask = {
      id: this.tasks.length + 1,
      description,
    };
    this.tasks.push(newTask);
    return newTask;
  }
  getTasks() {
    return this.tasks;
  }
}

Task module

The TaskModule is designed to encapsulate all the functionalities related to task management. It uses the TaskController to manage API routes and the TaskService to handle business logic of task creation. The module connects these components using the @Module() decorator enabling the separation of concerns.

//task.module.ts
import { Module } from '@nestjs/common';
import { TaskController } from './task.controller';
import { TaskService } from './task.service';

@Module({
  controllers: [TaskController],
  providers: [TaskService],
})
export class TaskModule {}
//app.module.tss
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { TaskModule } from './task/task.module';

@Module({
  imports: [TaskModule],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

The @Post() decorator indicates the createTask method will handle HTTP POST requests to the /tasks/createTask endpoint.

createTask(@Body() body: { description: string }): This method signature uses decorators from NestJS to specify how to handle the incoming request data.

@Body() decorator instructs nest to destructure the description from the request body and pass it to the createTask method.

Making a POST request on Postman

Image

The decorator @Get() takes an argument (the method being decorated, in this case, getTasks and returns an array of tasks.

Also, the @Get() decorator accepts a pathname that is used to make an api request. For example, a request to /taks/getTasks will invoke the method getTasksand return all the tasks.

Making a GET request on Postman

Image

share

Mary Maina

I am a frontend devloper