The NestJS Handbook – Learn to Use Nest with Code Examples
NestJS is a progressive Node.js framework for building efficient, reliable, and scalable server-side applications. Combining the best ideas from OOP (Object-Oriented Programming), FP (Functional Programming), and FRP (Functional Reactive Programming)...

NestJS is a progressive Node.js framework for building efficient, reliable, and scalable server-side applications. Combining the best ideas from OOP (Object-Oriented Programming), FP (Functional Programming), and FRP (Functional Reactive Programming), it gives you a fully-architected, batteries-included platform on top of Express (or Fastify).
If you’re coming from Angular, you’ll feel right at home with its module/controller/service structure and powerful dependency-injection system.
In this article we’ll cover both theory – why NestJS exists, how it’s structured, and when to reach for it –and practice, with bite-sized code snippets demonstrating how to bootstrap a project, define routes, inject dependencies, and more. Let’s start by understanding what NestJS is and where it came from.
Table of Contents
1. What is NestJS?
NestJS is a framework for building server-side applications in Node.js. It’s written in TypeScript (but supports plain JavaScript as well). At its core, it:
Wraps a mature HTTP server library (Express or Fastify)
Standardizes application architecture around modules, controllers, and providers
Leverages TypeScript’s type system for compile-time safety and clear APIs
Offers built-in support for things like validation, configuration, and testing
Rather than stitching together middleware by hand, NestJS encourages a declarative, layered approach. You define modules to group related functionality, controllers to handle incoming requests, and providers (often called “services”) for your business logic. Behind the scenes, NestJS resolves dependencies via an IoC container, so you can focus on writing clean, reusable classes.
To start up a project, run the following commands:
# Install the Nest CLI globally
npm install -g @nestjs/cli
# Create a new project called 'my-app'
nest new my-app
cd my-app
npm run start:dev
Once it’s running, you have a ready-to-go HTTP server with hot reloading, strict typing, and a sensible folder layout.
1.1 History and Philosophy
NestJS first appeared in 2017, created by Kamil Myśliwiec. Its goal was to bring the architectural patterns of Angular to the backend world, providing:
Consistency: A single, opinionated way to structure applications.
Scalability: Clear boundaries (modules) make it easier to grow teams and codebases.
Testability: Built-in support for Jest and clear separation of concerns.
Extensibility: A pluggable module system makes it easy to integrate ORMs, WebSockets, GraphQL, microservices, and more.
Under the hood, NestJS embraces these principles:
Modularity: Everything lives in a module (
AppModule
,UsersModule
, and so on), which can import other modules or export providers.Dependency Injection: Services can be injected into controllers (and even into other services), which fosters loose coupling.
Decorators and Metadata: With TypeScript decorators (
@Module()
,@Controller()
,@Injectable()
), NestJS reads metadata at runtime to wire everything together.
Here’s a tiny example showing the interplay of these pieces:
// users.service.ts
import { Injectable } from '@nestjs/common';
@Injectable()
export class UsersService {
private users = [{ id: 1, name: 'Alice' }];
findAll() {
return this.users;
}
}
// users.controller.ts
import { Controller, Get } from '@nestjs/common';
import { UsersService } from './users.service';
@Controller('users')
export class UsersController {
constructor(private readonly usersService: UsersService) {}
@Get()
getUsers() {
return this.usersService.findAll();
}
}
// 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 {}
The
@Module
decorator groups controller + serviceThe controller injects the service via its constructor
A simple
GET /users
route returns an array of user objects
With that foundation laid, in the next section we’ll explore why you’d choose NestJS, comparing it to other popular Node frameworks and outlining common real-world use cases.
2. Why Choose NestJS?
NestJS isn’t just another Node.js framework – it brings a structured, enterprise-grade approach to building backend services. In this section we’ll cover benefits and real-world use cases, then compare NestJS to other popular Node frameworks so you can see where it fits best.
2.1 Benefits and Use Cases
Strong architectural patterns
Modularity: You break your app into focused modules (
AuthModule
,ProductsModule
, and so on), each responsible for a slice of functionality.Separation of concerns: Controllers handle HTTP, services encapsulate business logic, modules wire everything up.
Scalability: Growing teams map naturally onto modules—new features rarely touch existing code.
Built-in dependency injection (DI)
DI makes testing and swapping implementations trivial.
You can easily mock a service in a unit test:
// products.controller.spec.ts
import { Test, TestingModule } from '@nestjs/testing';
import { ProductsController } from './products.controller';
import { ProductsService } from './products.service';
describe('ProductsController', () => {
let controller: ProductsController;
const mockService = { findAll: () => ['apple', 'banana'] };
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [ProductsController],
providers: [
{ provide: ProductsService, useValue: mockService },
],
}).compile();
controller = module.get(ProductsController);
});
it('returns a list of products', () => {
expect(controller.getAll()).toEqual(['apple', 'banana']);
});
});
TypeScript-first
Full type safety at compile time.
Leverage interfaces and decorators (
@Body()
,@Param()
) to validate and transform data.
Rich ecosystem and extensibility
Official integrations for WebSockets, GraphQL, microservices (RabbitMQ, Kafka), and more.
Hundreds of community modules (for example
@nestjs/swagger
for OpenAPI docs).
Production-grade tooling
CLI generates boilerplate (
nest g module
,nest g service
).Support for hot-reload in development (
npm run start:dev
).Built-in testing setup with Jest.
Real-World Use Cases:
Enterprise APIs with strict module boundaries and RBAC.
Microservices architectures, where each service is a self-contained NestJS app.
Real-time applications (chat, live dashboards) using Nest’s WebSocket gateways.
GraphQL backends with code-first schemas.
Event-driven systems connecting to message brokers.
2.2 Comparison with Other Frameworks
Feature | Express | Koa | NestJS |
Architecture | Minimal, unopinionated | Minimal, middleware-based | Opinionated modules/controllers/services |
Dependency Injection | Manual wiring | Manual wiring | Built-in, reflect-metadata |
TypeScript Support | Via DefinitelyTyped | Via DefinitelyTyped | First-class, decorators |
CLI Tooling | None (3rd-party) | None | @nestjs/cli generates code |
Testing | User-configured | User-configured | Jest + DI makes mocking easy |
Ecosystem | Middleware library | Middleware library | Official microservices, GraphQL, Swagger modules |
Learning Curve | Low | Low | Medium (learning Nest idioms) |
Express is great if you want minimal layers and full control, but you’ll end up hand-rolling a lot (DI, validation, folder structure).
Koa offers a more modern middleware approach, but still leaves architecture decisions to you.
NestJS provides the full stack: structure, DI, validation, testing, and official integrations, which is ideal if you value consistency, type safety, and out-of-the-box best practices.
When to choose NestJS:
NextJS is great for various use cases. It’s particularly effective if you’re building a large-scale API or microservice suite, if you want a solid architecture from day one, and if you prefer TypeScript and DI to keep code testable and maintainable.
With these advantages in mind, you’ll find that NestJS can dramatically speed up development, especially on projects that need robust structure and clear boundaries.
In the next section, we’ll dive into getting started: installing the CLI, creating a project, and exploring the generated folder layout.
3. Getting Started
Let’s jump into the basics: installing the CLI, scaffolding a new project, and exploring the default folder layout.
3.1 Installing the CLI
Nest ships with an official command-line tool that helps you generate modules, controllers, services, and more. Under the hood it uses Yeoman templates to keep everything consistent.
# Install the CLI globally (requires npm ≥ 6)
npm install -g @nestjs/cli
Once installed, you can run nest --help
to see available commands:
nest --help
Usage: nest <command> [options]
Commands:
new Scaffold a new project
generate|g [options] Generate artifacts (modules, controllers, ...)
build Build project with webpack
...
Options:
-v, --version Show version number
-h, --help Show help
3.2 Creating Your First Project
Scaffolding a new app is a single command. The CLI will ask whether to use npm or yarn, and whether to enable strict TypeScript settings.
# Create a new Nest app in the "my-nest-app" folder
nest new my-nest-app
After answering the prompts, you’ll have:
cd my-nest-app
npm run start:dev
This launches a development server on http://localhost:3000
with automatic reload on file changes.
3.3 Project Structure Overview
By default, you’ll see something like:
my-nest-app/
├── src/
│ ├── app.controller.ts # example controller
│ ├── app.controller.spec.ts # unit test for controller
│ ├── app.module.ts # root application module
│ ├── app.service.ts # example provider
│ └── main.ts # entry point (bootstraps Nest)
├── test/ # end-to-end tests
├── node_modules/
├── package.json
├── tsconfig.json
└── nest-cli.json # CLI configuration
src/main.ts
The “bootstrap” script. It creates a Nest application instance and starts listening on a port:import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; async function bootstrap() { const app = await NestFactory.create(AppModule); await app.listen(3000); console.log(`