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

Часто задаваемые вопросы

Неофициальный Бета-перевод

Эта страница переведена PageTurner AI (бета). Не одобрена официально проектом. Нашли ошибку? Сообщить о проблеме →

Как обновить схему базы данных?

Одна из основных задач TypeORM — поддерживать синхронизацию таблиц базы данных с вашими сущностями. Для этого существует два подхода:

  • Используйте synchronize: true в настройках источника данных:

    import { DataSource } from "typeorm"

    const myDataSource = new DataSource({
    // ...
    synchronize: true,
    })

    Эта опция автоматически синхронизирует таблицы базы данных с указанными сущностями при каждом запуске кода. Идеально подходит для разработки, но в продакшене её рекомендуется отключать.

  • Используйте CLI-инструменты для ручной синхронизации схемы через командную строку:

    typeorm schema:sync

    Эта команда выполняет синхронизацию схемы.

Синхронизация схемы работает очень быстро. Если вы планируете отключать опцию synchronize в разработке из-за проблем с производительностью — сначала проверьте её скорость.

Как изменить имя столбца в базе данных?

По умолчанию имена столбцов генерируются из названий свойств сущности. Для изменения укажите опцию name в декораторе столбца:

@Column({ name: "is_active" })
isActive: boolean;

Как установить значение по умолчанию в виде функции (например, NOW())?

Опция default поддерживает функции. Если передать функцию, возвращающую строку, она будет использована как значение по умолчанию без экранирования. Пример:

@Column({ default: () => "NOW()" })
date: Date;

Как реализовать валидацию?

Валидация не входит в TypeORM, так как это отдельный процесс, не связанный напрямую с его задачами. Для валидации используйте class-validator — он идеально интегрируется с TypeORM.

Что означает "сторона владельца" в отношениях и зачем нужны @JoinColumn/@JoinTable?

Рассмотрим отношение one-to-one на примере сущностей User и Photo:

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

@Column()
name: string

@OneToOne()
photo: Photo
}
@Entity()
export class Photo {
@PrimaryGeneratedColumn()
id: number

@Column()
url: string

@OneToOne()
user: User
}

В этом примере отсутствует @JoinColumn, что неверно. Почему? Потому что для создания реальной связи необходимо добавить столбец в базу данных: userId в таблице photo или photoId в user. Но какой столбец следует создать - userId или photoId? TypeORM не может принять решение за вас. Чтобы принять решение, необходимо использовать @JoinColumn на одной из сторон.

  • @JoinColumn в Photo → создаст столбец userId в таблице photo
  • @JoinColumn в User → создаст столбец photoId в таблице user

Сторона с @JoinColumn называется "владельцем отношения" (owner side), противоположная сторона отношения, без @JoinColumn, — "инверсной (не владеющей) стороной" (inverse/non-owner side).

Аналогично для отношений @ManyToMany: @JoinTable указывает на владеющую сторону.

В отношениях @ManyToOne/@OneToMany декоратор @JoinColumn не обязателен — таблица с @ManyToOne автоматически получает связующий столбец.

Декораторы @JoinColumn и @JoinTable также позволяют настраивать дополнительные параметры: имя связующего столбца, название junction-таблицы и т.д.

Как добавить дополнительные столбцы в таблицу связи "многие-ко-многим"?

Невозможно добавить кастомные столбцы в автоматически генерируемую junction-таблицу. Решение:

  1. Создайте отдельную сущность-связку
  2. Свяжите её с целевыми сущностями через два отношения @ManyToOne
  3. Добавьте нужные столбцы в сущность-связку

Результат будет аналогичен связи "многие-ко-многим". Подробнее в разделе Отношения "многие-ко-многим".

Как работать с опцией компилятора TypeScript outDir?

При использовании опции компилятора outDir не забывайте копировать ресурсы и ассеты вашего приложения в выходную директорию. В противном случае убедитесь, что пути к этим ресурсам настроены корректно.

Важно понимать, что при удалении или перемещении сущностей старые файлы остаются в выходной директории. Например, вы создали сущность Post, затем переименовали её в Blog — файл Post.ts исчез из проекта, но Post.js останется в выходной директории. Когда TypeORM будет читать сущности из этой директории, он обнаружит обе сущности — Post и Blog, что может привести к ошибкам. Поэтому при изменении сущностей с включённой опцией outDir настоятельно рекомендуется удалять выходную директорию и перекомпилировать проект заново.

Как использовать TypeORM с ts-node?

