Перейти к содержимому

Utils-клиенты (libs/apis/utils)

libs/apis/utils содержит тонкие NestJS-обёртки над внешними инфраструктурными клиентами (БД, кэш, объектное хранилище, брокер сообщений, почта). Каждая обёртка — отдельная Nx-библиотека с модулем + сервисом, инкапсулирующая создание соединения, его жизненный цикл (OnModuleInit / OnModuleDestroy / OnApplicationShutdown) и типизированный API. Конфигурация в каждый клиент приходит из соответствующего конфиг-модуля из libs/apis/configs.

КлиентОборачивает (npm)Импорт (алиас)Как подключаетсяИсточник конфига
prisma-client@prisma/client (PrismaClient)@crewsforge-back/apis/utils/prisma-clientPrismaClientModule импортирует AppConfigModule; сервис extends PrismaClient, $connect в onModuleInit, $disconnect в onModuleDestroyAppConfigService (уровень логов зависит от env)
redis-clientredis (node-redis v4, createClient)@crewsforge-back/redis-clientПровайдер-фабрика REDIS_CLIENT создаёт и connect()-ит клиент; сервис инжектит его через @Inject(REDIS_CLIENT)RedisConfigService (config-shared-redis)
s3-client@aws-sdk/client-s3 + @aws-sdk/s3-request-presigner@crewsforge-back/s3-clientПровайдер-фабрика S3_CLIENT создаёт new S3Client({...}); сервис инжектит клиент и S3ConfigServiceS3ConfigService (config-shared-s3)
rabbit-clientamqplib@crewsforge-back/rabbit-clientСервис вручную открывает соединение/канал в методе connect() (не в фабрике), хранит connection+channelRabbitConfigService (config-shared-rabbit)
mailer-client@nestjs-modules/mailer (nodemailer SMTP)@crewsforge-back/mailer-clientMailerModule.forRootAsync(...) с фабрикой из SmtpConfigService; сервис оборачивает MailerServiceSmtpConfigService (config-shared-smtp)

Обёртка над PrismaClient. PrismaClientService extends PrismaClient и реализует OnModuleInit ($connect) / OnModuleDestroy ($disconnect). Уровень логирования зависит от окружения: в non-production добавляются info, warn, query к базовому error (через AppConfigService.env). Модуль импортирует AppConfigModule, провайдит и экспортирует сервис. Регистрируется через обычный imports: [PrismaClientModule] (без forRoot).

Сериализация денег. Конструктор сервиса вызывает enableBigIntJsonSerialization() из @crewsforge-back/apis/core/money. Причина: денежные поля схемы — BigInt в минорных единицах, а JSON.stringify этот тип не умеет и бросает TypeError — любой ответ с суммой отдавал бы 500. Настройка живёт здесь, а не в main.ts каждого сервиса, потому что источник BigInt в приложении — именно Prisma: так её получают и тесты, собирающие AppModule напрямую. В ответах сумма представлена целым числом минорных единиц; значение за пределами безопасного целочисленного диапазона JSON даёт RangeError, а не молчаливую потерю точности.

Transactional adapter (@nestjs-cls). Сам PrismaClientModule НЕ содержит настройки транзакций. Декларативные транзакции подключаются на уровне каждого приложения в app.module.ts через nestjs-cls:

ClsModule.forRoot({
plugins: [
new ClsPluginTransactional({
// ...
adapter: new TransactionalAdapterPrisma({ prismaInjectionToken: PrismaClientService }),
}),
],
})

Адаптер @nestjs-cls/transactional-adapter-prisma привязывается к PrismaClientService через prismaInjectionToken. После этого:

  • Сервисы помечают методы декоратором @Transactional() (@nestjs-cls/transactional) — весь метод выполняется в одной транзакции через CLS-контекст.
  • Репозитории инжектят TransactionHost<TransactionalAdapterPrisma> и работают через this.txHost.tx.<model>..., что автоматически использует активную транзакцию (или обычное соединение вне транзакции).

Так одно и то же соединение Prisma участвует и в обычных запросах, и в транзакциях без ручного проброса tx. Настройка присутствует во всех приложениях: auth-api, user-api, admin-api, project-api, ai-agent-service.

Обёртка над node-redis. Клиент создаётся провайдером-фабрикой под токеном REDIS_CLIENT (Symbol): createClient({ socket: { host, port }, password }), вешается обработчик error, вызывается await client.connect(). RedisClientService инжектит клиент через @Inject(REDIS_CLIENT) и предоставляет удобный API: set (с опциональным TTL через EX), get, множества (sAdd/sRem/sMembers), deleteByPattern (итеративный SCAN + DEL), геттер client. При onModuleDestroy / onApplicationShutdown — корректный close() + destroy(). Модуль экспортирует и токен REDIS_CLIENT, и сервис.

Обёртка над AWS SDK v3. Клиент new S3Client({ endpoint, region, credentials, forcePathStyle: false }) создаётся провайдером-фабрикой под токеном S3_CLIENT (Symbol) из S3ConfigService. S3ClientService инжектит клиент и конфиг и предоставляет доменный API: uploadFile / deleteFile / getFileUrl (presigned URL через getSignedUrl, TTL по умолчанию 3600с, ACL по умолчанию private), а также билдеры путей и хелперы для аватаров/портфолио пользователя (buildUserAvatarPath, uploadUserAvatar, getUserPortfolioUrl и т.д.). Бакет берётся из S3ConfigService.bucket. При shutdown — s3Client.destroy().

Обёртка над amqplib. В отличие от redis/s3, соединение НЕ создаётся фабрикой при старте модуля — RabbitClientService открывает его вручную методом connect(): строит URL amqp://user:pass@host:port, создаёт connection и channel, выставляет prefetch из конфига, вешает обработчики error/close. API: assertExchange, assertQueue (durable), bindQueue, publish (persistent JSON-сообщения), consume (с авто-ack при успехе и nack(false,false) при ошибке через Logger). При onModuleDestroy / onApplicationShutdown закрывает канал и соединение. Модуль импортирует RabbitConfigModule.

Обёртка над @nestjs-modules/mailer (поверх nodemailer). Модуль подключает транспорт через MailerModule.forRootAsync(...): SMTP-транспорт (host, port, secure: false, auth: { user, pass }, tls.rejectUnauthorized: false) и defaults.from — всё из SmtpConfigService. MailerClientService оборачивает MailerService и даёт методы: send(options), sendTemplate(to, subject, template, context, extras), sendPlain(to, subject, text, html, extras), sendBulk(mails[]), геттер client. При shutdown пытается аккуратно закрыть транспорт. Используется провайдером email-sender-api (см. docs/03-services/email-sender.md).