QueryBuilder нужен, когда репозиторного find уже мало: join, агрегация, часть полей или особый порядок. Он строит SQL с параметрами, а не склеивает входные данные в строку.
Join и параметры
const products = await repo.createQueryBuilder('product')
.leftJoinAndSelect('product.seller', 'seller')
.where('product.price >= :minPrice', { minPrice: 1000 })
.andWhere('seller.active = :active', { active: true })
.orderBy('product.id', 'ASC')
.getMany();
product и seller - aliases. Они используются в join, where и select. Опечатка в alias приводит к ошибке SQL или неожиданному запросу, поэтому единые имена выбирают на уровне сценария.
Параметр :minPrice передается драйверу отдельно. Строка product.price >= ${req.query.price} создает SQL-инъекцию и ломает типы.
Проекция вместо всей entity
Для списка не всегда нужны все колонки и все relations. QueryBuilder позволяет выбрать поля:
const rows = await repo.createQueryBuilder('product')
.select('product.id', 'id')
.addSelect('product.name', 'name')
.where('product.active = :active', { active: true })
.getRawMany();
getMany возвращает entities, getRawMany - строки проекции. Выбирают одно поведение явно и не смешивают ожидания.
Коротко
- QueryBuilder используют для joins, проекций и агрегатов.
- Входные данные передают параметрами, не интерполяцией.
- Aliases должны совпадать во всех частях запроса.
getManyиgetRawManyимеют разный результат.- Выбирают только нужные поля и relations.
Что почитать дальше
- Relations и lazy loading - какой join нужен.
- Производительность и ошибки - EXPLAIN и пагинация.