Track A · Tutorial 1

NestJS Fundamentals

Chapter 24 · Backend Specialization
~2.5 hours Intermediate TypeScript · DI · Modules · Controllers

Overview

NestJS is a progressive Node.js framework built with TypeScript, inspired by Angular's architecture. This tutorial introduces the core concepts: modules, controllers, providers, and dependency injection. You will build a RESTful API with a modular structure that scales beautifully.

Why this matters: NestJS enforces a clean, maintainable architecture using proven patterns. It's increasingly popular in enterprise Node.js development.

1. Introduction to NestJS

NestJS is built on top of Express (or Fastify) and uses TypeScript by default. Key features:

  • Modular architecture: Organise code into modules.
  • Dependency Injection (DI): Decouple components with inversion of control.
  • Decorators: TypeScript decorators define controllers, routes, and more.
  • Middleware & Guards: Request‑handling pipeline.
  • Testing: Built‑in support for unit and e2e testing.
// Example controller (decorator-based) @Controller('users') export class UsersController { @Get() findAll() { return 'All users'; } }

2. Setting Up a NestJS Project

Prerequisites

  • Node.js (v16+)
  • npm or yarn

Installation

npm i -g @nestjs/cli nest new my-app cd my-app npm run start:dev

The CLI generates a project with:

  • src/main.ts – application entry point
  • src/app.module.ts – root module
  • src/app.controller.ts – example controller

3. Controllers & Routing

Controllers handle incoming requests. They use decorators to define routes:

  • @Controller('path') – base route prefix
  • @Get(), @Post(), @Put(), @Delete()
  • @Param('id') – extract URL parameter
  • @Body() – extract request body
  • @Query('search') – extract query string
import { Controller, Get, Post, Body, Param } from '@nestjs/common'; @Controller('users') export class UsersController { @Get() findAll() { return []; } @Get(':id') findOne(@Param('id') id: string) { return { id, name: 'Alice' }; } @Post() create(@Body() createUserDto: any) { return createUserDto; } }
Tip: Use DTOs (Data Transfer Objects) to validate incoming data with class-validator.

4. Providers & Dependency Injection

Providers are classes that can be injected into controllers or other providers. They are registered in a module's providers array.

// users.service.ts import { Injectable } from '@nestjs/common'; @Injectable() export class UsersService { getUsers() { return ['Alice', 'Bob']; } } // users.controller.ts @Controller('users') export class UsersController { constructor(private readonly usersService: UsersService) {} @Get() findAll() { return this.usersService.getUsers(); } }

NestJS uses the IoC container to manage dependencies. The @Injectable() decorator marks a class as injectable.

5. Modules & Application Structure

Modules organise your application. Each module encapsulates related controllers, providers, and imports.

// users.module.ts import { Module } from '@nestjs/common'; import { UsersController } from './users.controller'; import { UsersService } from './users.service'; @Module({ controllers: [UsersController], providers: [UsersService], }) export class UsersModule {} // app.module.ts (root) @Module({ imports: [UsersModule], }) export class AppModule {}
  • Root module: AppModule – the entry point.
  • Feature modules: UsersModule – group related features.
  • Shared modules: Export providers for reuse.
Best practice: Keep modules focused and follow domain‑driven design principles.

Quiz

Question 1

Which decorator is used to define a controller in NestJS?

  • @Service
  • @Controller
  • @Component
  • @Module
Show answer
B. @Controller.

Question 2

What is the purpose of the @Injectable() decorator?

  • To define a controller
  • To make a class available for dependency injection
  • To define a module
  • To create a route
Show answer
B. To make a class available for dependency injection.

Question 3

How do you extract a URL parameter like id from /users/42?

  • @Param('id') id: string
  • @Query('id') id: string
  • @Body('id') id: string
  • @Headers('id') id: string
Show answer
A. @Param('id') id: string.

Exercises

Exercise 1

Create a ProductsController with GET /products and GET /products/:id. Use a ProductsService to handle the data.

Sample answer
// products.service.ts @Injectable() export class ProductsService { getProducts() { return [{ id: 1, name: 'Laptop' }]; } getProduct(id: number) { return { id, name: 'Laptop' }; } } // products.controller.ts @Controller('products') export class ProductsController { constructor(private readonly productsService: ProductsService) {} @Get() findAll() { return this.productsService.getProducts(); } @Get(':id') findOne(@Param('id') id: string) { return this.productsService.getProduct(+id); } }

Exercise 2

Add a POST /products endpoint that accepts a body and returns the created product.

Sample answer
// In ProductsController @Post() create(@Body() createProductDto: any) { return { id: 2, ...createProductDto }; }

Homework

Homework 1

Set up a NestJS project with two modules: UsersModule and PostsModule. Each module should have a controller and a service. Users have many posts (1:N).

Sample answer

Structure:

  • users/users.module.ts – imports PostsModule
  • users/users.controller.tsGET /users/:id/posts
  • users/users.service.ts – data methods
  • posts/posts.module.ts, posts/posts.controller.ts, posts/posts.service.ts

Use dependency injection to share PostsService across modules (export the service from PostsModule and import it in UsersModule).

Mini‑Project

Task Management API

Build a Task Management API with NestJS:

  • GET /tasks – list all tasks
  • GET /tasks/:id – get single task
  • POST /tasks – create a task (title, description)
  • PUT /tasks/:id – update a task
  • DELETE /tasks/:id – delete a task
  • Use an in‑memory array as a data store
Sample implementation outline

TaskService:

  • tasks: Task[] = []
  • findAll(), findOne(id: number), create(taskDto), update(id, taskDto), delete(id)

TaskController:

  • @Get() findAll()
  • @Get(':id') findOne(@Param('id') id: string)
  • @Post() create(@Body() dto)
  • @Put(':id') update(@Param('id') id: string, @Body() dto)
  • @Delete(':id') delete(@Param('id') id: string)

Tutorial Summary

You learned the fundamentals of NestJS: setting up a project, creating controllers with routing, building providers with dependency injection, and organising code into modules. You built a REST API with a clean, modular architecture that scales with your application.

Key takeaway: NestJS's decorator‑based approach and DI container make it easy to build maintainable, testable backend applications.