TypeORM для TypeScript: полное руководство с примерами

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Что такое TypeORM и зачем она нужна?

TypeORM — это мощная объектно-реляционная библиотека (ORM) для TypeScript и JavaScript, которая работает с различными базами данных: PostgreSQL, MySQL, MariaDB, SQLite, MS SQL Server, Oracle и MongoDB (частично). Она предоставляет удобный способ взаимодействия с реляционными базами данных через объектно-ориентированный подход, позволяя разработчикам работать с таблицами как с классами, а с записями — как с объектами.

Основные причины использовать TypeORM:

  • Типизация: Полная интеграция с TypeScript обеспечивает строгую типизацию и автодополнение в IDE.
  • Декларативный подход: Определение схемы базы данных через декораторы или схемы.
  • Автомиграции: Автоматическое создание и синхронизация схемы базы данных.
  • Активная запись (Active Record) и Data Mapper: Поддержка обоих популярных паттернов.
  • Поддержка связей: One-to-One, One-to-Many, Many-to-Many.
  • Query Builder: Гибкий построитель запросов для сложных SQL-запросов.
  • Кроссплатформенность: Работает с Node.js, браузерами, React Native и Ionic.

Установка TypeORM

Для установки TypeORM и необходимых драйверов базы данных используйте npm (или yarn):

// Установка TypeORM
npm install typeorm --save

// Установка reflect-metadata (обязательно для декораторов)
npm install reflect-metadata --save

// Установка драйвера для PostgreSQL
npm install pg --save

// Установка драйвера для MySQL/MariaDB
npm install mysql2 --save

// Установка драйвера для SQLite
npm install sqlite3 --save

// Установка драйвера для SQL Server
npm install mssql --save

// Установка драйвера для MongoDB (экспериментально)
npm install mongodb --save

После установки добавьте в tsconfig.json:

{
  "compilerOptions": {
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true
  }
}

Быстрый старт с примером кода

Создадим простое приложение для управления пользователями с использованием SQLite (не требует установки сервера).

1. Создайте файл data-source.ts для конфигурации подключения:

import { DataSource } from "typeorm";
import { User } from "./entity/User";

export const AppDataSource = new DataSource({
    type: "sqlite",
    database: "database.sqlite",
    synchronize: true,
    logging: true,
    entities: [User],
    subscribers: [],
    migrations: [],
});

// Инициализация подключения
AppDataSource.initialize()
    .then(() => {
        console.log("Data Source has been initialized!");
    })
    .catch((err) => {
        console.error("Error during Data Source initialization:", err);
    });

2. Создайте сущность User в файле entity/User.ts:

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

@Entity()
export class User {
    @PrimaryGeneratedColumn()
    id: number;

    @Column()
    firstName: string;

    @Column()
    lastName: string;

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

    @Column({ default: true })
    isActive: boolean;
}

3. Создайте файл index.ts для демонстрации работы:

import "reflect-metadata";
import { AppDataSource } from "./data-source";
import { User } from "./entity/User";

async function main() {
    await AppDataSource.initialize();

    // Создаем нового пользователя
    const user = new User();
    user.firstName = "Иван";
    user.lastName = "Петров";
    user.email = "ivan@example.com";
    user.isActive = true;

    // Сохраняем в базу данных
    await AppDataSource.manager.save(user);
    console.log("Пользователь сохранен с ID:", user.id);

    // Получаем всех пользователей
    const users = await AppDataSource.manager.find(User);
    console.log("Все пользователи:", users);

    // Получаем пользователя по ID
    const foundUser = await AppDataSource.manager.findOneBy(User, { id: 1 });
    console.log("Найденный пользователь:", foundUser);

    // Обновляем пользователя
    if (foundUser) {
        foundUser.lastName = "Сидоров";
        await AppDataSource.manager.save(foundUser);
        console.log("Пользователь обновлен");
    }

    // Удаляем пользователя
    await AppDataSource.manager.delete(User, { id: 1 });
    console.log("Пользователь удален");

    await AppDataSource.destroy();
}

main().catch(console.error);

4. Запустите приложение:

npx ts-node index.ts

Основные методы и функции TypeORM

1. DataSource.initialize()

Что делает: Инициализирует подключение к базе данных. Должен быть вызван перед любыми операциями с БД.

Параметры: Нет (использует конфигурацию из DataSource).

Пример:

const dataSource = new DataSource({
    type: "postgres",
    host: "localhost",
    port: 5432,
    username: "test",
    password: "test",
    database: "test",
});

await dataSource.initialize();
console.log("Подключение установлено");

2. EntityManager.save(entity)

