NestJS Modules, Providers, and Dependency Injection Explained
The three concepts that make NestJS feel structured — and why dependency injection makes your code dramatically easier to test.
NestJS brings Angular-style architecture — modules, dependency injection, decorators — to backend Node.js development. If you already know TypeScript, this is the most structured way to build a Node API without inventing your own conventions from scratch.
npm i -g @nestjs/cli
nest new my-api
cd my-api
npm run start:dev
Nest's CLI scaffolds an entire CRUD module — controller, service, module, and DTOs — in one command:
nest generate resource posts
@Controller('posts')
export class PostsController {
constructor(private postsService: PostsService) {}
@Get()
findAll() {
return this.postsService.findAll();
}
@Post()
create(@Body() dto: CreatePostDto) {
return this.postsService.create(dto);
}
}
Notice the constructor: PostsService is injected, not manually instantiated. Nest's dependency injection container handles wiring it up.
export class CreatePostDto {
@IsString()
@IsNotEmpty()
title: string;
@IsString()
body: string;
}
With class-validator and a global ValidationPipe enabled, invalid requests are rejected automatically before they ever reach your controller method — no manual validation code needed.
@Injectable()
export class PostsService {
private posts = [];
findAll() {
return this.posts;
}
create(dto: CreatePostDto) {
const post = { id: Date.now(), ...dto };
this.posts.push(post);
return post;
}
}
Controllers stay thin — they just delegate to services, the same "thin controller" principle from Laravel and every other mature framework.
Express gives you total freedom and zero structure — fine for a tiny script, painful once three developers are working on the same codebase with three different opinions about folder layout. Nest trades a bit of that freedom for consistency: every Nest project you'll ever open looks roughly the same, which matters enormously on a team.
The three concepts that make NestJS feel structured — and why dependency injection makes your code dramatically easier to test.