NestJS und TypeORM 2026: Migrationen, Relationen und Interviewfragen
NestJS TypeORM Integration mit TypeORM 1.1 Migrationen, Entity-Relationen, Repository-Patterns und häufigen Interviewfragen für Backend-Entwickler.

NestJS TypeORM bleibt eine der bewährtesten ORM-Integrationen für Enterprise-Node.js-Backends. Mit TypeORM Version 1.0 im Mai 2026, gefolgt von der 1.1.x-Patch-Serie, die Verbindungshandling und Relationspersistenz verbessert, sowie der abgeschlossenen ESM-Migration in NestJS 12, bietet der Stack eine stabile Grundlage für Produktionsanwendungen. Dieser Artikel behandelt die wesentlichen Patterns für Backend-Entwickler: Migrationen, Entity-Relationen, das Repository-Pattern und die zugehörigen Interviewfragen.
NestJS 12 liefert Core-Pakete als ESM und erfordert Node.js 20.19+ oder 22.12+. Das Paket @nestjs/typeorm v12 folgt diesem Ansatz. TypeORM 1.1.2 behebt Verbindungslecks in DataSource.initialize() und korrigiert die Persistenz von Many-to-Many-Junction-Rows, wenn Relationen nicht geladen sind.
TypeORM-Migrationen in NestJS 12 einrichten
Die erste Regel für Produktionsdatenbanken: niemals synchronize: true verwenden. Auto-Sync vergleicht Entity-Metadaten mit dem Live-Schema und wendet Änderungen direkt an, was bei Spaltenentfernungen, Typänderungen oder umbenannten Feldern zu Datenverlust führen kann. Migrationen bieten eine versionierte, überprüfbare und reversible Alternative.
TypeORM 1.x erfordert eine dedizierte DataSource-Konfigurationsdatei für die CLI, getrennt von der NestJS-Modulkonfiguration. Die CLI kann NestJS Dependency Injection nicht auflösen und benötigt daher einen eigenständigen Einstiegspunkt.
import { DataSource } from 'typeorm';
import { config } from 'dotenv';
config(); // Load .env variables
export default new DataSource({
type: 'postgres',
host: process.env.DB_HOST,
port: parseInt(process.env.DB_PORT || '5432'),
username: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME,
entities: ['src/**/*.entity.ts'],
migrations: ['src/migrations/*.ts'],
synchronize: false,
});Die NestJS-Modulkonfiguration spiegelt diese Einstellungen wider, verwendet jedoch das TypeOrmModule.forRootAsync-Pattern mit Dependency Injection:
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { ConfigModule, ConfigService } from '@nestjs/config';
@Module({
imports: [
ConfigModule.forRoot({ isGlobal: true }),
TypeOrmModule.forRootAsync({
inject: [ConfigService],
useFactory: (config: ConfigService) => ({
type: 'postgres',
host: config.get('DB_HOST'),
port: config.getOrThrow<number>('DB_PORT'),
username: config.get('DB_USER'),
password: config.get('DB_PASSWORD'),
database: config.get('DB_NAME'),
autoLoadEntities: true, // Auto-register entities from feature modules
synchronize: false, // Always false in production
}),
}),
],
})
export class AppModule {}autoLoadEntities: true ersetzt das manuelle entities-Array durch automatische Registrierung jeder Entity, die über TypeOrmModule.forFeature() in Feature-Modulen importiert wird.
Migrations-Workflow: Generieren, Prüfen, Ausführen
TypeORM 1.x liefert typeorm-ts-node-commonjs als empfohlenen CLI-Runner, der den älteren ts-node ./node_modules/typeorm/cli-Ansatz ersetzt. Diese Scripts sollten in package.json hinzugefügt werden:
{
"migration:generate": "typeorm-ts-node-commonjs migration:generate src/migrations/$npm_config_name -d src/config/typeorm.config.ts",
"migration:run": "typeorm-ts-node-commonjs migration:run -d src/config/typeorm.config.ts",
"migration:revert": "typeorm-ts-node-commonjs migration:revert -d src/config/typeorm.config.ts"
}Der Generate-Befehl vergleicht Entity-Metadaten mit der Live-Datenbank und erzeugt SQL-Statements. Die Ausgabe sollte vor dem Commit immer geprüft werden. TypeORM erkennt Spaltenhinzufügungen, -entfernungen, Typänderungen, Index-Modifikationen und Fremdschlüsseländerungen, übersieht jedoch Daten-Backfills, Tabellenumbenennungen (die als Drop gefolgt von Create erscheinen) und alles, was Datenbankerweiterungen oder Trigger betrifft.
Eine umbenannte Spalte erzeugt ein DROP + ADD statt eines ALTER RENAME. Dies vernichtet vorhandene Daten. Die SQL-Anweisungen in der up()-Methode sollten immer geprüft und bei Bedarf manuell angepasst werden.
Entity-Relationen: OneToMany, ManyToOne, ManyToMany
TypeORM Entity-Relationen werden über Decorators direkt auf SQL-Fremdschlüssel abgebildet. Das Verständnis der drei Kern-Relationstypen und ihrer Optionen ist grundlegend für jedes NestJS-Datenbankmodul.
Ein User mit mehreren Order-Datensätzen zeigt das häufigste Pattern:
import { Entity, PrimaryGeneratedColumn, Column, OneToMany, CreateDateColumn } from 'typeorm';
import { Order } from './order.entity';
@Entity('users')
export class User {
@PrimaryGeneratedColumn('uuid')
id: string;
@Column({ unique: true })
email: string;
@Column()
name: string;
@OneToMany(() => Order, (order) => order.user)
orders: Order[]; // No DB column created here; relation lives on the Order side
@CreateDateColumn()
createdAt: Date;
}import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, JoinColumn } from 'typeorm';
import { User } from './user.entity';
@Entity('orders')
export class Order {
@PrimaryGeneratedColumn('uuid')
id: string;
@Column('decimal', { precision: 10, scale: 2 })
total: number;
@Column({ default: 'pending' })
status: string;
@ManyToOne(() => User, (user) => user.orders, { onDelete: 'CASCADE' })
@JoinColumn({ name: 'user_id' }) // Explicit FK column name
user: User;
}Die @ManyToOne-Seite besitzt den Fremdschlüssel. @JoinColumn ist bei @ManyToOne optional (TypeORM leitet es ab), aber explizite Benennung vermeidet Überraschungen bei Raw-Queries oder Migrations-Diffs.
Für Many-to-Many-Relationen erstellt TypeORM automatisch eine Junction-Tabelle:
import { Entity, PrimaryGeneratedColumn, Column, ManyToMany, JoinTable } from 'typeorm';
import { Category } from './category.entity';
@Entity('products')
export class Product {
@PrimaryGeneratedColumn('uuid')
id: string;
@Column()
name: string;
@Column('decimal', { precision: 10, scale: 2 })
price: number;
@ManyToMany(() => Category, (category) => category.products)
@JoinTable({ name: 'product_categories' }) // Owns the junction table
categories: Category[];
}@JoinTable muss auf genau einer Seite der Relation erscheinen. Es definiert, welche Entity die Junction-Tabelle besitzt und kontrolliert deren Namen.
Bereit für deine Node.js / NestJS-Interviews?
Übe mit unseren interaktiven Simulatoren, Flashcards und technischen Tests.
Repository-Pattern und Custom Repositories
Das NestJS-Modul- und DI-System integriert TypeORM-Repositories über TypeOrmModule.forFeature(). Die Injektion des Standard-Repository<T> deckt Standard-CRUD-Operationen ab. Bei komplexen Queries kapseln Custom Repositories diese Logik.
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Order } from '../entities/order.entity';
import { OrdersService } from './orders.service';
import { OrdersController } from './orders.controller';
@Module({
imports: [TypeOrmModule.forFeature([Order])],
providers: [OrdersService],
controllers: [OrdersController],
})
export class OrdersModule {}import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Order } from '../entities/order.entity';
@Injectable()
export class OrdersService {
constructor(
@InjectRepository(Order)
private readonly orderRepo: Repository<Order>,
) {}
async findByUser(userId: string): Promise<Order[]> {
return this.orderRepo.find({
where: { user: { id: userId } },
relations: ['user'], // Eager-load the user relation
order: { createdAt: 'DESC' }, // Most recent first
});
}
async findWithFilters(status: string, minTotal: number): Promise<Order[]> {
// QueryBuilder for complex queries
return this.orderRepo
.createQueryBuilder('order')
.leftJoinAndSelect('order.user', 'user')
.where('order.status = :status', { status })
.andWhere('order.total >= :minTotal', { minTotal })
.orderBy('order.total', 'DESC')
.getMany();
}
}Die find()-API behandelt unkomplizierte Queries. Der QueryBuilder behandelt Joins, Subqueries, Aggregationen und alles, was feingranulare SQL-Kontrolle erfordert. Beides im selben Service zu mischen ist vollkommen gültig: find() für einfache Lookups und QueryBuilder wenn die Query nicht über die Options-API ausgedrückt werden kann.
Transaktionsmanagement für Datenintegrität
Operationen, die mehrere Tabellen modifizieren, benötigen Transaktionen. TypeORM bietet zwei Ansätze: die DataSource.transaction()-Methode und die QueryRunner-API.
async createOrderWithItems(
userId: string,
items: { productId: string; quantity: number }[],
dataSource: DataSource,
): Promise<Order> {
return dataSource.transaction(async (manager) => {
// All operations use the transactional manager
const order = manager.create(Order, {
user: { id: userId },
total: 0,
status: 'pending',
});
const savedOrder = await manager.save(order);
let total = 0;
for (const item of items) {
const product = await manager.findOneByOrFail(Product, { id: item.productId });
total += product.price * item.quantity;
await manager.save(OrderItem, {
order: savedOrder,
product,
quantity: item.quantity,
unitPrice: product.price,
});
}
savedOrder.total = total;
return manager.save(savedOrder);
});
}Die Callback-basierte transaction()-Methode committet automatisch bei Erfolg und rollt bei jedem geworfenen Fehler zurück. Jede Datenbankoperation innerhalb des Callbacks muss den bereitgestellten manager anstelle des injizierten Repositorys verwenden, andernfalls laufen diese Operationen außerhalb der Transaktion.
DataSource.transaction() eignet sich für unkomplizierte Multi-Step-Operationen. Der QueryRunner bietet manuelle Kontrolle über Commit/Rollback-Punkte und Savepoints, etwa bei partiellen Rollbacks in Batch-Verarbeitungen.
Häufige NestJS TypeORM Interviewfragen
Diese Fragen erscheinen regelmäßig in Backend-Entwickler-Interviews. Jede Antwort zielt auf die Tiefe, die in einer technischen Diskussion auf Senior-Level erwartet wird.
Warum sollte synchronize: true niemals in Produktion verwendet werden?
Auto-Sync wendet destruktive Schemaänderungen ohne Bestätigung an. Das Entfernen einer Spalte, das Ändern eines Typs oder das Entfernen eines Index geschieht sofort auf Live-Daten. Migrationen bieten Versionskontrolle, Code-Review und Rollback-Fähigkeit. Der einzig sichere Einsatz von synchronize ist lokale Entwicklung mit Wegwerfdaten.
Was ist der Unterschied zwischen eager und lazy Relationen?
Eager-Relationen werden automatisch mit jeder Query auf die Parent-Entity geladen. Lazy-Relationen geben ein Promise zurück und führen eine separate Query nur beim Zugriff aus. Eager Loading verursacht N+1-ähnlichen Overhead, wenn die verwandten Daten nicht benötigt werden. Lazy Loading verzögert die Kosten, kann aber unerwartete Queries tief im Call-Stack auslösen. Die explizite relations-Option in find() oder leftJoinAndSelect im QueryBuilder gibt volle Kontrolle darüber, was pro Query geladen wird.
Wie funktioniert cascade bei TypeORM-Relationen?
Das Setzen von cascade: true auf einer Relation bedeutet, dass das Speichern der Parent-Entity auch nicht gespeicherte Child-Entities persistiert. cascade: ['insert', 'update'] beschränkt das Verhalten auf bestimmte Operationen. Cascades operieren auf ORM-Ebene, nicht auf Datenbankebene. Für kaskadierende Löschungen auf Datenbankebene wird onDelete: 'CASCADE' auf dem @ManyToOne-Decorator verwendet, der einen ON DELETE CASCADE-Constraint in der Migration erzeugt.
Wann sollte ein Custom Repository anstelle des Standard-Repository<T> verwendet werden?
Custom Repositories kapseln komplexe Query-Logik, die nicht in Services gehört. Anzeichen für die Extraktion eines Custom Repositorys: dieselbe QueryBuilder-Kette erscheint in mehreren Services, die Query beinhaltet mehrere Joins oder Subqueries, oder die Query erfordert Raw SQL. Einfache find()-Aufrufe rechtfertigen kein Custom Repository.
Für weitere NestJS-Architekturfragen, siehe den vollständigen NestJS Guards und Interceptors Guide.
Bereit für deine Node.js / NestJS-Interviews?
Übe mit unseren interaktiven Simulatoren, Flashcards und technischen Tests.
Sources
- TypeORM 1.0.0 Release - ECMAScript 2023-Anforderung, Connection-Entfernung, striktere Validierungsstandards
- TypeORM 1.1.2 Release - Behebung von Verbindungslecks in DataSource.initialize(), Many-to-Many Junction Row Fixes
- NestJS 12 Release - ESM-Migration, Node.js 20.19+/22.12+ Anforderung
- @nestjs/typeorm 12.0.0 - Native ES-Module, CommonJS-Kompatibilität über Node require(esm)
Wichtige Erkenntnisse für NestJS TypeORM Produktionsanwendungen
- Eine separate
DataSource-Konfigurationsdatei für die TypeORM-CLI verwenden, unabhängig von der NestJS-Modulkonfiguration - Niemals
synchronizein Produktion aktivieren; stattdessen auf generierte und geprüfte Migrationen für alle Schemaänderungen setzen @ManyToOneauf die Entity setzen, die den Fremdschlüssel besitzt, und explizite@JoinColumn-Benennung für Klarheit bei Raw-Queries und Migrations-Output verwenden- Zwischen
find()-Options-API für einfache Queries undQueryBuilderfür komplexe Joins, Aggregationen und bedingte Logik wählen - Multi-Tabellen-Mutationen in Transaktionen über
DataSource.transaction()wrappen und sicherstellen, dass jede Operation innerhalb des Callbacks den transaktionalenmanagerverwendet - Mit TypeORM 1.1.x, das die API-Oberfläche stabilisiert, und NestJS 12, das die ESM-Migration abschließt, bleibt dieser Stack eine starke Wahl für Enterprise-Backends, die strukturierten, typsicheren Datenbankzugriff erfordern
Fang an zu üben!
Teste dein Wissen mit unseren Interview-Simulatoren und technischen Tests.
Findest du den Bug in Node.js / NestJS?
Ein echter Codeausschnitt, ein versteckter Bug, ein Versuch pro Tag. Zum Ausprobieren ohne Konto.

Geschrieben von
Anthony Fillion-MailletGründer von SharpSkill
Seit über 10 Jahren Fullstack-Entwickler. Er leitet SharpSkill und verantwortet alles, was hier erscheint.
Aktualisiert am 21. Oktober 2026
Tags
Teilen
Verwandte Artikel

NestJS + Prisma: der moderne Backend-Stack für Node.js
Vollständiger Leitfaden zum Aufbau einer modernen Backend-API mit NestJS und Prisma. Setup, Modelle, Services, Transaktionen und Best Practices erklärt.

NestJS: Eine vollstaendige REST-API von Grund auf erstellen
Schritt-fuer-Schritt-Anleitung zum Erstellen einer produktionsreifen REST-API mit NestJS, TypeScript, Prisma und class-validator. CRUD, Validierung, Fehlerbehandlung und Interceptors.

Microservices mit NestJS in 2026: Architektur, gRPC und Interview-Fragen
Praktischer Guide zur NestJS Microservices-Architektur mit gRPC, einschließlich Service-Grenzen, Transport Layern, Streaming-Patterns und häufigen Interview-Fragen für 2026.