При разработке на Node.js и TypeScript многие сталкиваются с одной и той же болью: ручное написание SQL-запросов и синхронизация схемы с кодом. Ошибка в SQL-запросе — и production лежит. TypeORM решает эту проблему, но его настройка требует учёта десятка нюансов: от конфигурации подключения до генерации миграций. Мы поможем вам настроить TypeORM под ваш проект — от простого сервера до сложного NestJS-приложения. Наши инженеры имеют более 10 лет опыта в работе с реляционными базами данных и гарантируют стабильность вашей схемы.
Почему стандартная настройка TypeORM часто приводит к проблемам?
N+1 запрос — классическая проблема при работе с ORM. Без оптимизации загрузка 100 статей с авторами породит 101 запрос. TypeORM позволяет легко настроить жадную загрузку через relations или использовать Query Builder для точного контроля. Ещё одна боль — synchronize: true в production: это приводит к потере данных при изменении схемы. Правильная настройка миграций с генерацией diff-файлов — единственный безопасный способ обновлять базу. Третья проблема — производительность: неоптимизированные запросы создают лишнюю нагрузку. TypeORM даёт инструменты для мониторинга и тонкой настройки: subscribers для кэширования, пул подключений, логирование медленных запросов. При типичной нагрузке 10 000 запросов в минуту грамотная настройка снижает количество запросов на 60%.
Как мы настраиваем TypeORM: разбор кейса
Из нашей практики: на проекте с PostgreSQL и нагрузкой 10 000 запросов в минуту мы столкнулись с падением производительности из-за N+1 при загрузке связанных тегов. Решение: использовали Query Builder с leftJoinAndSelect и пагинацию через skip/take. Дополнительно настроили пул соединений с max: 20 и idleTimeoutMillis: 30000, что снизило время ответа на 40%. Использование Query Builder позволило выполнить сложный запрос в 3 раза быстрее по сравнению с загрузкой через relations. Правильная настройка пула соединений сэкономила клиенту около 150 000 рублей в год на инфраструктуре. Закажите настройку TypeORM — получите консультацию инженера в течение часа.
// db/data-source.ts
import 'reflect-metadata'
import { DataSource } from 'typeorm'
import { User } from './entities/User'
import { Post } from './entities/Post'
export const AppDataSource = new DataSource({
type: 'postgres',
url: process.env.DATABASE_URL,
entities: [User, Post],
migrations: ['dist/db/migrations/*.js'],
migrationsTableName: 'migrations',
synchronize: false, // НИКОГДА true в production
logging: process.env.NODE_ENV === 'development' ? ['query', 'error'] : ['error'],
ssl: process.env.NODE_ENV === 'production' ? { rejectUnauthorized: false } : false,
extra: {
max: 20,
idleTimeoutMillis: 30000,
}
})
// Инициализация
await AppDataSource.initialize()
Что входит в настройку TypeORM?
Процесс работы проходит в пять этапов:
- Аналитика — изучаем текущую схему БД, нагрузку и профиль запросов.
- Проектирование — выбираем паттерн (Active Record или Data Mapper), проектируем сущности и связи.
- Реализация — настраиваем подключение, пишем сущности с декораторами, создаём миграции и репозитории.
- Тестирование — проверяем производительность, отсутствие N+1 и корректность миграций.
- Деплой и мониторинг — разворачиваем на production, настраиваем логирование и алерты.
В результат настройки входит:
- Конфигурация DataSource с пулом и SSL (под любую БД: PostgreSQL, MySQL, SQLite).
- Репозитории и сущности с индексами и связями.
- Миграции (генерация, запуск, откат).
- Query Builder для типовых запросов.
- Интеграция с NestJS (модуль, сервисы, контроллеры).
- Подписки (subscribers) для кэширования и уведомлений.
- Документация по API и архитектуре.
- Гарантия на 30 дней после сдачи — исправляем ошибки бесплатно.
Сравнение паттернов и выбор сущности
| Характеристика | Active Record | Data Mapper |
|---|---|---|
| Разделение логики | Данные и поведение вместе | Данные в сущности, поведение в репозиториях |
| Сложность | Низкая, подходит для простых CRUD | Высокая, требует больше кода |
| Тестирование | Сложнее из-за прямых обращений к БД | Легче благодаря репозиториям |
| Гибкость | Ограниченная | Высокая, легко менять запросы |
| Популярность | В небольших проектах | В крупных enterprise-решениях |
Наши инженеры всегда рекомендуют Data Mapper для проектов с бизнес-логикой, так как он даёт больше контроля и упрощает тестирование. Но если вам нужно быстрое прототипирование — Active Record тоже рабочий вариант. Правильный выбор сущности критичен для производительности. Используйте @PrimaryGeneratedColumn('uuid') для распределённых систем, @Index() для часто фильтруемых полей. Репозиторий — это прослойка между бизнес-логикой и БД. Оборачивайте в репозиторий все запросы к сущности: это делает код переиспользуемым и тестируемым.
| Декоратор | Назначение |
|---|---|
@PrimaryGeneratedColumn |
Автоинкрементный UUID или integer |
@Column |
Простое поле с типом и опциями |
@Index |
Индекс для ускорения запросов |
@ManyToOne |
Связь «многие к одному» |
@OneToMany |
Обратная сторона связи |
Пример репозитория с пагинацией:
const postRepository = AppDataSource.getRepository(Post)
async function findPosts(opts: { page: number; limit: number; search?: string }) {
const { page, limit, search } = opts
const qb = postRepository.createQueryBuilder('post')
.leftJoinAndSelect('post.author', 'author')
.leftJoinAndSelect('post.tags', 'tag')
.where('post.published = :published', { published: true })
.orderBy('post.createdAt', 'DESC')
.skip((page - 1) * limit)
.take(limit)
if (search) {
qb.andWhere(
'post.title ILIKE :search OR post.content ILIKE :search',
{ search: `%${search}%` }
)
}
const [items, total] = await qb.getManyAndCount()
return { items, total, pages: Math.ceil(total / limit) }
}
// Сложные агрегаты
const stats = await AppDataSource.query(`
SELECT
date_trunc('week', created_at) AS week,
count(*) AS posts,
count(*) FILTER (WHERE published = true) AS published
FROM posts
WHERE created_at >= now() - interval '90 days'
GROUP BY 1
ORDER BY 1
`)
Почему миграции важны для продакшена?
Согласно официальной документации TypeORM, миграции — единственный безопасный способ управления схемой в production. Миграции позволяют версионировать изменения схемы базы данных и применять их последовательно. TypeORM умеет автоматически генерировать миграции на основе изменений в сущностях. Это экономит до 50% времени на разработку миграций по сравнению с ручным написанием SQL. Пример генерации и применения:
# Генерация миграции из diff схемы
npx typeorm migration:generate -n AddUserProfile -d dist/db/data-source.js
# Создать пустую миграцию вручную
npx typeorm migration:create -n AddIndexes
# Применить
npx typeorm migration:run -d dist/db/data-source.js
# Откатить последнюю
npx typeorm migration:revert -d dist/db/data-source.js
// db/migrations/1234567890-AddUserProfile.ts
import { MigrationInterface, QueryRunner } from 'typeorm'
export class AddUserProfile1234567890 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`
CREATE TABLE profiles (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL UNIQUE REFERENCES users(id) ON DELETE CASCADE,
bio TEXT,
avatar_url VARCHAR(500),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
)
`)
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`DROP TABLE profiles`)
}
}
Интеграция с NestJS и подписки на события
В NestJS настройка TypeORM сводится к импорту TypeOrmModule с конфигурацией. Дополнительно можно внедрять репозитории через декоратор @InjectRepository. Subscribers (подписки) позволяют реагировать на изменения сущностей: после вставки обновить поисковый индекс, после публикации — отправить уведомление. Это мощный инструмент, если не злоупотреблять. Пример настройки NestJS:
// app.module.ts
import { TypeOrmModule } from '@nestjs/typeorm'
@Module({
imports: [
TypeOrmModule.forRootAsync({
inject: [ConfigService],
useFactory: (config: ConfigService) => ({
type: 'postgres',
url: config.get('DATABASE_URL'),
entities: [__dirname + '/**/*.entity{.ts,.js}'],
migrations: [__dirname + '/db/migrations/*{.ts,.js}'],
migrationsRun: true,
synchronize: false,
})
}),
TypeOrmModule.forFeature([User, Post])
]
})
export class AppModule {}
Типичные ошибки и как их избежать
- Synchronize в production — никогда. Используйте миграции.
- Отсутствие индексов — добавляйте
@Index()на поля, по которым часто фильтруете. - Игнорирование пула соединений — настройте
extra.maxиidleTimeoutMillis. - N+1 запросы — используйте
relationsили Query Builder сleftJoinAndSelect. - Хранение паролей в сущности — используйте
@Column({ select: false })и хешируйте в@BeforeInsert.
Ориентировочные сроки и стоимость
Базовая настройка TypeORM с сущностями, миграциями и репозиториями: 1–2 дня. Интеграция в NestJS-проект с модулями и тестами: 2–3 дня. Перевод существующего проекта с другого ORM на TypeORM: 3–5 дней. Стоимость базовой конфигурации TypeORM начинается от 35 000 рублей и зависит от сложности проекта. Свяжитесь с нами для оценки — это бесплатно и займёт не более часа.







