First Steps with NestJS
NestJS structures backend applications using a modular architecture inspired by enterprise-grade patterns. In this hands-on guide, you will learn how to install the CLI, bootstrap a new project, understand its internal layout, and build your own modules, controllers, and services.
1. Architecture and Component Fundamentals
Before generating code or scaffolding with the CLI, it is essential to understand how core components interact within a NestJS module:
Main Components Overview:
- Module (
@Module): The fundamental organizational unit in NestJS. It bundles cohesive controllers and providers (services), establishing clear encapsulation boundaries for a scalable modular system. - Controller (
@Controller): Responsible for handling incoming HTTP requests on specified route paths (such as/users), unpacking parameters or request body payloads, and delegating execution to the appropriate service method. - Service (
@Injectable): Contains pure business logic (calculations, transformations, database calls, or external API communication). Services are decorated with@Injectable()so they can be injected automatically by NestJS's IoC container.
2. Step-by-Step Hands-On Guide
Global NestJS CLI Installation
The NestJS Command Line Interface (CLI) automates project bootstrapping and component generation according to architectural best practices.
npm install -g @nestjs/cli
The CLI prevents manual configuration of TypeScript, Webpack/SWC, ESLint, and Jest, maintaining a clean and standardized structure across projects.
Bootstrapping the Base Project
Run the nest new command to scaffold a new project named my-nest-app:
nest new my-nest-app
During execution, the CLI will ask which package manager you prefer. Select npm (or yarn / pnpm depending on your setup).
Inspecting Project Anatomy
Navigate into the newly created directory and inspect the generated files:
cd my-nest-app
The resulting folder structure is organized as follows:
my-nest-app/
├── src/
│ ├── app.controller.spec.ts # Unit tests for root controller
│ ├── app.controller.ts # Base controller with test endpoint ('/')
│ ├── app.module.ts # Root module of the application
│ ├── app.service.ts # Base service with simple methods
│ └── main.ts # Application entry point (starts HTTP server)
├── test/
│ └── app.e2e-spec.ts # End-to-end test suite
├── nest-cli.json # NestJS CLI configuration
├── package.json # Dependencies and npm scripts
└── tsconfig.json # TypeScript compiler options
Core files in src/:
main.ts: UsesNestFactory.create(AppModule)to instantiate the app and bind the server to port 3000 by default.app.module.ts: The root module that mounts the application and registers base providers and controllers.app.controller.ts: Contains sample HTTP route handlers (e.g.,GET /).app.service.ts: Returns sample responses (such as"Hello World!").
Running the App in Development Mode
Start the local server in watch mode so that file changes are automatically detected and reloaded:
npm run start:dev
Open your browser at http://localhost:3000 to verify that the application responds correctly.
If port 3000 is occupied on your machine, you can change it in src/main.ts:
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
// Instantiate NestJS application using root module
const app = await NestFactory.create(AppModule);
// Define HTTP listen port (e.g., 3001)
await app.listen(3001);
}
bootstrap();
Generating Modules with the CLI
To build an isolated feature (such as user management), it is recommended to generate its three architectural layers: Module, Controller, and Service.
Open a new terminal tab and run:
- Individual Commands
- All-In-One Command (CRUD Resource)
# 1. Generate users module
nest generate module users
# 2. Generate users controller
nest generate controller users
# 3. Generate users service
nest generate service users
# Generates full users resource (Module, Controller, Service, DTOs, Entities)
nest generate resource users
You can use shorthand aliases in your terminal: nest g mo users, nest g co users, and nest g s users.
Implementing Users Feature Logic
Inspect the code generated under src/users/ and implement the basic inter-layer communication:
import { Injectable } from '@nestjs/common';
// Decorator marking the class as injectable by the NestJS IoC container
@Injectable()
export class UsersService {
// In-memory array simulating a data source
private users = [
{ id: 1, name: 'Alice', email: 'alice@icesi.edu.co' },
{ id: 2, name: 'Bob', email: 'bob@icesi.edu.co' },
];
// Return all registered users
findAll() {
return this.users;
}
// Return a specific user by ID
findOne(id: number) {
return this.users.find((user) => user.id === id);
}
}
import { Controller, Get, Param } from '@nestjs/common';
import { UsersService } from './users.service';
// Decorator setting the base route path '/users' for this controller
@Controller('users')
export class UsersController {
// Dependency injection: Nest automatically injects UsersService
constructor(private readonly usersService: UsersService) {}
// Handles HTTP GET to '/users'
@Get()
getAllUsers() {
return this.usersService.findAll();
}
// Handles HTTP GET to '/users/:id'
@Get(':id')
getUserById(@Param('id') id: string) {
// Cast route param string to number before passing to service
return this.usersService.findOne(Number(id));
}
}
import { Module } from '@nestjs/common';
import { UsersController } from './users.controller';
import { UsersService } from './users.service';
// Decorator registering controllers and providers of this module
@Module({
controllers: [UsersController],
providers: [UsersService],
})
export class UsersModule {}