Каждый tRPC-запрос получает контекст — объект с информацией о текущем пользователе, доступными сервисами и функциями для работы с БД.

Структура контекста

{
  user: User | null,              // Текущий пользователь (или null)
  userAccess: {
    privileges: string[],         // Группы привилегий
    permissions: string[],        // Права доступа
  },
  sessionToken: string | null,    // Токен текущей сессии (или null)
  clientIp: string | null,        // IP клиента (cf-connecting-ip / x-real-ip / x-forwarded-for / remoteAddress)
  withDatabase,                   // Функция для запросов к БД
  withTransaction,                // Функция для транзакций
  avatarService,                  // Генерация аватаров
  storageService,                 // S3-хранилище
  oauthStateStorage,              // Хранилище OAuth-состояний
  verificationService,            // Верификация пользователей
  paymentsService,                // Платёжная система
  analyticsService,               // Аналитика
  notificationService,            // Уведомления и WebSocket
  rconService,                    // RCON-подключение к серверам
  permissionSyncService,          // Синхронизация прав с игровыми серверами
  punishmentsService,             // Наказания (баны, муты, варны)
  res,                            // Express Response (для cookies)
}

Пользователь

Если запрос содержит валидный токен авторизации (cookie или заголовок Authorization), в ctx.user будет объект пользователя из БД. Иначе — null.
// В protectedProcedure ctx.user гарантированно не null
const userId = ctx.user.id;
const username = ctx.user.username;

Работа с БД

withDatabase

Выполняет запрос к базе данных. Принимает функцию с аргументами db (Drizzle-клиент), schema (все таблицы) и dialect (тип БД).
const users = await ctx.withDatabase(({ db, schema }) =>
  db.select().from(schema.users).limit(10)
);

withTransaction

То же самое, но в рамках транзакции. Если функция выбросит ошибку — все изменения откатятся.
await ctx.withTransaction(async ({ db, schema }) => {
  await db.insert(schema.payments).values({ ... });
  await db.update(schema.users).set({ balance: newBalance }).where(...);
});
Обе функции автоматически подмешивают схемы модулей в объект schema. Это значит, что таблицы модулей доступны наравне с основными.

Сервисы

Сервисы прокидываются из NestJS через dependency injection. Каждый сервис — это интерфейс, описанный в packages/trpc/src/context.ts.
Динамические import() запрещены. Все зависимости должны прокидываться через контекст.

avatarService

Генерация аватаров по умолчанию.
if (ctx.avatarService) {
  const url = await ctx.avatarService.generateDefaultAvatar(username);
}

storageService

Загрузка файлов в S3-совместимое хранилище.
await ctx.storageService.uploadObject({
  key: 'avatars/user-1.png',
  body: buffer,
  contentType: 'image/png',
});

const url = ctx.storageService.getObjectUrl('avatars/user-1.png');

paymentsService

Создание платёжных ссылок и получение информации о платежах.
const { link } = await ctx.paymentsService.createPaymentLink(
  'yookassa', userId, 500, email, username
);
const providers = await ctx.paymentsService.getEnabledProviders();
const limits = await ctx.paymentsService.getPaymentLimits();

notificationService

Отправка уведомлений пользователям и broadcast.
await ctx.notificationService.send({
  userId: 1,
  type: 'payment',
  title: 'Оплата получена',
  message: 'Ваш баланс пополнен на 500 руб.',
  channels: ['push', 'websocket'],
});

// Отправка события через WebSocket
ctx.notificationService.emitToUser(userId, 'balance_update', { balance: 1500 });

verificationService

Управление верификацией пользователей.
const result = await ctx.verificationService.checkOnLogin(userId, {
  ip, userAgent, fingerprint, deviceInfo,
});

if (result.requiresVerification) {
  // Требуется верификация
}

rconService

Отправка команд на Minecraft серверы через RCON.
const response = await ctx.rconService.sendCommand(serverId, 'say Hello!');
const isOnline = ctx.rconService.isConnected(serverId);

analyticsService

Сбор и получение аналитики.
if (ctx.analyticsService?.isEnabled()) {
  ctx.analyticsService.trackPageView({ sessionId, path: '/shop', userId });
  const summary = await ctx.analyticsService.getSummary(30); // за 30 дней
  const realtime = await ctx.analyticsService.getRealTimeVisitors();
}

permissionSyncService

Синхронизация привилегий и прав с игровыми серверами (например, LuckPerms) при выдаче, отзыве и изменении.
if (ctx.permissionSyncService) {
  await ctx.permissionSyncService.onPrivilegeGranted(
    username, privilegeName, privilegeServerId, durationSeconds
  );
  await ctx.permissionSyncService.onUserPermissionAdded(username, node);
}

punishmentsService

Управление наказаниями (баны, муты, варны, HWID-баны) и покупками разбана.
const { punishments, total } = await ctx.punishmentsService.getPunishments({
  userId, isActive: true,
});
const price = await ctx.punishmentsService.calculateUnbanPrice(punishmentId, userId);
const stats = await ctx.punishmentsService.getStats();

Создание контекста

Контекст создаётся в packages/trpc/src/context.ts через функцию createContext. На сервере (NestJS) используется createContextFactory, которая принимает сервисы и возвращает фабрику контекста для каждого запроса.
const contextFactory = createContextFactory({
  avatarService,
  storageService,
  paymentsService,
  // ...
});

// Для каждого запроса:
const ctx = await contextFactory({ req, res });