NestJS dan TypeORM di 2026: Migrasi, Relasi, dan Pertanyaan Wawancara
Kuasai integrasi NestJS TypeORM dengan migrasi TypeORM 1.1, relasi entitas, repository pattern, dan pertanyaan wawancara umum untuk backend developer.

NestJS TypeORM tetap menjadi salah satu integrasi ORM paling teruji untuk backend Node.js enterprise. Dengan TypeORM mencapai versi 1.0 pada Mei 2026, diikuti oleh seri patch 1.1.x yang memperbaiki penanganan koneksi dan persistensi relasi, serta NestJS 12 yang menyelesaikan migrasi ESM-nya, stack ini telah matang menjadi fondasi yang stabil untuk aplikasi produksi. Panduan ini membahas pola-pola esensial yang harus dikuasai setiap backend developer: migrasi, relasi entitas, repository pattern, dan pertanyaan wawancara yang menyertainya.
NestJS 12 menyediakan paket core sebagai ESM, memerlukan Node.js 20.19+ atau 22.12+. Paket @nestjs/typeorm v12 mengikuti hal yang sama. TypeORM 1.1.2 memperbaiki kebocoran koneksi pada DataSource.initialize() dan mengoreksi persistensi junction row many-to-many ketika relasi tidak dimuat.
Menyiapkan Migrasi TypeORM di NestJS 12
Aturan pertama untuk database produksi: jangan pernah menggunakan synchronize: true. Auto-sync membandingkan metadata entitas dengan skema live dan menerapkan perubahan secara langsung, yang berisiko kehilangan data pada penghapusan kolom, perubahan tipe, atau field yang diubah namanya. Migrasi menyediakan alternatif yang terversi, dapat ditinjau, dan dapat dibalik.
TypeORM 1.x memerlukan file konfigurasi DataSource khusus untuk CLI, terpisah dari konfigurasi modul NestJS. CLI tidak dapat menyelesaikan dependency injection NestJS, sehingga memerlukan entry point mandiri.
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,
});Konfigurasi modul NestJS mencerminkan pengaturan ini tetapi menggunakan pola TypeOrmModule.forRootAsync dengan 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 menggantikan array entities manual dengan secara otomatis mendaftarkan setiap entitas yang diimpor melalui TypeOrmModule.forFeature() di modul fitur.
Alur Kerja Migrasi: Generate, Review, Run
TypeORM 1.x menyediakan typeorm-ts-node-commonjs sebagai CLI runner yang direkomendasikan, menggantikan pendekatan lama ts-node ./node_modules/typeorm/cli. Tambahkan script berikut ke package.json:
{
"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"
}Perintah generate membandingkan metadata entitas dengan database live dan menghasilkan statement SQL. Selalu tinjau output sebelum commit. TypeORM mendeteksi penambahan kolom, penghapusan, perubahan tipe, modifikasi indeks, dan perubahan foreign key, tetapi tidak menangkap backfill data, rename tabel (yang muncul sebagai drop diikuti create), dan hal yang melibatkan ekstensi database atau trigger.
Kolom yang diubah namanya menghasilkan DROP + ADD, bukan ALTER RENAME. Ini menghancurkan data yang ada. Selalu periksa SQL di method up() dan sesuaikan secara manual jika diperlukan.
Relasi Entitas: OneToMany, ManyToOne, ManyToMany
Relasi entitas TypeORM memetakan langsung ke foreign key SQL menggunakan decorator. Memahami tiga tipe relasi inti dan opsinya merupakan hal fundamental untuk setiap modul database NestJS.
User dengan beberapa record Order mendemonstrasikan pola paling umum:
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;
}Sisi @ManyToOne memiliki foreign key. @JoinColumn bersifat opsional pada @ManyToOne (TypeORM menyimpulkannya), tetapi penamaan eksplisit menghindari kejutan ketika nama kolom penting untuk raw query atau diff migrasi.
Untuk relasi many-to-many, TypeORM membuat junction table secara otomatis:
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 harus muncul tepat di satu sisi relasi. Ini mendefinisikan entitas mana yang memiliki junction table dan mengontrol namanya.
Siap menguasai wawancara Node.js / NestJS Anda?
Berlatih dengan simulator interaktif, flashcards, dan tes teknis kami.
Repository Pattern dan Custom Repository
Sistem modul dan DI NestJS mengintegrasikan repository TypeORM melalui TypeOrmModule.forFeature(). Menginjeksi Repository<T> default mencakup operasi CRUD standar. Ketika query menjadi kompleks, custom repository mengenkapsulasi logika tersebut.
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();
}
}API find() menangani query sederhana. QueryBuilder menangani join, subquery, agregasi, dan apa pun yang memerlukan kontrol SQL yang lebih detail. Menggabungkan keduanya dalam service yang sama sepenuhnya valid: gunakan find() untuk lookup sederhana dan QueryBuilder ketika query tidak dapat diekspresikan melalui options API.
Manajemen Transaksi untuk Integritas Data
Operasi yang memodifikasi beberapa tabel memerlukan transaksi. TypeORM menawarkan dua pendekatan: method DataSource.transaction() dan API QueryRunner.
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);
});
}Method transaction() berbasis callback secara otomatis commit pada keberhasilan dan rollback pada error yang dilemparkan. Setiap operasi database di dalam callback harus menggunakan manager yang disediakan, bukan repository yang diinjeksi, karena operasi tersebut berjalan di luar transaksi.
Gunakan DataSource.transaction() untuk operasi multi-langkah yang sederhana. Beralih ke QueryRunner ketika kontrol manual atas commit/rollback point atau savepoint diperlukan, seperti partial rollback dalam batch processing.
Pertanyaan Wawancara NestJS TypeORM yang Umum
Pertanyaan-pertanyaan ini sering muncul dalam wawancara backend developer. Setiap jawaban menargetkan kedalaman yang diharapkan dalam diskusi teknis tingkat senior.
Mengapa synchronize: true tidak boleh digunakan di produksi?
Auto-sync menerapkan perubahan skema yang destruktif tanpa konfirmasi. Menghapus kolom, mengubah tipe, atau menghapus indeks terjadi langsung terhadap data live. Migrasi menyediakan version control, code review, dan kemampuan rollback. Satu-satunya penggunaan synchronize yang aman adalah pengembangan lokal dengan data yang dapat dibuang.
Apa perbedaan antara relasi eager dan lazy?
Relasi eager dimuat secara otomatis dengan setiap query pada entitas induk. Relasi lazy mengembalikan Promise dan mengeksekusi query terpisah hanya ketika diakses. Eager loading menyebabkan overhead gaya N+1 ketika data terkait tidak diperlukan. Lazy loading menunda biaya tetapi dapat memicu query tak terduga jauh di dalam call stack. Opsi relations eksplisit dalam find() atau leftJoinAndSelect dalam QueryBuilder memberikan kontrol penuh atas apa yang dimuat per query.
Bagaimana cascade bekerja dalam relasi TypeORM?
Mengatur cascade: true pada relasi berarti menyimpan entitas induk juga mempersist entitas anak yang belum disimpan. cascade: ['insert', 'update'] membatasi perilaku ke operasi tertentu. Cascade beroperasi di level ORM, bukan level database. Untuk cascading delete di level database, gunakan onDelete: 'CASCADE' pada decorator @ManyToOne, yang menghasilkan constraint ON DELETE CASCADE dalam migrasi.
Kapan custom repository harus digunakan daripada Repository<T> default?
Custom repository mengenkapsulasi logika query kompleks yang tidak termasuk dalam service. Tanda-tanda untuk mengekstrak custom repository: chain QueryBuilder yang sama muncul di beberapa service, query melibatkan beberapa join atau subquery, atau query memerlukan raw SQL. Panggilan find() sederhana tidak membenarkan custom repository.
Untuk pertanyaan arsitektur NestJS lainnya, lihat panduan lengkap guards dan interceptors NestJS.
Siap menguasai wawancara Node.js / NestJS Anda?
Berlatih dengan simulator interaktif, flashcards, dan tes teknis kami.
Sumber
- TypeORM 1.0.0 Release - Persyaratan ECMAScript 2023, penghapusan Connection, default validasi yang lebih ketat
- TypeORM 1.1.2 Release - Perbaikan kebocoran koneksi di DataSource.initialize(), perbaikan junction row many-to-many
- NestJS 12 Release - Migrasi ESM, persyaratan Node.js 20.19+/22.12+
- @nestjs/typeorm 12.0.0 - Native ES modules, kompatibilitas CommonJS via Node require(esm)
Poin Penting untuk Aplikasi NestJS TypeORM Produksi
- Gunakan file konfigurasi
DataSourceterpisah untuk CLI TypeORM, independen dari konfigurasi modul NestJS - Jangan pernah mengaktifkan
synchronizedi produksi; andalkan migrasi yang dihasilkan dan ditinjau untuk semua perubahan skema - Tempatkan
@ManyToOnepada entitas yang memiliki foreign key, dan gunakan penamaan@JoinColumneksplisit untuk kejelasan dalam raw query dan output migrasi - Pilih antara options API
find()untuk query sederhana danQueryBuilderuntuk join kompleks, agregasi, dan logika kondisional - Bungkus mutasi multi-tabel dalam transaksi menggunakan
DataSource.transaction(), dan pastikan setiap operasi di dalam callback menggunakanmanagertransaksional - Dengan TypeORM 1.1.x yang menstabilkan API surface dan NestJS 12 yang menyelesaikan migrasi ESM-nya, stack ini tetap menjadi pilihan kuat untuk backend enterprise yang memerlukan akses database yang terstruktur dan type-safe
Mulai berlatih!
Uji pengetahuan Anda dengan simulator wawancara dan tes teknis kami.
Bisakah kamu menemukan bug di Node.js / NestJS?
Satu potongan kode nyata, satu bug tersembunyi, satu percobaan per hari. Tanpa akun untuk mencoba.

Ditulis oleh
Anthony Fillion-MailletPendiri SharpSkill
Developer fullstack selama lebih dari 10 tahun. Ia menjalankan SharpSkill dan bertanggung jawab atas semua yang diterbitkan di sini.
Diperbarui 21 Oktober 2026
Tag
Bagikan
Artikel terkait

NestJS + Prisma: stack backend modern untuk Node.js
Panduan lengkap untuk membangun API backend modern dengan NestJS dan Prisma. Setup, model, service, transaksi, dan praktik terbaik dijelaskan.

NestJS: Membangun REST API Lengkap dari Nol
Panduan lengkap membangun REST API profesional dengan NestJS. Controller, Service, Module, validasi dengan class-validator, dan penanganan error dijelaskan secara praktis.

NestJS dan MongoDB di 2026: Mongoose, Agregasi, dan Pertanyaan Interview
Kuasai NestJS dengan MongoDB dan Mongoose 9. Pelajari desain schema, pipeline agregasi, dan persiapan interview teknis dengan contoh praktis.