@makebelieve21213-packages/nest-common
v1.1.7
Published
Common NestJS utilities, guards, interceptors, and shared modules for NakolenkeChain microservices
Maintainers
Readme
@makebelieve21213-packages/nest-common
Общий пакет с утилитами для NestJS микросервисов. Предоставляет единую систему обработки ошибок, валидации, логирования и базовые классы для HTTP, RPC и WebSocket контекстов.
📋 Содержание
- Возможности
- Требования
- Установка
- Быстрый старт
- Система обработки ошибок
- API Reference
- Best Practices
- Тестирование
- Разработка
🚀 Возможности
- ✅ Единая система обработки ошибок - автоматическое преобразование ошибок между HTTP, RPC и WebSocket контекстами
- ✅ Глобальные фильтры - автоматическая обработка всех ошибок без try-catch в контроллерах
- ✅ Валидация - готовые пайпы для валидации DTO, файлов, query параметров и заголовков
- ✅ Логирование - автоматическое логирование всех HTTP, RPC и WebSocket запросов
- ✅ Guards - готовые guards для аутентификации, авторизации и rate limiting
- ✅ Декораторы - удобные декораторы для метаданных (@Public, @Roles, @Permissions, @ApiKey, @Serialize)
- ✅ Утилиты - функции для работы с контекстом, файлами, CORS, compression, versioning
- ✅ 100% покрытие тестами - надежность и качество кода
- ✅ TypeScript типизация - полная типобезопасность
📋 Требования
- Node.js: >= 22.11.0
- npm: >= 10.0.0
- NestJS: >= 11.0.0
📦 Установка
npm install @makebelieve21213-packages/nest-common🔧 Быстрый старт
Шаг 1: Регистрация глобальных фильтров и перехватчиков
// main.ts
import { NestFactory } from "@nestjs/core";
import { UnifiedExceptionFilter, UnifiedInterceptor } from "@makebelieve21213-packages/nest-common";
import { LoggerService } from "@makebelieve21213-packages/logger";
async function bootstrap() {
const app = await NestFactory.create(AppModule);
const logger = await connectLogger(app, "ServiceName");
// Регистрируем глобальный фильтр ошибок
app.useGlobalFilters(new UnifiedExceptionFilter(logger, dlxExchange)); // dlxExchange опционально
// Регистрируем глобальный перехватчик логирования
app.useGlobalInterceptors(new UnifiedInterceptor(logger));
await app.listen(3000);
}Шаг 2: HTTP контроллер
import { Controller, Get } from "@nestjs/common";
import { BaseController } from "@makebelieve21213-packages/nest-common";
import { LoggerService } from "@makebelieve21213-packages/logger";
@Controller("test")
export default class TestController extends BaseController {
constructor(
private readonly testService: TestService,
logger: LoggerService,
) {
super(logger);
}
@Get("data")
async getData() {
// БЕЗ try-catch - глобальный фильтр все обработает
return await this.testService.getData();
}
}Шаг 3: RPC контроллер
import { Controller } from "@nestjs/common";
import { MessagePattern, Payload, Ctx } from "@nestjs/microservices";
import { BaseController, RpcValidationPipe } from "@makebelieve21213-packages/nest-common";
import { LoggerService } from "@makebelieve21213-packages/logger";
@Controller()
export default class TestController extends BaseController {
constructor(
private readonly testService: TestService,
logger: LoggerService,
) {
super(logger);
}
@MessagePattern("test.pattern")
@UsePipes(new RpcValidationPipe(TestRequestDto))
async getData(
@Payload() dto: TestRequestDto,
@Ctx() ctx: RmqContext,
): Promise<TestResponseDto> {
const data = await this.testService.getData();
this.acknowledge(ctx);
return data;
}
}Шаг 4: Использование Guards
import { Controller, Get, UseGuards } from "@nestjs/common";
import { JwtAuthGuard, RolesGuard, Roles, Public } from "@makebelieve21213-packages/nest-common";
@Controller("test")
@UseGuards(JwtAuthGuard, RolesGuard)
export default class TestController extends BaseController {
@Get("public")
@Public() // Публичный эндпоинт
async getPublicInfo() {
return { message: "Public information" };
}
@Get("profile")
async getProfile(@Request() req) {
return await this.testService.getProfile(req.user);
}
@Get("admin")
@Roles("admin", "moderator") // Требуются роли
async getAdminData() {
return await this.testService.getData();
}
}🔄 Система обработки ошибок
Пакет предоставляет единую систему обработки ошибок для трех типов контекстов:
- HTTP - для HTTP контроллеров
- RPC - для RabbitMQ RPC запросов
- WebSocket - для Socket.io соединений
Все ошибки автоматически преобразуются между контекстами с сохранением типа и статус-кода.
Принципы работы
- Глобальные фильтры - все ошибки обрабатываются автоматически через
UnifiedExceptionFilter - Без catch блоков в контроллерах - контроллеры не должны иметь try-catch блоков
- Автоматическое преобразование - ошибки автоматически преобразуются между контекстами
- Сохранение типа и статуса - тип ошибки и статус-код сохраняются при преобразовании
Классы ошибок
HttpError - для HTTP контекста
- Автоматически преобразует
RpcException/RpcErrorв HTTP ошибку - Сохраняет правильный HTTP статус-код
- Метод:
HttpError.fromUnknown(error, descriptionPrefix?)
RpcError - для RabbitMQ RPC контекста
- Автоматически определяет тип ошибки (временная/постоянная)
- Используется в
RpcExceptionFilterдля retry/DLX логики - Метод:
RpcError.fromUnknown(error) - Метод:
isTransient(): boolean
SocketError - для WebSocket контекста
- Используется для обработки ошибок при отправке событий через Socket.io
- Метод:
SocketError.fromUnknown(error)
JsonRpcException - для JSON-RPC 2.0 протокола
- Поддерживает стандартные коды ошибок JSON-RPC 2.0
- Автоматически маппит HTTP статусы на JSON-RPC коды ошибок
📚 API Reference
Фильтры
UnifiedExceptionFilter - единый глобальный фильтр для обработки HTTP и RPC ошибок
app.useGlobalFilters(new UnifiedExceptionFilter(logger, dlxExchange));JsonRpcExceptionFilter - фильтр для обработки JSON-RPC 2.0 ошибок
@UseFilters(JsonRpcExceptionFilter)Перехватчики
UnifiedInterceptor - единый глобальный перехватчик для логирования запросов
app.useGlobalInterceptors(new UnifiedInterceptor(logger));ResponseInterceptor - стандартизация формата HTTP ответов
@UseInterceptors(ResponseInterceptor)SerializeInterceptor - сериализация ответов с исключением чувствительных данных
@UseInterceptors(SerializeInterceptor)
@Serialize(UserResponseDto)CompressionInterceptor - автоматическое сжатие больших ответов
@UseInterceptors(new CompressionInterceptor(2048, 0.7))RequestIdResponseInterceptor - добавление Request ID в заголовки ответов
@UseInterceptors(RequestIdResponseInterceptor)Пайпы валидации
HttpValidationPipe - валидация DTO для HTTP контроллеров
@UsePipes(new HttpValidationPipe(CreateDto))RpcValidationPipe - валидация DTO для RabbitMQ (автоматически извлекает correlationId и correlationTimestamp)
@UsePipes(new RpcValidationPipe(TestRequestDto))JsonRpcValidationPipe - валидация JSON-RPC 2.0 запросов
@Body(JsonRpcValidationPipe) request: JsonRpcRequestFileValidationPipe - валидация загруженных файлов
@UploadedFile(new FileValidationPipe({ maxSize: 5 * 1024 * 1024 }))QueryValidationPipe - валидация query параметров
@Query(new QueryValidationPipe(PaginationDto)) query: PaginationDtoHeaderValidationPipe - валидация заголовков
@Headers(new HeaderValidationPipe(ApiHeadersDto)) headers: ApiHeadersDtoGuards
JwtAuthGuard - проверка JWT токена
@UseGuards(JwtAuthGuard)ApiKeyGuard - проверка API ключа (работает с декоратором @ApiKey())
// Регистрация в модуле (Reflector из @nestjs/core)
{
provide: ApiKeyGuard,
useFactory: (reflector: Reflector) =>
new ApiKeyGuard(reflector, "x-api-key", new Set(["key1", "key2"])),
inject: [Reflector],
}
// В контроллере
@UseGuards(ApiKeyGuard)
@ApiKey()RolesGuard - проверка ролей пользователя (работает с декоратором @Roles())
@UseGuards(JwtAuthGuard, RolesGuard)
@Roles("admin", "moderator")PermissionsGuard - проверка разрешений пользователя (работает с декоратором @Permissions())
@UseGuards(JwtAuthGuard, PermissionsGuard)
@Permissions("users:delete", "users:write")RateLimitGuard - ограничение частоты запросов
// Регистрация в модуле (Reflector из @nestjs/core)
{
provide: RateLimitGuard,
useFactory: (reflector: Reflector) =>
new RateLimitGuard(reflector, 200, 60000), // 200 запросов в минуту
inject: [Reflector],
}
// В контроллере
@UseGuards(RateLimitGuard)WebSocketAuthGuard - проверка аутентификации WebSocket
@UseGuards(new WebSocketAuthGuard(async (token, context) => {
return await this.authService.validateToken(token);
}))Декораторы
@Public()- пометка публичного эндпоинта@Roles(...)- указание требуемых ролей@Permissions(...)- указание требуемых разрешений@ApiKey()- пометка эндпоинта как требующего API ключ@Serialize(DtoClass)- указание DTO для сериализации ответа
Утилиты
validateEnv - валидация переменных окружения с Joi
const validatedEnv = validateEnv(env, ["DATABASE_URL", "REDIS_URL"]);getUserFromContext - извлечение пользователя из контекста
const user = getUserFromContext(context);getIpFromContext - извлечение IP адреса из контекста
const ip = getIpFromContext(context);getRequestIdFromContext - извлечение Request ID из контекста
const requestId = getRequestIdFromContext(context);createCorsOptions - создание опций CORS
app.enableCors(createCorsOptions({ origin: ["https://example.com"] }));createCompressionOptions - создание опций compression
app.use(compression(createCompressionOptions({ level: 9 })));createVersioningOptions - создание опций версионирования
app.enableVersioning(createVersioningOptions({ type: "uri", defaultVersion: "1" }));validateFile - валидация файла
const result = validateFile(file, { maxSize: 5 * 1024 * 1024 });CircuitBreakerService - реализация паттерна Circuit Breaker
const circuitBreaker = new CircuitBreakerService(logger, {
failureThreshold: 5,
successThreshold: 2,
resetTimeout: 60000,
});getServicePath - определение пути в сервисе
const path = getServicePath({ serviceName: "test-service", dirname: __dirname, pathType: "locales", relativePath: "locales" });BaseController
Базовый контроллер для всех контроллеров проекта. Предоставляет метод acknowledge(ctx: RmqContext) для подтверждения RPC сообщений.
export default class TestController extends BaseController {
constructor(
private readonly testService: TestService,
logger: LoggerService,
) {
super(logger);
}
}✅ Best Practices
1. Не используйте catch блоки в контроллерах
// ❌ ПЛОХО
@Get("global")
async getGlobal() {
try {
return await this.service.getData();
} catch (error) {
// Обработка ошибок через try-catch не нужна
}
}
// ✅ ХОРОШО
@Get("global")
async getGlobal() {
// Глобальный фильтр все обработает
return await this.service.getData();
}2. Используйте catch блоки в сервисах только для логирования
// ✅ ХОРОШО - дополнительное логирование
async getData() {
try {
return await this.externalService.call();
} catch (error) {
this.logger.error(`Дополнительное логирование: ${error}`);
// Пробрасываем дальше - фильтр преобразует
throw error;
}
}3. Не преобразуйте ошибки вручную в контроллерах
// ❌ ПЛОХО
@Get("global")
async getGlobal() {
try {
return await this.service.getData();
} catch (error) {
const httpError = HttpError.fromUnknown(error);
throw httpError;
}
}
// ✅ ХОРОШО - фильтр сделает это автоматически
@Get("global")
async getGlobal() {
return await this.service.getData();
}4. Используйте правильный тип ошибки для контекста
// HTTP контекст → HttpError (автоматически через UnifiedExceptionFilter)
// RPC контекст → RpcError (автоматически через UnifiedExceptionFilter)
// Socket контекст → SocketError (вручную в SocketGateway.publish())5. Используйте валидацию через пайпы
// ✅ ХОРОШО
@Post("create")
@UsePipes(new HttpValidationPipe(CreateDto))
async create(@Body() dto: CreateDto) {
return await this.service.create(dto);
}6. Используйте guards с соответствующими декораторами
// ✅ ХОРОШО
@UseGuards(JwtAuthGuard, RolesGuard)
@Roles("admin", "moderator")
async getAdminData() {
return await this.testService.getData();
}🧪 Тестирование
Пакет имеет высокое покрытие тестами (>95% для веток, 100% для statements и функций).
# Запуск тестов
npm test
# Запуск тестов с покрытием
npm run test:coverageПокрытие тестами:
- Statements: 100%
- Branches: 97%+
- Functions: 100%
- Lines: 100%
🏗️ Разработка
Технический стек
- TypeScript 5.7+ - строгая типизация
- ESM модули - современный стандарт модулей JavaScript
- NestJS 11.x - фреймворк для микросервисов
- Jest - тестирование
Процесс сборки
Пакет использует многоэтапную сборку для корректной работы ESM:
- TypeScript компиляция (
tsc --build) - компиляция TypeScript в JavaScript - Замена алиасов (
tsc-alias) - замена путейsrc/*на относительные - Исправление ESM (
tsc-esm-fix) - добавление.jsрасширений к импортам
# Установка зависимостей
npm install
# Сборка
npm run build
# Запуск тестов
npm test
# Запуск тестов с покрытием
npm run test:coverage
# Линтер
npm run lint
npm run lint:fix
# Форматирование
npm run format
npm run format:fixGit Hooks (Husky)
Пакет использует Husky для автоматической проверки кода:
- pre-commit: Автоматически исправляет линтер и форматирование перед коммитом
- pre-push: Запускает тесты с проверкой покрытия перед push
🐳 Развертывание в Docker
Сборка образа
Соберите Docker образ из корня проекта:
docker build -t nest-common-package:latest .Запуск контейнера
docker run -d \
--name nest-common-package \
nest-common-package:latestСовместимость
- Node.js: >=22.11.0
- npm: >=10.0.0
- @nestjs/common: ^11.1.6
- @nestjs/microservices: ^11.1.3
- rxjs: ^7.8.2
📝 Лицензия
MIT License - см. файл LICENSE для деталей.
🤝 Contribution
Pull requests приветствуются! Для крупных изменений, пожалуйста, сначала откройте issue для обсуждения.
