TypeORM в NestJS — практическое руководство

О чём эта статья
В этой статье подробно описано, как подключить и использовать TypeORM в проекте на NestJS. Вы найдёте пошаговые инструкции по установке, созданию сущностей, подключению к базе данных (пример на SQLite), работе с репозиториями, внедрению зависимостей, а также рекомендации по безопасности, тестированию и переходу в продакшен.
Important: краткие определения
- ORM: библиотека, которая переводит объектную модель приложения в структуру реляционной базы данных.
- Сущность (Entity): класс, где описаны колонки таблицы.
- Репозиторий: слой доступа к данным для конкретной сущности.
Почему это важно
TypeORM позволяет писать запросы к БД в привычном объектно-ориентированном стиле. В NestJS это значит меньше шаблонного SQL-кода и более строгая типизация, если вы используете TypeScript.
Установка зависимостей
Перед началом создайте проект NestJS или откройте существующий. Для примера используем sqlite как лёгкий драйвер без настроек сервера.
Запустите установку TypeORM и интеграционного пакета NestJS:
npm install @nestjs/typeorm typeorm
Установите драйвер SQLite:
npm install sqlite3
Примечание: для других баз (Postgres, MySQL, MariaDB) меняется только драйвер (pg, mysql2 и т.д.) и значение type в конфигурации.
Шаг 1 — Создание сущности (Entity)
Сущность описывает структуру таблицы в коде. TypeORM использует декораторы для указания колонок и ключей.
Порядок действий:
- Создайте файл по соглашению NestJS:
.entity.ts. - Импортируйте декораторы Entity, Column, PrimaryGeneratedColumn из typeorm.
- Экспортируйте класс и добавьте поля (id, name и пр.).
- Пометьте класс декоратором @Entity().
- Пометьте первичный ключ декоратором @PrimaryGeneratedColumn().
- Остальные поля пометьте декоратором @Column().
Пример:
// src/test/test.entity.ts
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';
@Entity()
export class Test {
@PrimaryGeneratedColumn()
id: number;
@Column()
property_1: string;
@Column()
property_2: string;
@Column()
property_3: string;
} Эта сущность создаст таблицу с колонками:
| таблица: test | ||
|---|---|---|
| id | int(11) | PRIMARY KEY AUTO_INCREMENT |
| property_1 | varchar(255) | |
| property_2 | varchar(255) | |
| property_3 | varchar(255) |
TypeORM имеет множество опций у Column (nullable, unique, length, default и др.). Ознакомьтесь с документацией по колонкам при необходимости.
Шаг 2 — Подключение приложения к базе данных
В корневом модуле приложения (обычно src/app.module.ts) нужно импортировать TypeOrmModule и передать ему конфигурацию через forRoot. Это создаст глобальное подключение, доступное во всех модулях.
Рекомендуемая минимальная конфигурация для разработки (SQLite):
// src/app.module.ts
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Test } from './test/test.entity';
import { Entity2 } from './entity/entity.entity';
import { TestModule } from './test/test.module';
@Module({
imports: [
TypeOrmModule.forRoot({
type: 'sqlite',
database: 'test.db',
entities: [Test, Entity2],
synchronize: true, // development only
}),
TestModule,
],
controllers: [],
providers: [],
})
export class AppModule {} Ключевые параметры конфигурации:
- type: имя драйвера (‘sqlite’, ‘postgres’, ‘mysql’ и т.д.).
- database: путь/имя базы (для SQLite) или настройки хоста/порта/пользователя.
- entities: массив классов сущностей или glob-паттерн.
- synchronize: автоматически синхронизирует схему при запуске (только для разработки).
Важно: в продакшене не используйте synchronize: true, чтобы не потерять данные. Вместо этого используйте миграции.
Шаг 3 — Создание репозитория
Репозиторий — это слой доступа к данным. TypeORM автоматически создаёт репозиторий для каждой сущности после регистрации через forFeature в модуле.
Пример модуля сущности:
// src/test/test.module.ts
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { TestController } from './test.controller';
import { TestService } from './test.service';
import { Test } from './test.entity';
@Module({
imports: [TypeOrmModule.forFeature([Test])],
providers: [TestService],
controllers: [TestController],
}) forFeature регистрирует репозитории для указанных сущностей в текущем модуле.
Шаг 4 — Внедрение репозитория в сервис через Dependency Injection
В NestJS репозиторий внедряется в сервис с помощью декоратора InjectRepository и типа Repository из TypeORM.
Пример сервиса:
// test.service.ts
import { Injectable } from '@nestjs/common';
import { Repository } from 'typeorm';
import { InjectRepository } from '@nestjs/typeorm';
import { Test } from './test.entity';
@Injectable()
export class TestService {
constructor(
@InjectRepository(Test)
private repo: Repository,
) {}
} Теперь в методах TestService вы можете вызывать стандартные методы репозитория: find, findOne, save, remove, delete, update и т.д.
Выполнение запросов: методы репозитория и QueryBuilder
TypeORM предлагает простые методы репозитория для обычных CRUD операций и мощный QueryBuilder для сложных запросов и джойнов.
Примеры:
- repo.find({ where: { property_1: ‘value’ } })
- repo.findOne(id)
- repo.save(entity)
- repo.delete(id)
QueryBuilder пример:
const users = await repo.createQueryBuilder('t')
.where('t.property_1 = :val', { val: 'value' })
.orderBy('t.id', 'DESC')
.getMany();QueryBuilder полезен для динамических условий, сложных джойнов и пагинации.
Миграции и продакшен
В разработке удобно использовать synchronize: true. Однако для продакшен-среды используйте миграции:
- Отключите synchronize.
- Генерируйте миграции при изменении сущностей.
- Применяйте миграции в CI/CD или вручную на сервере.
TypeORM поддерживает команды CLI для создания и запуска миграций. Миграции дают контроль над изменениями схемы и минимизируют риск потери данных.
Безопасность и конфиденциальность
- Никогда не включайте synchronize: true в продакшен.
- Храните секреты подключения (пароли, хосты) в переменных окружения или в секретном хранилище (Vault, AWS Secrets Manager и т.д.).
- Для SQLite локальные файлы удобны для dev/test, но в продакшене используйте серверную СУБД (Postgres, MySQL).
- Ограничьте права доступа БД: приложение должно иметь только необходимые привилегии.
- Регулярно делайте бэкапы и проверяйте восстановление.
Тестирование
При модульном тестировании сервисов используйте заглушки (mocks) для репозиториев:
- Замокайте методы репозитория (find, save и т.д.).
- Для e2e тестов используйте отдельную тестовую базу или in-memory решение.
Пример теста с мок-репозиторием (псевдокод):
const mockRepo = { find: jest.fn().mockResolvedValue([]), save: jest.fn() };
const module = await Test.createTestingModule({
providers: [
TestService,
{ provide: getRepositoryToken(Test), useValue: mockRepo },
],
}).compile();Когда TypeORM не лучший выбор (контрпример)
- Проекты с исключительно сложными аналитическими запросами, где выгоднее писать оптимизированный SQL вручную.
- Микросервисы, где нужно очень лёгкое и быстрое хранилище без ORM-слоя (например, key-value store).
- Сценарии с высокими требованиями к масштабированию и кастомным SQL, где ORM генерирует неоптимальный код.
В таких случаях рассмотрите альтернативы: Prisma (типобезопасный генератор запросов), Sequelize, Knex (query builder) или чистый SQL.
Альтернативы и сравнительная таблица
| Инструмент | Подходит для | Плюсы | Минусы |
|---|---|---|---|
| TypeORM | Традиционные приложения NestJS с TypeScript | Декораторы, интеграция с NestJS, репозитории, QueryBuilder | Поддержка миграций требует внимания, иногда тонкие баги в генерации SQL |
| Prisma | Проекты с высокой типобезопасностью | Сильная типизация, удобный клиент | Не использует декораторы, миграции и подход отличаются |
| Sequelize | JS/TS, зрелая ORM | Широко используется, много драйверов | API менее «typescript-ориентирован» |
| Knex | Query builder | Полный контроль SQL | Сам по себе не ORM — нужен слой моделей |
Ментальные модели и эвристики
- Сущность = таблица, экземпляр = строка.
- Репозиторий = набор CRUD-операций над сущностью.
- synchronize: true = удобно, но опасно.
- Используйте QueryBuilder, когда стандартных методов недостаточно.
Роль‑ориентированные чеклисты
Разработчик:
- Создал Entity с нужными колонками.
- Протестировал репозиторий методом save/find.
- Не включил synchronize в production конфиг.
DevOps:
- Хранит секреты подключения в безопасном месте.
- Автоматизировал миграции в CI/CD.
- Настроил бэкапы и мониторинг БД.
QA:
- Проверил CRUD-операции и граничные условия.
- Выполнил тесты с реальными размерами данных.
- Проверил поведение при ошибках доступа к базе.
Критерии приёмки
- API-сервис корректно сохраняет и читает данные из БД.
- Миграции корректно применяются к тестовой/стейдж-среде.
- Производительность запросов соответствует SLA (если применимо).
- В продакшене synchronize выключен и используется только миграция.
Примеры сценариев и тесты приёмки
Тест-случай: создание нового Test
- Шаги: вызвать endpoint POST /tests с телом { property_1, property_2, property_3 }
- Ожидание: статус 201 и запись появляется в БД (repo.findOne возвращает запись)
Тест-случай: обновление записи
- Шаги: PATCH /tests/:id с изменёнными полями
- Ожидание: ответ 200 и значения изменены в БД
Руководство по миграциям — мини-методология
- В ветке фичи измените сущности.
- Сгенерируйте миграцию (CLI команды TypeORM или ручной скрипт).
- Просмотрите SQL миграции вручную.
- Создайте PR с миграцией.
- На этапе CI примените миграцию к тестовой БД и запустите тесты.
- Примените миграцию на staging, затем на production.
Отладка и типичные ошибки
- Проблема: сущности не обнаруживаются. Причина: entities не указаны в конфиге или путь/импорт неверны.
- Проблема: миграции не применяются. Причина: synchronize выключен и миграции не запускались вручную.
- Проблема: несоответствие типов/тайпстринги. Причина: несовпадение полей в сущности и в БД — проверьте миграции.
Decision flow — как выбрать TypeORM?
flowchart TD
A[Начать проект на NestJS] --> B{Планируется работать с SQL?}
B -- Да --> C{Нужна ли tight TypeScript-типизация клиентского API?}
C -- Да --> D[Рассмотреть Prisma]
C -- Нет --> E[TypeORM — хорошая опция]
B -- Нет --> F[Рассмотреть NoSQL/другие хранилища]Лучшие практики
- Разделяйте слои: контроллеры не должны содержать SQL.
- Используйте DTO и валидацию (class-validator) для входных данных.
- Пишите интеграционные тесты с реальной БД в CI.
- Логируйте ошибки доступа к БД и время выполнения тяжёлых запросов.
Notes: локальные альтернативы для России
- Для разработки часто используют SQLite или локальные контейнеры Postgres.
- При деплое в облако предпочтительнее управляемые Postgres/Cloud SQL решения.
Краткая галерея крайних случаев
- Edge-case: большой объём массовой вставки — репозиторий.save может быть медленен; используйте queryRunner и BULK INSERT.
- Edge-case: сложные агрегаты и аналитика — лучше выполнять на стороне БД через оптимизированный SQL.
Короткое объявление для команды (100–200 слов)
TypeORM интегрируется с NestJS и позволяет быстро перейти от сущностей к рабочей базе данных. Для локальной разработки удобно использовать SQLite и synchronize: true, но в продакшене используйте миграции и управляемую СУБД. В ближайшем спринте создайте сущности, настройте миграции и добавьте CI шаг для их применения на тестовом окружении.
Итог
TypeORM — надёжный инструмент для большинства приложений на NestJS. Он ускоряет разработку, обеспечивает связность модели и базы данных и интегрируется с инфраструктурой NestJS. Помните про запрет использования synchronize в production и продуманную стратегию миграций.
Summary:
- Настройте TypeOrmModule.forRoot в AppModule и используйте forFeature в модулях.
- Создавайте сущности с декораторами Entity/Column/PrimaryGeneratedColumn.
- Внедряйте репозитории с InjectRepository и используйте методы репозитория или QueryBuilder.
- Для продакшена используйте миграции, секреты в переменных окружения и бэкапы.
Похожие материалы
Nearby Sharing в Windows 10: быстрый обмен файлами
Медиа‑источник не отображается в OBS — как исправить
Поменять папку для скриншотов в One UI 5.1
Собрать мощный дешёвый ПК из серверных комплектующих
BitTorrent на Android: как скачивать и делиться