Чтобы избежать постоянной перекомпиляции файлов, используйте ts-node. При работе с ts-node укажите сущности с расширением ts в настройках источника данных:

{
entities: [__dirname + "/entities/**/*{.js,.ts}"],
subscribers: [__dirname + "/subscribers/**/*{.js,.ts}"]
}

Если вы компилируете JavaScript-файлы в ту же директорию, где находятся TypeScript-файлы, используйте опцию компилятора outDir, чтобы избежать этой проблемы.

Для запуска TypeORM через CLI ts-node используйте следующую команду:

npx typeorm-ts-node-commonjs schema:sync

Для проектов ESM используйте вместо этого следующую команду:

npx typeorm-ts-node-esm schema:sync

Как использовать Webpack для бэкенда?

Как использовать Webpack для бэкенда?

Webpack выдаёт предупреждения из-за отсутствующих require-выражений для всех драйверов, поддерживаемых TypeORM. Чтобы отключить предупреждения для неиспользуемых драйверов, измените конфигурационный файл Webpack.

const FilterWarningsPlugin = require('webpack-filter-warnings-plugin');

module.exports = {
...
plugins: [
// ignore the drivers you don't want. This is the complete list of all drivers -- remove the suppressions for drivers you want to use.
new FilterWarningsPlugin({
exclude: [/mongodb/, /mssql/, /mysql2/, /oracledb/, /pg/, /pg-native/, /pg-query-stream/, /react-native-sqlite-storage/, /redis/, /sqlite3/, /sql.js/, /typeorm-aurora-data-api-driver/]
})
]
};

Бандлинг файлов миграций

По умолчанию Webpack собирает всё в один файл. Это может вызвать проблемы при наличии файлов миграций, которые должны выполняться после деплоя собранного кода в production. Чтобы TypeORM мог распознать и выполнить все миграции, используйте "Object Syntax" для настройки entry исключительно для файлов миграций.

const { globSync } = require("node:fs")
const path = require("node:path")

module.exports = {
// ... your webpack configurations here...
// Dynamically generate a `{ [name]: sourceFileName }` map for the `entry` option
// change `src/db/migrations` to the relative path to your migrations folder
entry: globSync(path.resolve("src/db/migrations/*.ts")).reduce(
(entries, filename) => {
const migrationName = path.basename(filename, ".ts")
return Object.assign({}, entries, {
[migrationName]: filename,
})
},
{},
),
resolve: {
// assuming all your migration files are written in TypeScript
extensions: [".ts"],
},
output: {
// change `path` to where you want to put transpiled migration files.
path: __dirname + "/dist/db/migrations",
// this is important - we want UMD (Universal Module Definition) for migration files.
libraryTarget: "umd",
filename: "[name].js",
},
}

Кроме того, начиная с Webpack 4, при mode: 'production' файлы по умолчанию оптимизируются — включая искажение имён (mangling) для минимизации размера. Это нарушает работу миграций, поскольку TypeORM идентифицирует их по именам. Для отключения минимизации полностью добавьте:

module.exports = {
// ... other Webpack configurations here
optimization: {
minimize: false,
},
}

Если вы используете UglifyJsPlugin, вы можете указать ему не изменять имена классов или функций следующим образом:

const UglifyJsPlugin = require("uglifyjs-webpack-plugin")

module.exports = {
// ... other Webpack configurations here
optimization: {
minimizer: [
new UglifyJsPlugin({
uglifyOptions: {
keep_classnames: true,
keep_fnames: true,
},
}),
],
},
}

Убедитесь, что в настройках источника данных указаны скомпилированные файлы миграций:

// TypeORM Configurations
module.exports = {
// ...
migrations: [__dirname + "/migrations/**/*{.js,.ts}"],
}

Как использовать TypeORM в ESM-проектах?

Добавьте "type": "module" в package.json вашего проекта, чтобы TypeORM использовал import( ... ) для загрузки файлов.

Для избежания проблем с циклическими зависимостями используйте тип-обёртку Relation для определений связей в сущностях:

@Entity()
export class User {
@OneToOne(() => Profile, (profile) => profile.user)
profile: Relation<Profile>
}

Это предотвращает сохранение типа свойства в транспилированном коде метаданных свойства, устраняя циклические зависимости.

Поскольку тип колонки уже определён через декоратор @OneToOne, дополнительная типовая информация от TypeScript не требуется.

Важно: Не используйте Relation для колонок, не являющихся связями