Ответ
Работая с TypeORM в Node.js, я столкнулся со следующими сложностями и нашел способы их решения:
-
Проблема N+1 запроса: Самая частая проблема при ленивой загрузке связей (
{eager: false}). TypeORM по умолчанию не предупреждает об этом.- Решение: Явно использовать
leftJoinAndSelectв QueryBuilder или указыватьrelationsв методеfind. Для сложных сценариев писал raw SQL черезquery().// Проблема: N+1 запросов const users = await userRepository.find(); for (const user of users) { console.log(user.photos); // Отдельный запрос для каждого пользователя! } // Решение: Загрузка связей сразу const usersWithPhotos = await userRepository.find({ relations: ['photos'] }); // Или с QueryBuilder const users = await userRepository .createQueryBuilder('user') .leftJoinAndSelect('user.photos', 'photo') .getMany();
- Решение: Явно использовать
-
Производительность сложных запросов: Сгенерированный SQL для
QueryBuilderс множественнымиjoinиwhereиногда был неоптимальным.- Решение: Для критичных по производительности endpoint'ов я использовал нативные запросы (
manager.query()) или донастраивал индексы в БД, опираясь наEXPLAIN ANALYZE.
- Решение: Для критичных по производительности endpoint'ов я использовал нативные запросы (
-
Работа с миграциями: Автогенерация миграций через
typeorm migration:generateиногда пропускала изменения или генерировала лишние. Особенно это касалось переименования полей.- Решение: Я всегда внимательно проверял сгенерированный SQL-файл миграции перед применением. Для сложных изменений (типа изменения типа колонки с данными) писал миграции вручную, используя
QueryRunner.
- Решение: Я всегда внимательно проверял сгенерированный SQL-файл миграции перед применением. Для сложных изменений (типа изменения типа колонки с данными) писал миграции вручную, используя
-
Транзакции и управление подключением: Необходимость явно передавать
EntityManagerилиQueryRunnerв транзакцию усложняла архитектуру сервисов.- Решение: Я создавал абстракцию — Unit of Work паттерн или использовал вложенные транзакции через
dataSource.transaction, который автоматически управляет менеджером.
- Решение: Я создавал абстракцию — Unit of Work паттерн или использовал вложенные транзакции через
-
Типизация с кастомными репозиториями: TypeScript-типы иногда "терялись" при использовании кастомных методов репозитория, если не следовать строгому шаблону из документации.
- Решение: Я создавал абстрактные базовые репозитории с дженериками и явно указывал типы сущностей.