При разработке на 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 рублей и зависит от сложности проекта. Свяжитесь с нами для оценки — это бесплатно и займёт не более часа.







