TypeORM для JavaScript (Node.js): полное руководство
1. Что это и зачем нужна
TypeORM — это мощная Object-Relational Mapping (ORM) библиотека, предназначенная для работы с базами данных в среде Node.js. Она поддерживает как TypeScript, так и JavaScript, предоставляя разработчикам удобный способ взаимодействия с реляционными базами данных (PostgreSQL, MySQL, MariaDB, SQLite, Microsoft SQL Server, Oracle и CockroachDB) через объектно-ориентированный подход. Вместо написания сырых SQL-запросов, вы работаете с сущностями (Entity) — обычными классами JavaScript, которые автоматически маппятся на таблицы базы данных.
Зачем нужна TypeORM? Во-первых, она значительно ускоряет разработку: вам не нужно вручную писать SQL-запросы для CRUD-операций (Create, Read, Update, Delete). Во-вторых, она обеспечивает безопасность типов (если вы используете TypeScript) и защиту от SQL-инъекций благодаря использованию параметризованных запросов. В-третьих, TypeORM предоставляет мощный Query Builder для сложных запросов, миграции для управления схемой базы данных, а также поддержку отношений между таблицами (один-к-одному, один-ко-многим, многие-ко-многим). Это делает её идеальным выбором для построения масштабируемых и поддерживаемых приложений.
В отличие от некоторых других ORM, TypeORM активно использует декораторы (в TypeScript) или аннотации (в JavaScript через экспериментальные возможности), что делает код декларативным и интуитивно понятным. Библиотека также поддерживает Active Record и Data Mapper паттерны, позволяя разработчику выбрать наиболее подходящий стиль работы. Благодаря своей гибкости и богатому функционалу, TypeORM является одной из самых популярных ORM в экосистеме Node.js.
2. Установка
Для установки TypeORM в ваш проект Node.js используйте менеджер пакетов npm. Вам также потребуется установить драйвер для вашей базы данных. Ниже приведены команды для наиболее популярных СУБД.
# Установка TypeORM
npm install typeorm --save
# Установка драйвера для PostgreSQL
npm install pg --save
# Установка драйвера для MySQL / MariaDB
npm install mysql2 --save
# Установка драйвера для SQLite
npm install sqlite3 --save
# Установка драйвера для Microsoft SQL Server
npm install mssql --save
# Установка драйвера для CockroachDB
npm install pg --save
# Для TypeScript также установите reflect-metadata и типы
npm install reflect-metadata --save
npm install @types/node --save-dev
После установки, убедитесь, что в вашем tsconfig.json (если используете TypeScript) включены следующие настройки:
{
"emitDecoratorMetadata": true,
"experimentalDecorators": true
}
Для JavaScript (Node.js) эти настройки не требуются, но вы можете использовать экспериментальные декораторы, если хотите.
3. Быстрый старт — минимальный рабочий пример
Давайте создадим простое приложение, которое подключается к базе данных SQLite (файловая БД, не требует установки сервера) и выполняет базовые операции.
Создайте файл index.js и добавьте следующий код:
const { createConnection, EntitySchema } = require("typeorm");
// Определяем сущность (Entity) через EntitySchema
const UserSchema = new EntitySchema({
name: "User",
tableName: "users",
columns: {
id: {
primary: true,
type: "int",
generated: true
},
firstName: {
type: "varchar"
},
lastName: {
type: "varchar"
},
age: {
type: "int"
}
}
});
async function main() {
// Создаем подключение к базе данных
const connection = await createConnection({
type: "sqlite",
database: "test.db",
synchronize: true, // Автоматически создает таблицы (только для разработки!)
logging: false,
entities: [UserSchema]
});
console.log("Подключение к базе данных установлено.");
// Получаем репозиторий для работы с сущностью User
const userRepository = connection.getRepository(UserSchema);
// Создаем нового пользователя
const newUser = userRepository.create({
firstName: "Иван",
lastName: "Петров",
age: 30
});
// Сохраняем пользователя в базу данных
await userRepository.save(newUser);
console.log("Пользователь сохранен:", newUser);
// Находим всех пользователей
const allUsers = await userRepository.find();
console.log("Все пользователи:", allUsers);
// Закрываем подключение
await connection.close();
console.log("Подключение закрыто.");
}
main().catch(error => console.error("Ошибка:", error));
Запустите скрипт командой node index.js. В текущей директории будет создан файл test.db (база данных SQLite) и таблица users с одной записью.
4. Основные методы/функции/классы
Ниже приведены 8 ключевых методов и классов TypeORM, которые используются в повседневной разработке. Для каждого метода указана сигнатура, описание и пример кода на JavaScript.
4.1. createConnection()
Сигнатура: createConnection(options?: ConnectionOptions | string): Promise<Connection>
Что делает: Создает и возвращает подключение к базе данных. Принимает объект конфигурации или имя заранее определенного подключения.
const connection = await createConnection({
type: "mysql",
host: "localhost",
port: 3306,
username: "root",
password: "password",
database: "test",
entities: [__dirname + "/entities/*.js"],
synchronize: true
});
console.log("Подключено к MySQL");
4.2. getRepository()
Сигнатура: connection.getRepository(target: EntityTarget<Entity>): Repository<Entity>
Что делает: Возвращает репозиторий для работы с определенной сущностью. Репозиторий предоставляет методы для CRUD-операций.
const userRepository = connection.getRepository(User);
4.3. repository.find()
Сигнатура: find(options?: FindManyOptions<Entity>): Promise<Entity[]>
Что делает: Находит все записи, соответствующие переданным условиям. Если условия не указаны, возвращает все записи из таблицы.
// Найти всех пользователей старше 25 лет
const users = await userRepository.find({
where: { age: MoreThan(25) }
});
console.log(users);
4.4. repository.findOne()
Сигнатура: findOne(id: string | number | Date | ObjectID, options?: FindOneOptions<Entity>): Promise<Entity | undefined>
Что делает: Находит одну запись по первичному ключу или по переданным условиям. Возвращает undefined, если запись не найдена.
// Найти пользователя с id = 1
const user = await userRepository.findOne(1);
console.log(user);
// Найти пользователя по имени
const userByName = await userRepository.findOne({
where: { firstName: "Иван" }
});
console.log(userByName);
4.5. repository.save()
Сигнатура: save(entity: Entity | Entity[], options?: SaveOptions): Promise<Entity | Entity[]>
Что делает: Сохраняет одну или несколько сущностей в базу данных. Если сущность новая (не имеет id), выполняется INSERT. Если сущность уже существует (имеет id), выполняется UPDATE.
// Создание нового пользователя
const newUser = new User();
newUser.firstName = "Петр";
newUser.lastName = "Сидоров";
newUser.age = 28;
await userRepository.save(newUser);
// Обновление существующего пользователя
const existingUser = await userRepository.findOne(1);
if (existingUser) {
existingUser.age = 31;
await userRepository.save(existingUser);
}
4.6. repository.remove()
Сигнатура: remove(entity: Entity | Entity[], options?: RemoveOptions): Promise<Entity | Entity[]>
Что делает: Удаляет одну или несколько сущностей из базы данных. Сущности должны быть предварительно загружены (иметь id).
// Удалить пользователя с id = 1
const userToRemove = await userRepository.findOne(1);
if (userToRemove) {
await userRepository.remove(userToRemove);
console.log("Пользователь удален");
}
4.7. createQueryBuilder()
Сигнатура: repository.createQueryBuilder(alias?: string): SelectQueryBuilder<Entity>
Что делает: Создает Query Builder для построения сложных SQL-запросов с использованием цепочки методов. Позволяет выполнять JOIN, подзапросы, агрегации и другие операции.
// Найти всех пользователей с возрастом > 18, отсортированных по фамилии
const users = await userRepository.createQueryBuilder("user")
.where("user.age > :age", { age: 18 })
.orderBy("user.lastName", "ASC")
.getMany();
console.log(users);
4.8. getManager()
Сигнатура: connection.getManager(): EntityManager
Что делает: Возвращает EntityManager — центральный объект для управления всеми сущностями. Он предоставляет методы, аналогичные репозиторию, но работает на уровне менеджера, а не конкретной сущности.
const entityManager = connection.getManager();
// Использование EntityManager для поиска
const users = await entityManager.find(User, {
where: { age: LessThan(30) }
});
console.log(users);
// Использование EntityManager для транзакции
await entityManager.transaction(async transactionalEntityManager => {
const user1 = await transactionalEntityManager.findOne(User, 1);
const user2 = await transactionalEntityManager.findOne(User, 2);
// Выполнение операций в рамках одной транзакции
});
5. Реальный практический пример (100-200 строк кода)
Рассмотрим реальный пример: создание простого REST API для управления списком задач (Todo List) с использованием Express.js и TypeORM. Мы будем использовать SQLite для простоты.
Сначала установите дополнительные зависимости:
npm install express body-parser --save
Создайте файл app.js со следующим содержимым:
const express = require('express');
const bodyParser = require('body-parser');
const { createConnection, EntitySchema } = require('typeorm');
// Определяем сущность Task
const TaskSchema = new EntitySchema({
name: "Task",
tableName: "tasks",
columns: {
id: {
primary: true,
type: "int",
generated: true
},
title: {
type: "varchar"
},
description: {
type: "text",
nullable: true
},
isCompleted: {
type: "boolean",
default: false
},
createdAt: {
type: "datetime",
createDate: true
}
}
});
const app = express();
app.use(bodyParser.json());
let taskRepository;
// Инициализация подключения и запуск сервера
async function start() {
const connection = await createConnection({
type: "sqlite",
database: "todo.db",
synchronize: true,
logging: false,
entities: [TaskSchema]
});
taskRepository = connection.getRepository(TaskSchema);
console.log("База данных подключена");
// Маршруты API
// GET /tasks - получить все задачи
app.get('/tasks', async (req, res) => {
try {
const tasks = await taskRepository.find();
res.json(tasks);
} catch (error) {
res.status(500).json({ error: error.message });
}
});
// GET /tasks/:id - получить задачу по ID
app.get('/tasks/:id', async (req, res) => {
try {
const task = await taskRepository.findOne(parseInt(req.params.id));
if (!task) {
return res.status(404).json({ error: "Задача не найдена" });
}
res.json(task);
} catch (error) {
res.status(500).json({ error: error.message });
}
});
// POST /tasks - создать новую задачу
app.post('/tasks', async (req, res) => {
try {
const { title, description } = req.body;
if (!title) {
return res.status(400).json({ error: "Поле title обязательно" });
}
const newTask = taskRepository.create({
title,
description: description || "",
isCompleted: false
});
const savedTask = await taskRepository.save(newTask);
res.status(201).json(savedTask);
} catch (error) {
res.status(500).json({ error: error.message });
}
});
// PUT /tasks/:id - обновить задачу
app.put('/tasks/:id', async (req, res) => {
try {
const taskId = parseInt(req.params.id);
const task = await taskRepository.findOne(taskId);
if (!task) {
return res.status(404).json({ error: "Задача не найдена" });
}
// Обновляем только переданные поля
if (req.body.title !== undefined) task.title = Также в библиотеке
Работа с изображениями: resize, crop на TypeScript
Blade для PHP: полное руководство с примерами
Валидация данных в PHP: Полное руководство по инструментам и библиотекам
Работа с ZIP-архивами в Go (Golang): создание, чтение и извлечение файлов
JSON обработка в C# (.NET): полное руководство с примерами