NestJS Complete Guide — Enterprise Node.js

Sanjeev SharmaSanjeev Sharma
5 min read

Advertisement

NestJS brings enterprise patterns to Node.js. It's opinionated, well-structured, and perfect for large teams building complex applications.

NestJS Architecture

NestJS uses three main components:

  • Controllers: Handle HTTP requests
  • Services: Business logic
  • Modules: Organize code
npm install -g @nestjs/cli
nest new my-app
cd my-app
npm run start:dev

Controllers

import { Controller, Get, Post, Body, Param } from "@nestjs/common";
import { UserService } from "./user.service";

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

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

  @Get(":id")
  findOne(@Param("id") id: string) {
    return this.userService.findOne(parseInt(id));
  }

  @Post()
  create(@Body() createUserDto: CreateUserDto) {
    return this.userService.create(createUserDto);
  }
}

Services

import { Injectable } from "@nestjs/common";

@Injectable()
export class UserService {
  private users = [
    { id: 1, name: "Alice", email: "alice@example.com" },
  ];

  findAll() {
    return this.users;
  }

  findOne(id: number) {
    return this.users.find((user) => user.id === id);
  }

  create(createUserDto: CreateUserDto) {
    const user = { id: 2, ...createUserDto };
    this.users.push(user);
    return user;
  }
}

Modules

import { Module } from "@nestjs/common";
import { UserService } from "./user.service";
import { UserController } from "./user.controller";

@Module({
  imports: [],
  controllers: [UserController],
  providers: [UserService],
  exports: [UserService], // Export for other modules
})
export class UserModule {}

// app.module.ts
import { Module } from "@nestjs/common";
import { UserModule } from "./user/user.module";

@Module({
  imports: [UserModule],
})
export class AppModule {}

Dependency Injection

interface Logger {
  log(message: string): void;
}

@Injectable()
class ConsoleLogger implements Logger {
  log(message: string): void {
    console.log(message);
  }
}

@Module({
  providers: [
    {
      provide: "Logger",
      useClass: ConsoleLogger,
    },
  ],
})
export class AppModule {}

@Injectable()
export class UserService {
  constructor(@Inject("Logger") private logger: Logger) {}

  findAll() {
    this.logger.log("Finding all users");
    return [];
  }
}

Decorators and Guards

import { Injectable, CanActivate, ExecutionContext } from "@nestjs/common";

@Injectable()
export class AuthGuard implements CanActivate {
  canActivate(context: ExecutionContext): boolean {
    const request = context.switchToHttp().getRequest();
    return !!request.headers.authorization;
  }
}

@Controller("admin")
@UseGuards(AuthGuard)
export class AdminController {
  @Get()
  getAdminData() {
    return { admin: true };
  }
}

Middleware

import { Injectable, NestMiddleware } from "@nestjs/common";
import { Request, Response, NextFunction } from "express";

@Injectable()
export class LoggerMiddleware implements NestMiddleware {
  use(req: Request, res: Response, next: NextFunction) {
    console.log(`${req.method} ${req.path}`);
    next();
  }
}

@Module({
  imports: [UserModule],
})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(LoggerMiddleware)
      .forRoutes(
        { path: "users", method: RequestMethod.GET },
        { path: "users/:id", method: RequestMethod.GET }
      );
  }
}

DTOs (Data Transfer Objects)

import { IsString, IsEmail, MinLength } from "class-validator";

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

  @IsEmail()
  email: string;

  @IsOptional()
  @IsInt()
  age: number;
}

@Controller("users")
export class UserController {
  @Post()
  create(@Body() createUserDto: CreateUserDto) {
    // Validation happens automatically
    return this.userService.create(createUserDto);
  }
}

Database Integration with TypeORM

npm install @nestjs/typeorm typeorm sqlite3
import { Module } from "@nestjs/common";
import { TypeOrmModule } from "@nestjs/typeorm";
import { User } from "./entities/user.entity";
import { UserService } from "./user.service";
import { UserController } from "./user.controller";

@Module({
  imports: [
    TypeOrmModule.forRoot({
      type: "sqlite",
      database: "db.sqlite",
      entities: [User],
      synchronize: true,
    }),
    TypeOrmModule.forFeature([User]),
  ],
  controllers: [UserController],
  providers: [UserService],
})
export class AppModule {}

@Entity()
export class User {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  name: string;

  @Column()
  email: string;
}

@Injectable()
export class UserService {
  constructor(
    @InjectRepository(User)
    private userRepository: Repository<User>
  ) {}

  findAll() {
    return this.userRepository.find();
  }

  create(createUserDto: CreateUserDto) {
    const user = this.userRepository.create(createUserDto);
    return this.userRepository.save(user);
  }
}

Exception Handling

import { HttpException, HttpStatus } from "@nestjs/common";

@Get(":id")
getUser(@Param("id") id: string) {
  const user = this.userService.findOne(parseInt(id));
  if (!user) {
    throw new HttpException("User not found", HttpStatus.NOT_FOUND);
  }
  return user;
}

// Custom exception filter
@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
  catch(exception: HttpException, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse<Response>();
    const status = exception.getStatus();

    response.status(status).json({
      statusCode: status,
      message: exception.getResponse(),
    });
  }
}

// Use in main.ts
app.useGlobalFilters(new HttpExceptionFilter());

Testing

import { Test, TestingModule } from "@nestjs/testing";
import { UserController } from "./user.controller";
import { UserService } from "./user.service";

describe("UserController", () => {
  let controller: UserController;
  let service: UserService;

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      controllers: [UserController],
      providers: [
        {
          provide: UserService,
          useValue: {
            findAll: jest.fn().mockReturnValue([]),
          },
        },
      ],
    }).compile();

    controller = module.get<UserController>(UserController);
    service = module.get<UserService>(UserService);
  });

  it("should find all users", () => {
    expect(controller.findAll()).toEqual([]);
  });
});

FAQ

Q: Is NestJS overkill for small projects? A: Probably. Use Express for small APIs, NestJS for team projects with multiple developers.

Q: Can I use NestJS with MongoDB? A: Yes, use @nestjs/mongoose instead of TypeORM.

Q: What's the performance of NestJS? A: Similar to Express. NestJS builds on Express by default. You can use Fastify adapter for better performance.


NestJS brings structure and scalability to Node.js backend development. It's perfect for teams and large projects where consistency and maintainability matter.

Advertisement

Sanjeev Sharma

Written by

Sanjeev Sharma

Full Stack Engineer · E-mopro