Что делает: Сохраняет сущность в базу данных. Если сущность новая — создает запись, если существующая — обновляет.

Параметры:

  • entity — экземпляр сущности или массив сущностей
  • options (опционально) — объект с настройками

Пример:

const user = new User();
user.firstName = "Анна";
user.email = "anna@example.com";

await dataSource.manager.save(user);

// Сохранение нескольких сущностей
const users = [user1, user2, user3];
await dataSource.manager.save(users);

3. EntityManager.find(entityClass, options?)

Что делает: Находит все записи, соответствующие условиям.

Параметры:

  • entityClass — класс сущности
  • options (опционально) — объект с условиями поиска

Пример:

// Все пользователи
const allUsers = await dataSource.manager.find(User);

// С условиями
const activeUsers = await dataSource.manager.find(User, {
    where: { isActive: true },
    order: { firstName: "ASC" },
    take: 10,
    skip: 0,
});

// С выборкой полей
const partialUsers = await dataSource.manager.find(User, {
    select: ["id", "email"],
});

4. EntityManager.findOneBy(entityClass, where)

Что делает: Находит одну запись по простым условиям.

Параметры:

  • entityClass — класс сущности
  • where — объект с условиями

Пример:

const user = await dataSource.manager.findOneBy(User, {
    id: 1,
});

const userByEmail = await dataSource.manager.findOneBy(User, {
    email: "test@example.com",
});

5. EntityManager.delete(entityClass, criteria)

Что делает: Удаляет записи, соответствующие критериям.

Параметры:

  • entityClass — класс сущности
  • criteria — условия удаления (ID, объект или массив)

Пример:

// Удаление по ID
await dataSource.manager.delete(User, 1);

// Удаление по условию
await dataSource.manager.delete(User, { isActive: false });

// Удаление нескольких записей
await dataSource.manager.delete(User, [1, 2, 3]);

6. EntityManager.update(entityClass, criteria, partialEntity)

Что делает: Обновляет записи без полной загрузки сущности.

Параметры:

  • entityClass — класс сущности
  • criteria — условия обновления
  • partialEntity — частичные данные для обновления

Пример:

await dataSource.manager.update(User, 1, {
    lastName: "НоваяФамилия",
    isActive: false,
});

// Обновление по условию
await dataSource.manager.update(
    User,
    { isActive: true },
    { isActive: false }
);

7. EntityManager.count(entityClass, options?)

Что делает: Возвращает количество записей, соответствующих условиям.

Параметры:

  • entityClass — класс сущности
  • options (опционально) — объект с условиями

Пример:

const totalUsers = await dataSource.manager.count(User);

const activeCount = await dataSource.manager.count(User, {
    where: { isActive: true },
});

8. EntityManager.createQueryBuilder(entityClass, alias)

Что делает: Создает QueryBuilder для построения сложных SQL-запросов.

Параметры:

  • entityClass — класс сущности
  • alias — псевдоним для таблицы

Пример:

const users = await dataSource
    .manager
    .createQueryBuilder(User, "user")
    .leftJoinAndSelect("user.posts", "post")
    .where("user.isActive = :isActive", { isActive: true })
    .andWhere("user.createdAt > :date", { date: new Date("2023-01-01") })
    .orderBy("user.firstName", "ASC")
    .addOrderBy("post.title", "DESC")
    .take(20)
    .skip(0)
    .getMany();

9. Repository.find(options?)

Что делает: Работает аналогично EntityManager.find, но через репозиторий сущности.

Параметры: Те же, что и у EntityManager.find.

Пример:

const userRepository = dataSource.getRepository(User);

const users = await userRepository.find({
    relations: ["posts"],
    where: { isActive: true },
});

const user = await userRepository.findOne({
    where: { id: 1 },
    relations: ["profile"],
});

10. DataSource.transaction(callback)

Что делает: Выполняет операции в транзакции с возможностью отката.

Параметры:

  • callback — асинхронная функция, принимающая EntityManager

Пример:

await dataSource.transaction(async (manager) => {
    const user = await manager.findOneBy(User, { id: 1 });
    
    if (!user) {
        throw new Error("Пользователь не найден");
    }
    
    user.balance -= 100;
    await manager.save(user);
    
    const order = new Order();
    order.userId = user.id;
    order.amount = 100;
    await manager.save(order);
});

Реальный практический пример использования

Создадим систему управления заказами для интернет-магазина с использованием TypeORM и PostgreSQL.

Сущности:

// entity/Customer.ts
import { Entity, PrimaryGeneratedColumn, Column, OneToMany } from "typeorm";
import { Order } from "./Order";

@Entity()
export class Customer {                    

Рекомендаций