При розробці на Node.js і TypeScript багато хто стикається з однією і тією ж болем: ручне написання SQL-запитів і синхронізація схеми з кодом. Помилка в SQL-запиті — і production лежить. TypeORM вирішує цю проблему, але його налаштування вимагає врахування десятка нюансів: від конфігурації підключення до генерації міграцій. Ми допоможемо вам налаштувати TypeORM під ваш проєкт — від простого сервера до складного NestJS-застосунку. Наші інженери мають понад 10 років досвіду роботи з реляційними базами даних і гарантують стабільність вашої схеми.
Чому стандартне налаштування TypeORM часто призводить до проблем?
N+1 запит TypeORM — класична проблема при роботі з 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 грн і залежить від складності проєкту. Зв'яжіться з нами для оцінки — це безкоштовно і займе не більше години.
TypeORM на GitHub
TypeORM має понад 30 000 зірок на GitHub та понад 400 контриб'юторів, що свідчить про його популярність і активний розвиток.







