
NestJS: архитектурный фреймворк
NestJS — это прогрессивный Node.js фреймворк для создания эффективных, масштабируемых и надёжных серверных приложений. Он использует TypeScript и вдохновлён архитектурой Angular, предоставляя мощную систему модулей, декораторов и внедрения зависимостей. NestJS идеально подходит для создания корпоративных приложений с чёткой архитектурой и поддерживает как REST API, так и GraphQL, микросервисы и WebSockets.
Архитектура NestJS
NestJS построен на принципах архитектуры, которая обеспечивает модульность, тестируемость и масштабируемость:
- Модули — основная единица организации кода, которая инкапсулирует связанную функциональность
- Контроллеры — обрабатывают входящие HTTP-запросы и возвращают ответы
- Сервисы — содержат бизнес-логику и могут быть переиспользованы в разных частях приложения
- Провайдеры — классы, которые могут быть внедрены в другие классы (сервисы, контроллеры и т.д.)
- Middleware — функции, выполняемые перед обработкой запроса
- Guards — защищают маршруты на основе определённых условий
- Interceptors — перехватывают и модифицируют запросы и ответы
- Pipes — валидируют и трансформируют данные
Установка и настройка
Для начала работы с NestJS необходимо установить CLI и создать новый проект:
npm i -g @nestjs/cli
nest new nestjs-app
cd nestjs-app
После создания проекта у вас будет следующая структура:
nestjs-app/
├── src/
│ ├── app.controller.ts
│ ├── app.service.ts
│ ├── app.module.ts
│ └── main.ts
├── test/
├── package.json
└── nest-cli.json
Основные компоненты
Модули (Modules)
Модули — это способ организации кода в NestJS. Каждое приложение имеет как минимум один корневой модуль:
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
@Module({
imports: [],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
Пояснения:
@Module()
— декоратор, который определяет модульimports
— массив других модулей, которые импортируются в текущийcontrollers
— массив контроллеров, принадлежащих модулюproviders
— массив сервисов и других провайдеров
Контроллеры (Controllers)
Контроллеры обрабатывают входящие HTTP-запросы и возвращают ответы клиенту:
import { Controller, Get, Post, Body, Param, Delete, Put } from '@nestjs/common';
import { UsersService } from './users.service';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
@Controller('users')
export class UsersController {
constructor(private readonly usersService: UsersService) {}
@Post()
create(@Body() createUserDto: CreateUserDto) {
return this.usersService.create(createUserDto);
}
@Get()
findAll() {
return this.usersService.findAll();
}
@Get(':id')
findOne(@Param('id') id: string) {
return this.usersService.findOne(+id);
}
@Put(':id')
update(@Param('id') id: string, @Body() updateUserDto: UpdateUserDto) {
return this.usersService.update(+id, updateUserDto);
}
@Delete(':id')
remove(@Param('id') id: string) {
return this.usersService.remove(+id);
}
}
Пояснения:
@Controller('users')
— определяет базовый путь для всех маршрутов контроллера@Get()
,@Post()
,@Put()
,@Delete()
— HTTP-методы@Body()
— извлекает данные из тела запроса@Param()
— извлекает параметры из URL
Сервисы (Services)
Сервисы содержат бизнес-логику и могут быть переиспользованы в разных контроллерах:
import { Injectable, NotFoundException } from '@nestjs/common';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
@Injectable()
export class UsersService {
private users = [
{ id: 1, name: 'Иван', email: 'ivan@example.com' },
{ id: 2, name: 'Мария', email: 'maria@example.com' },
];
create(createUserDto: CreateUserDto) {
const newUser = {
id: this.users.length + 1,
...createUserDto,
};
this.users.push(newUser);
return newUser;
}
findAll() {
return this.users;
}
findOne(id: number) {
const user = this.users.find(user => user.id === id);
if (!user) {
throw new NotFoundException(`Пользователь с ID ${id} не найден`);
}
return user;
}
update(id: number, updateUserDto: UpdateUserDto) {
const userIndex = this.users.findIndex(user => user.id === id);
if (userIndex === -1) {
throw new NotFoundException(`Пользователь с ID ${id} не найден`);
}
this.users[userIndex] = { ...this.users[userIndex], ...updateUserDto };
return this.users[userIndex];
}
remove(id: number) {
const userIndex = this.users.findIndex(user => user.id === id);
if (userIndex === -1) {
throw new NotFoundException(`Пользователь с ID ${id} не найден`);
}
const deletedUser = this.users[userIndex];
this.users.splice(userIndex, 1);
return deletedUser;
}
}
Пояснения:
@Injectable()
— декоратор, который позволяет внедрять сервис в другие классыNotFoundException
— встроенный класс исключений для обработки ошибок- Методы сервиса содержат бизнес-логику и не зависят от HTTP-слоя
DTO (Data Transfer Objects)
DTO используются для валидации входящих данных:
import { IsString, IsEmail, IsOptional } from 'class-validator';
export class CreateUserDto {
@IsString()
name: string;
@IsEmail()
email: string;
}
export class UpdateUserDto {
@IsOptional()
@IsString()
name?: string;
@IsOptional()
@IsEmail()
email?: string;
}
Пояснения:
@IsString()
— валидатор, проверяющий, что поле является строкой@IsEmail()
— валидатор для email@IsOptional()
— делает поле необязательным
Внедрение зависимостей
NestJS использует мощную систему внедрения зависимостей (DI), которая автоматически создаёт и внедряет экземпляры классов:
@Controller('users')
export class UsersController {
constructor(private readonly usersService: UsersService) {}
// NestJS автоматически создаст экземпляр UsersService и внедрит его
}
Middleware
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(`[${new Date().toISOString()}] ${req.method} ${req.url}`);
next();
}
}
Для использования middleware нужно зарегистрировать его в модуле:
import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
import { LoggerMiddleware } from './logger.middleware';
@Module({
// ...
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(LoggerMiddleware)
.forRoutes('*');
}
}
Guards
Guards используются для защиты маршрутов:
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Observable } from 'rxjs';
@Injectable()
export class AuthGuard implements CanActivate {
canActivate(
context: ExecutionContext,
): boolean | Promise<boolean> | Observable<boolean> {
const request = context.switchToHttp().getRequest();
const token = request.headers.authorization;
// Проверка токена
return token && token.startsWith('Bearer ');
}
}
Использование guard:
@Controller('users')
export class UsersController {
@Get('profile')
@UseGuards(AuthGuard)
getProfile() {
return { message: 'Защищённый маршрут' };
}
}
Interceptors
Interceptors позволяют перехватывать и модифицировать запросы и ответы:
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
@Injectable()
export class TransformInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
return next.handle().pipe(
map(data => ({
data,
timestamp: new Date().toISOString(),
success: true
})),
);
}
}
Pipes
Pipes используются для валидации и трансформации данных:
import { PipeTransform, Injectable, ArgumentMetadata, BadRequestException } from '@nestjs/common';
@Injectable()
export class ValidationPipe implements PipeTransform {
transform(value: any, metadata: ArgumentMetadata) {
if (!value) {
throw new BadRequestException('Значение не может быть пустым');
}
return value;
}
}
Конфигурация и переменные окружения
NestJS поддерживает работу с переменными окружения через @nestjs/config
:
npm install @nestjs/config
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
envFilePath: '.env',
}),
],
})
export class AppModule {}
База данных с TypeORM
Для работы с базой данных в NestJS часто используется TypeORM:
npm install @nestjs/typeorm typeorm pg
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { UsersModule } from './users/users.module';
@Module({
imports: [
TypeOrmModule.forRoot({
type: 'postgres',
host: 'localhost',
port: 5432,
username: 'postgres',
password: 'password',
database: 'nestjs_db',
entities: [__dirname + '/**/*.entity{.ts,.js}'],
synchronize: true, // Только для разработки!
}),
UsersModule,
],
})
export class AppModule {}
Тестирование
NestJS предоставляет мощные инструменты для тестирования:
import { Test, TestingModule } from '@nestjs/testing';
import { UsersController } from './users.controller';
import { UsersService } from './users.service';
describe('UsersController', () => {
let controller: UsersController;
let service: UsersService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [UsersController],
providers: [UsersService],
}).compile();
controller = module.get<UsersController>(UsersController);
service = module.get<UsersService>(UsersService);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
it('should return all users', () => {
const result = [{ id: 1, name: 'Test' }];
jest.spyOn(service, 'findAll').mockImplementation(() => result);
expect(controller.findAll()).toBe(result);
});
});
Запуск приложения
Для запуска приложения используйте команды:
# Режим разработки
npm run start:dev
# Продакшн режим
npm run start:prod
# Отладка
npm run start:debug
Преимущества NestJS
- Архитектурная ясность — чёткое разделение ответственности между компонентами
- TypeScript — полная поддержка типизации из коробки
- Модульность — возможность создавать переиспользуемые модули
- Внедрение зависимостей — автоматическое управление зависимостями
- Тестируемость — встроенные инструменты для unit и e2e тестирования
- Масштабируемость — поддержка микросервисной архитектуры
- Экосистема — богатая экосистема пакетов и интеграций
Заключение
NestJS — это мощный фреймворк, который предоставляет архитектурные решения для создания масштабируемых серверных приложений. Благодаря использованию TypeScript, внедрению зависимостей и модульной архитектуре, NestJS позволяет создавать надёжные и поддерживаемые приложения. Фреймворк особенно хорошо подходит для корпоративных проектов, где важны архитектурная ясность и возможность масштабирования.
Для изучения NestJS рекомендуется начать с официальной документации и постепенно осваивать различные концепции: модули, контроллеры, сервисы, middleware, guards и interceptors. Это поможет создать прочную основу для разработки сложных приложений.