DEV Community

Teamz Lab LTD
Teamz Lab LTD

Posted on • Originally published at learn.teamzlab.com

Why NestJS?

TL;DR: NestJS is a progressive Node.js framework for building server-side applications. Built on top of Express (or Fastify), it provides an opinionated structure…

The analogy

Express is like a studio apartment — everything in one room, you decide where stuff goes. NestJS is like a well-designed house with labeled rooms: kitchen (services), front door (controllers), hallways (modules). Both work, but the house is WAY easier to manage when you have 50 rooms!

What is it, really?

NestJS is a progressive Node.js framework for building server-side applications. Built on top of Express (or Fastify), it provides an opinionated structure inspired by Angular, using modules, controllers, and services. It includes powerful features like Pipes, Interceptors, Filters, Middleware, and custom Decorators.

Why it matters in the real world

NestJS is used by Adidas, Roche, Autodesk, and many startups. It's the most popular structured Node.js framework, with over 70K GitHub stars.

The 11 things you actually need to know

Built-in Structure — Module → Controller → Service pattern. Everyone organizes code the same way — no more 'where did John put that file?'

Dependency Injection — Instead of manually creating instances, you just declare what you need and NestJS delivers it. Like room service!

TypeScript First — NestJS is built FOR TypeScript. Decorators (@get, @post, @Injectable) make code readable and self-documenting.

Batteries Included — Validation, auth guards, Swagger docs, WebSockets, microservices — all built-in or plug-and-play.

Scalable from Day 1 — Start small with one controller. Grow to 100 modules without refactoring. NestJS scales with your team!

Rich Ecosystem — @nestjs/jwt, @nestjs/passport, @nestjs/websockets, @nestjs/graphql. Add what you need, nothing more.

Pipes — Data Transformation & Validation — Pipes transform or validate incoming data before it reaches your controller. ValidationPipe automatically checks DTOs, ParseIntPipe converts strings to numbers. Think of pipes as airport security — they inspect your luggage (data) before you board the plane (controller).

Interceptors — Before & After Logic — Interceptors wrap around your route handler. They can transform the response, add logging, cache results, or measure execution time. Like a sandwich — interceptor code runs before AND after your handler. Example: automatically wrapping all responses in {success: true, data: ...} format.

Exception Filters — Centralized Error Handling — Instead of try/catch everywhere, exception filters catch errors globally and return consistent error responses. Create a custom filter to log errors, send alerts, and return user-friendly messages. One place to handle ALL errors.

Middleware — Request Pipeline — Middleware runs BEFORE the route handler on every request. Use it for logging, CORS, rate limiting, or authentication checks. It's the bouncer at the door — checking everyone before they enter the club (your controller).

Custom Decorators — Create your own decorators like @CurrentUser() to extract the logged-in user from the request, or @Roles('admin') to mark routes. Custom decorators make your code cleaner and more readable — hiding complexity behind a simple @ symbol.

Working code example

// The NestJS Holy Trinity 🔺
// 1. MODULE — wires everything together
@Module({
  controllers: [UserController],
  providers: [UserService],
})
export class UserModule {}

// 2. CONTROLLER — handles HTTP requests (the front door 🚪)
@Controller('users')
export class UserController {
  constructor(private userService: UserService) {}
  //          ↑ NestJS auto-injects this! Magic! ✨

  @Get()          // GET /users
  findAll() {
    return this.userService.findAll();
  }

  @Get(':id')     // GET /users/123
  findOne(@Param('id') id: string) {
    return this.userService.findOne(id);
  }

  @Post()         // POST /users
  create(@Body() data: CreateUserDto) {
    return this.userService.create(data);
  }
}

// 3. SERVICE — business logic (the brain 🧠)
@Injectable()
export class UserService {
  constructor(private prisma: PrismaService) {}

  findAll() {
    return this.prisma.user.findMany();
  }

  findOne(id: string) {
    return this.prisma.user.findUnique({ where: { id } });
  }

  create(data: CreateUserDto) {
    return this.prisma.user.create({ data });
  }
}

// DTO — validate input 📋
import { IsString, IsEmail, MinLength } from 'class-validator';

export class CreateUserDto {
  @IsString()
  @MinLength(2)
  name: string;

  @IsEmail()
  email: string;
}
Enter fullscreen mode Exit fullscreen mode

Line-by-line breakdown:

  1. The NestJS Holy Trinity 🔺
  2. 1. MODULE — wires everything together
  3. Decorator that adds metadata or behavior
  4. Exporting for use in other files
  5. 2. CONTROLLER — handles HTTP requests (the front door 🚪)
  6. Decorator that adds metadata or behavior
  7. Exporting for use in other files
  8. ↑ NestJS auto-injects this! Magic! ✨
  9. Decorator that adds metadata or behavior
  10. Returning a value
  11. Closing block
  12. Decorator that adds metadata or behavior
  13. Returning a value
  14. Closing block
  15. Decorator that adds metadata or behavior
  16. Returning a value
  17. Closing block
  18. Closing block
  19. 3. SERVICE — business logic (the brain 🧠)
  20. Decorator that adds metadata or behavior
  21. Exporting for use in other files
  22. Returning a value
  23. Closing block
  24. Returning a value
  25. Closing block
  26. Returning a value
  27. Closing block
  28. Closing block
  29. DTO — validate input 📋
  30. Importing required dependencies
  31. Exporting for use in other files
  32. Decorator that adds metadata or behavior
  33. Decorator that adds metadata or behavior
  34. Decorator that adds metadata or behavior
  35. Closing block

Spot the bug

@Controller('users')
export class UserController {
  constructor(private userService: UserService) {}

  @Get()
  findAll() {
    return this.userService.findAll();
  }
}

export class UserService {
  findAll() { return []; }
}
Enter fullscreen mode Exit fullscreen mode

Hint

What decorator is missing from the service class?

Answer

UserService is missing the @Injectable() decorator. Without it, NestJS can't inject it via dependency injection. Fix: add @Injectable() above 'export class UserService'.

Explain it like I'm five

Express is like a messy room where you put stuff wherever. NestJS is like a room with labeled shelves - toys here, books there, clothes in the closet. Both rooms work, but the organized one is way easier when you have LOTS of stuff!

Fun fact

NestJS was created by Kamil Myśliwiec in 2017 because he was frustrated that Node.js had no structure like Angular did for frontend. He basically thought 'why can't backend be organized too?' and just... built it. 😤🛠️

Try it yourself

Create a NestJS project with nest new my-app, then generate a module with nest g resource cats. Look at the files it creates — controller, service, module, DTO. Notice how they connect!


Want the interactive version?

This article is lesson 15 of Free Full-Stack Playbook — a free, interactive course with quizzes, a code playground, spaced-repetition flashcards, and 48 more lessons. No signup required.

Related lessons from this course


Built by Teamz Lab — we build production mobile and web apps for clients worldwide. This free course is how we give back. Free Full-Stack Playbook's interactive version is here.


Originally published at https://tool.teamzlab.com?utm_source=devto&utm_medium=article&utm_campaign=2026-04-why-nestjs-js-ts-fun-book

Top comments (0)