Express gives you freedom — and lets you build a 10,000-line server.js. Your routes, middleware, validation, and business logic become an entangled mess. Every new developer asks "where does this go?"
What if Node.js had a framework like Spring Boot or ASP.NET — structured, modular, with dependency injection and clear conventions?
That's NestJS.
Quick Start
npm i -g @nestjs/cli
nest new my-api
cd my-api && npm run start:dev
Controllers — Handle HTTP Requests
@Controller("users")
export class UsersController {
constructor(private readonly usersService: UsersService) {}
@Get()
findAll(@Query("page") page: number = 1) {
return this.usersService.findAll(page);
}
@Get(":id")
findOne(@Param("id") id: string) {
return this.usersService.findOne(id);
}
@Post()
@HttpCode(201)
create(@Body() createUserDto: CreateUserDto) {
return this.usersService.create(createUserDto);
}
@Patch(":id")
update(@Param("id") id: string, @Body() updateUserDto: UpdateUserDto) {
return this.usersService.update(id, updateUserDto);
}
@Delete(":id")
@HttpCode(204)
remove(@Param("id") id: string) {
return this.usersService.remove(id);
}
}
Services — Business Logic
@Injectable()
export class UsersService {
constructor(
@InjectRepository(User) private usersRepo: Repository<User>,
private readonly emailService: EmailService,
) {}
async create(dto: CreateUserDto): Promise<User> {
const user = this.usersRepo.create(dto);
const saved = await this.usersRepo.save(user);
await this.emailService.sendWelcome(saved.email);
return saved;
}
async findAll(page: number): Promise<User[]> {
return this.usersRepo.find({
skip: (page - 1) * 20,
take: 20,
order: { createdAt: "DESC" },
});
}
async findOne(id: string): Promise<User> {
const user = await this.usersRepo.findOneBy({ id });
if (!user) throw new NotFoundException("User not found");
return user;
}
}
Modules — Organize Your App
@Module({
imports: [
TypeOrmModule.forFeature([User]),
EmailModule,
],
controllers: [UsersController],
providers: [UsersService],
exports: [UsersService],
})
export class UsersModule {}
@Module({
imports: [
ConfigModule.forRoot(),
TypeOrmModule.forRoot({
type: "postgres",
url: process.env.DATABASE_URL,
autoLoadEntities: true,
}),
UsersModule,
AuthModule,
OrdersModule,
],
})
export class AppModule {}
Validation With DTOs
import { IsEmail, IsString, MinLength, IsOptional } from "class-validator";
export class CreateUserDto {
@IsString()
@MinLength(1)
name: string;
@IsEmail()
email: string;
@IsString()
@MinLength(8)
password: string;
}
export class UpdateUserDto {
@IsOptional()
@IsString()
name?: string;
@IsOptional()
@IsEmail()
email?: string;
}
Validation happens automatically via the ValidationPipe — invalid requests get 400 errors with field-level messages.
Guards — Authentication
@Injectable()
export class JwtAuthGuard implements CanActivate {
constructor(private jwtService: JwtService) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
const request = context.switchToHttp().getRequest();
const token = request.headers.authorization?.split(" ")[1];
if (!token) throw new UnauthorizedException();
try {
request.user = await this.jwtService.verifyAsync(token);
return true;
} catch {
throw new UnauthorizedException();
}
}
}
// Use on routes
@UseGuards(JwtAuthGuard)
@Get("profile")
getProfile(@Request() req) {
return req.user;
}
Built-in Features
-
Swagger:
@nestjs/swagger— auto-generated API docs from decorators -
WebSockets:
@nestjs/websockets— gateway-based real-time communication -
GraphQL:
@nestjs/graphql— code-first or schema-first -
Microservices:
@nestjs/microservices— Redis, RabbitMQ, Kafka, gRPC -
Task scheduling:
@nestjs/schedule— cron jobs -
Caching:
@nestjs/cache-manager— Redis, in-memory
When to Choose NestJS
Choose NestJS when:
- Building enterprise APIs that need clear structure
- Your team comes from Java/C#/Spring Boot background
- You need microservices, WebSockets, or GraphQL built in
- Long-term maintainability matters more than initial velocity
Skip NestJS when:
- Simple API with 5-10 routes (Express/Hono is faster to start)
- You dislike decorators and dependency injection patterns
- Performance-critical hot path (Fastify raw is faster)
- Serverless functions (NestJS has overhead for cold starts)
The Bottom Line
NestJS brings enterprise architecture patterns to Node.js without the enterprise pain. Dependency injection, modules, and decorators create structure that scales from 5 routes to 500.
Start here: docs.nestjs.com
Need custom data extraction, scraping, or automation? I build tools that collect and process data at scale — 78 actors on Apify Store and 265+ open-source repos. Email me: Spinov001@gmail.com | My Apify Actors
Top comments (0)