Перейти к основному содержимому

TypeORM

Разработчику Архитектору

Дальше: TypeScript и Node.js · Декораторы · Обработка ошибок


TypeORMORM (Object-Relational Mapping, отображение объектов на таблицы БД) для TypeScript/JavaScript: таблицы описывают классами-сущностями, запросы — через Repository и QueryBuilder. Типизация полей сущности проверяется TS; SQL и связи задаются декораторами или схемой Data API. Удобный порядок — TypeScript и Node.js, эта статья; нужны декораторы и experimentalDecorators в legacy-стеке.

Альтернативы: Prisma (schema-first), Drizzle (SQL-like types). TypeORM выбран как распространённый в NestJS-стеке.


Что даёт ORM

Без ORMС TypeORM
SQL строки вручнуюметоды репозитория
Расхождение полей и таблицыкласс Entity
Миграции отдельноCLI миграций (при настройке)

TS не заменяет миграции и индексы — схему БД проектируют осознанно.


Минимальная сущность


import { Entity, PrimaryGeneratedColumn, Column } from "typeorm";

@Entity("users")
export class User {
@PrimaryGeneratedColumn("uuid")
id!: string;

@Column({ unique: true })
email!: string;

@Column()
name!: string;
}

Разбор:

  • @Entity("users") — имя таблицы.
  • ! — поле инициализирует БД/ORM, без участия конструктора — Декораторы в TypeScript.

Инициализация DataSource

Код ITЗагрузка примера кода…

Разбор:

  • synchronize: true удобно в dev, опасно в prod — используйте миграции.
  • Секреты — из env — TypeScript и Node.js.

Repository

const repo = AppDataSource.getRepository(User);

async function createUser(email: string, name: string): Promise<User> {
const user = repo.create({ email, name });
return repo.save(user);
}

async function findUser(id: string): Promise<User | null> {
return repo.findOneBy({ id });
}

Разбор:


Связь one-to-many

Код ITЗагрузка примера кода…

Разбор:

  • () => User — ленивая ссылка на класс (циклический import).
  • Загрузка связей: relations: { posts: true } в find.

DTO и Entity

type CreateUserDto = { email: string; name: string };

async function register(dto: CreateUserDto): Promise<User> {
return createUser(dto.email, dto.name);
}

Не экспортируйте Entity напрямую в API-ответ — маппер toDto(user)TypeScript и Node.js, Паттерны в TypeScript.


Миграции

npx typeorm migration:generate -d src/data-source.ts src/migrations/AddUserIndex
npx typeorm migration:run -d src/data-source.ts

Разбор:

  • Миграция — версионированное изменение схемы.
  • Генерация сравнивает entities с БД — проверяйте SQL вручную.

Слой сервиса

export class UserService {
constructor(private readonly repo: Repository<User>) {}

async getOrThrow(id: string): Promise<User> {
const user = await this.repo.findOneBy({ id });
if (!user) throw new AppError(`User ${id}`, "NOT_FOUND");
return user;
}
}

Разделение: controllerservicerepositoryTypeScript и Node.js.


Частые ошибки

ОшибкаЧто делать
synchronize: true в prodмиграции
Entity в JSON ответеDTO
N+1 запросовrelations или join
Нет reflect-metadataimport в entry
any в QueryBuilderтипизировать параметры

Практика

  1. Поднимите Postgres (Docker) и сущность User.
  2. Добавьте Post с ManyToOne / OneToMany.
  3. Сгенерируйте и примените миграцию.
  4. Сервис getOrThrow с доменной ошибкой.
  5. Ответ API через DTO, без User entity.

Смежные статьи