Month 4, Week 1

Introduction to NestJS

The Professional Framework for Backend Architects

Module 1: The "Why" of NestJS

From Freedom to Framework

The Express.js Experience

Express gives you ultimate freedom. It is "unopinionated." This is powerful, but for large teams and complex applications, it can lead to chaos.

Every developer might structure their controllers differently. Error handling can be inconsistent. There's no standard way to manage dependencies. You have to build the entire application architecture yourself, every time.

Freedom can lead to inconsistency.

NestJS: An Opinionated Framework

NestJS is a framework for building efficient, scalable Node.js server-side applications. It is built on top of Express (or optionally Fastify) but imposes a strong architectural structure.

It is "opinionated". It provides a blueprint for how your application should be built. This ensures consistency, maintainability, and scalability across projects and teams.

NestJS provides the architectural blueprint, so you can focus on building the features.

Key Advantages of NestJS

  • Built with TypeScript: Provides static typing and modern JavaScript features out of the box.
  • Strong Architecture: Enforces a modular architecture inspired by Angular, making code organized and predictable.
  • Dependency Injection: A powerful, built-in system for managing dependencies and improving testability.
  • Extensible: A rich ecosystem of modules for databases, authentication, caching, and more.
  • Excellent CLI: A powerful command-line interface for scaffolding and managing your application.

Module 2: The Core Architectural Pillars

Modules, Controllers, and Providers

The Three Pillars

Every NestJS application is built from three main components. Understanding their roles is the key to mastering the framework.

  1. Controllers: The "Reception Desk"
  2. Providers (Services): The "Specialist Workers"
  3. Modules: The "Departments" that group them together.

Controllers: Handling Requests

A controller is responsible for handling incoming requests and returning responses to the client. Its job is to be the entry point for a specific URL path.

A controller should not contain complex business logic. Its primary role is to receive a request, call a service to do the actual work, and then format the response.


                        import { Controller, Get, Param } from '@nestjs/common';

                        @Controller('users') // Handles all requests to the /users path
                        export class UserController {

                          @Get(':id') // Handles GET /users/:id
                          findOne(@Param('id') id: string) {
                            // Logic to find the user will be in a service
                            return `This action returns user #${id}`;
                          }
                        }
                    

Providers (Services): The Business Logic

A provider is a class that can be "injected" as a dependency. The most common type of provider is a Service.

A service is where your business logic lives. It's responsible for tasks like fetching data from a database, performing calculations, or calling other APIs.


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

                        @Injectable() // Marks this class as a provider
                        export class UserService {
                          
                          findOne(id: number): string {
                            // In a real app, this would talk to a database.
                            return `Found user with ID ${id}.`;
                          }
                        }
                     

Dependency Injection (DI)

This is the most important concept in NestJS. Instead of the controller creating its own `UserService`, the controller declares a dependency on it, and the NestJS runtime "injects" an instance of the service into the controller's constructor.


                        @Controller('users')
                        export class UserController {
                          // NestJS will automatically create and "inject" an instance of UserService
                          constructor(private readonly userService: UserService) {}

                          @Get(':id')
                          findOne(@Param('id') id: string): string {
                            // The controller now delegates the work to the service
                            return this.userService.findOne(+id);
                          }
                        }
                    

This decouples your components, making them easier to test and reuse.

Modules: Organizing Your Application

A module is a class decorated with `@Module()`. It provides an organizational structure for your application.

Each module encapsulates a closely related set of capabilities (e.g., a `UsersModule`, a `ProductsModule`).


                        import { Module } from '@nestjs/common';
                        import { UserController } from './user.controller';
                        import { UserService } from './user.service';

                        @Module({
                          controllers: [UserController], // Lists all controllers in this module
                          providers: [UserService],      // Lists all services in this module
                        })
                        export class UserModule {}
                    

The main `AppModule` then imports these feature modules to build the complete application.

Mid-Lecture Knowledge Check

Module 3: The NestJS CLI

Your Architectural Assistant

Installation

The NestJS Command Line Interface is a powerful tool for scaffolding and managing your application. Install it globally.


                        npm install -g @nestjs/cli
                    

Creating a New Project

To start a new project, you use the `nest new` command.


                        nest new my-first-nest-app
                    

This will create a new directory, install all necessary dependencies, and generate a boilerplate project with a working `AppModule`, `AppController`, and `AppService`.

Generating Resources with the CLI

The true power of the CLI is in its "generate" (`g`) commands. It automates the creation of modules, controllers, and services, and automatically wires them up for you.

The most powerful command is `nest g resource`.


                        # This single command will...
                        nest g resource products

                        # ...create a `products` folder with:
                        # - products.module.ts
                        # - products.controller.ts
                        # - products.service.ts
                        # - products.entity.ts (placeholder)
                        # - products.dto.ts (Data Transfer Objects)
                        # ...and it will automatically import the new ProductsModule into your AppModule!
                      

In-Class Practical Exercise

Scaffolding a `todos` Resource

Your task is to use the NestJS CLI to create a new project and then generate a complete, working CRUD API for a `todos` resource. We will use the in-memory patterns from last week inside our new service.

  1. Install the NestJS CLI globally if you haven't already: `npm install -g @nestjs/cli`.
  2. Create a new NestJS project: `nest new todo-api`.
  3. `cd` into the `todo-api` directory.
  4. Use the CLI to generate a new resource named `todos`: `nest g resource todos` (select REST API and generate CRUD entry points).
  5. Inspect the generated files. Notice how the `TodosModule` was automatically added to `app.module.ts`.
  6. In `todos.service.ts`, create an in-memory array to store your todos.
  7. Implement the logic for the generated `create`, `findAll`, `findOne`, `update`, and `remove` methods using simple array manipulations (`.push`, `.find`, `.findIndex`, `.splice`).
  8. Start the development server: `npm run start:dev`.
  9. Use Postman or Insomnia to test all five CRUD endpoints for your new `/todos` resource.

Final Knowledge Check