Runtime
src/main.ts · src/app.module.ts · src/common/*
Цей документ описує не бізнес-домен, а те, як працює HTTP runtime stack-ai: global prefix, wrapper response, помилки, validation, Swagger, CORS і request timing.
Що це
stack-ai - NestJS backend. Вхідна точка - src/main.ts, кореневий модуль - src/app.module.ts.
Під час старту застосунок:
- Створює Nest app.
- Підключає global interceptors.
- Підключає global exception filter.
- Ставить global prefix.
- Вмикає cookies, CORS і JSON body limit.
- Вмикає validation pipe.
- Генерує Swagger UI.
- Слухає порт із налаштувань.
Global prefix
Усі routes живуть під:
/stack-aiПриклади:
/stack-ai/tips/generate;/stack-ai/tips/status;/stack-ai/legend/generate-profile;/stack-ai/legend/status;/stack-ai/api-docs.
Якщо route у controller виглядає як @Controller('tips') + @Post('generate'), реальний URL буде /stack-ai/tips/generate.
Response wrapper
ResponseWrapperInterceptor обгортає успішні відповіді:
{
"success": true,
"data": {}
}Controller methods повертають “чисті” дані, але клієнт майже завжди отримує wrapper. Це важливо для документації і frontend: DTO описує data, а фактичний HTTP body містить ще success.
Помилки
AllExceptionsFilter ловить усі exceptions і повертає:
{
"success": false,
"data": null,
"message": "..."
}Legacy-конвенція проєкту: навіть при помилці HTTP status виставляється 200.
Практичне правило для клієнтів:
- не покладатися тільки на HTTP status;
- завжди перевіряти
success; - якщо
success=false, показуватиmessage; - не очікувати стандартні 400/401/500 як основний signal.
Request timing
RequestTimingInterceptor підключений глобально. Його задача - логувати тривалість запитів і допомагати бачити важкі endpoints. Це особливо важливо для:
- LLM workflows;
- embeddings ingest;
- Supabase retrieval;
- Legend async job starts;
- upstream fetch.
Validation
Глобальний validation pipe описаний у src/common/pipes/validation.pipe.ts.
Сенс:
- DTO decorators (
@IsString,@IsEnum,@IsObject,@Min,@Max) реально беруть участь у перевірці body. - Validation errors проходять через загальний exception filter і стають
{ success:false, message }. - Деякі DTO мають index signature
[key: string]: unknown, тому strict whitelist не завжди ріже зайві поля. Це legacy-компроміс для гнучких payloads.
Swagger
Swagger UI доступний за:
/stack-ai/api-docsSwagger описує DTO і route-level contracts. Markdown docs описують бізнес-логіку, етапи, side effects і нюанси, яких Swagger не показує.
Правило підтримки документації:
- якщо змінюється DTO або route - оновити Swagger decorators;
- якщо змінюється поведінка, side effect, порядок викликів, fallback або бізнес-правило - оновити markdown docs.
CORS і body limit
Runtime вмикає:
cors({ origin: true, credentials: true });express.json({ limit: '50mb' });cookieParser().
Великий JSON limit потрібен через:
- великі dialog payloads;
- profile JSON;
- notes;
- legacy debug endpoints;
- можливі base64/large nested payloads.
AppModule
AppModule збирає основні модулі:
| Модуль | Роль |
|---|---|
ConfigModule | Env/config |
RequestContextModule | Доступ до auth token поточного request |
DatabaseModule | Supabase client/config |
UpstreamModule | HTTP client до зовнішніх Stack/family API |
LegendModule | Lady Legend pipeline |
TipsMemoryModule | Experimental memory/assistant API |
TipsModule | Новий tips status/generate flow |
Request context
RequestContext зберігає дані поточного запиту, насамперед authorization token. Upstream layer використовує цей token, щоб прокидати авторизацію у зовнішні Stack/family API.
Це важливо: stack-ai не завжди ходить в upstream як окремий service account. У частині flows він прокидає auth користувача/оператора далі.
Що важливо пам’ятати
- Усі реальні endpoints мають prefix
/stack-ai. - Успішні відповіді обгортаються в
{ success:true, data }. - Помилки теж приходять з HTTP 200, але
success:false. - Swagger і markdown docs доповнюють одне одного.
@UseGuardsна controller недостатньо: route має мати@ApiRoles, якщо він повинен бути закритий роллю.- Великі payloads дозволені навмисно, бо dialog/profile data може бути об’ємною.