NestJSJuniorTechnical
Что такое декораторы (decorators) в NestJS? Приведите примеры часто используемых декораторов.
Декораторы в NestJS — TypeScript-аннотации на основе reflect-metadata. Используются для маршрутизации (@Get, @Post), DI (@Injectable, @Inject), параметров (@Body, @Param, @Query) и кросс-срезовых задач (@UseGuards, @UsePipes).
Декораторы в NestJS
Декораторы — синтаксический сахар TypeScript над функциями-обёртками, которые применяются к классам, методам, параметрам и свойствам. NestJS строит на них весь метаданных-ориентированный DI и маршрутизацию. Под капотом используется reflect-metadata для хранения метаданных при компиляции.
Декораторы классов
@Module({ imports: [], controllers: [], providers: [] })
export class AppModule {}
@Controller('users') // базовый путь /users
export class UsersController {}
@Injectable() // провайдер, управляемый DI
export class UsersService {}
@Global() // модуль доступен везде без импорта
@Module({ providers: [ConfigService], exports: [ConfigService] })
export class ConfigModule {}
Декораторы методов контроллера (роуты)
@Get('profile') // GET /users/profile
@Post() // POST /users
@Put(':id') // PUT /users/:id
@Patch(':id') // PATCH /users/:id
@Delete(':id') // DELETE /users/:id
@All('*') // любой метод
@HttpCode(201) // явный статус ответа
@Header('Cache-Control', 'no-cache') // заголовок ответа
@Redirect('https://nestjs.com', 301) // редирект
Декораторы параметров метода
@Get(':id')
findOne(
@Param('id', ParseIntPipe) id: number, // route param
@Query('sort') sort: string, // query string
@Body() dto: CreateUserDto, // request body
@Req() req: Request, // весь объект запроса
@Res({ passthrough: true }) res: Response, // объект ответа
@Headers('authorization') auth: string, // заголовок
@Ip() ip: string, // IP клиента
@HostParam('account') account: string, // поддомен
) {}
Декораторы области видимости и жизненного цикла
// Scope.REQUEST — новый экземпляр на каждый запрос
@Injectable({ scope: Scope.REQUEST })
export class RequestScopedService {}
// Scope.TRANSIENT — новый экземпляр на каждый inject
@Injectable({ scope: Scope.TRANSIENT })
export class TransientService {}
Декораторы guards, interceptors, pipes, filters
@UseGuards(JwtAuthGuard) // Guard — авторизация
@UseInterceptors(LoggingInterceptor) // Interceptor — до/после
@UsePipes(ValidationPipe) // Pipe — трансформация/валидация
@UseFilters(HttpExceptionFilter) // Filter — обработка ошибок
Пользовательский декоратор параметра
// decorators/current-user.decorator.ts
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
export const CurrentUser = createParamDecorator(
(data: string | undefined, ctx: ExecutionContext) => {
const request = ctx.switchToHttp().getRequest();
const user = request.user;
return data ? user?.[data] : user;
},
);
// использование
@Get('me')
getProfile(@CurrentUser() user: User) {
return user;
}
@Get('me/email')
getEmail(@CurrentUser('email') email: string) {
return { email };
}
Декоратор SetMetadata и Reflector
// decorators/roles.decorator.ts
import { SetMetadata } from '@nestjs/common';
export const Roles = (...roles: string[]) =>
SetMetadata('roles', roles);
// controllers/admin.controller.ts
@Roles('admin')
@UseGuards(RolesGuard)
@Get('settings')
getSettings() {}
// guards/roles.guard.ts
@Injectable()
export class RolesGuard implements CanActivate {
constructor(private reflector: Reflector) {}
canActivate(ctx: ExecutionContext): boolean {
const roles = this.reflector.getAllAndOverride<string[]>('roles', [
ctx.getHandler(),
ctx.getClass(),
]);
if (!roles) return true;
const { user } = ctx.switchToHttp().getRequest();
return roles.some(role => user.roles.includes(role));
}
}
Подводные камни
- emitDecoratorMetadata в tsconfig — без
"emitDecoratorMetadata": trueи"experimentalDecorators": trueDI вообще не работает; тип параметра не сохраняется в метаданных. - @Res() без passthrough — если инжектируете
@Res()без{ passthrough: true }, NestJS перестаёт управлять ответом: интерцепторы не сработают, JSON не сериализуется автоматически. - Порядок декораторов — декораторы применяются снизу вверх;
@UseGuardsнад@UseInterceptorsозначает, что guard сработает первым — это может удивить. - @Global() злоупотребление — глобальные модули упрощают доступ, но скрывают зависимости и усложняют тестирование отдельных модулей.
- createParamDecorator и DI — внутри
createParamDecoratorнельзя инжектировать сервисы напрямую; для этого нужен Pipe или guard, а не декоратор параметра. - SetMetadata и наследование — метаданные контроллера не наследуются автоматически в дочерних классах; нужно явно применять декоратор в каждом классе.
Common mistakes
- Дает общий ответ про Node.js и не называет конкретные API NestJS.
- Не объясняет, где в lifecycle находится декораторы NestJS.
- Не разделяет validation, authorization, business logic и persistence.
- Игнорирует ошибки, лимиты входных данных, observability и тестирование.
What the interviewer is testing
- Может объяснить декораторы NestJS на примере кода.
- Называет ключевые API: @Get(), @Body(), @UseGuards().
- Использует точные API NestJS, а не вымышленные hooks/decorators/methods.
- Видит production-риски: безопасность, отказоустойчивость, логирование и тесты.