TypeORMのTransformer機能でマスターデータを扱う
TypeORMのTransformer機能を使って、データベースの数値IDとアプリケーションのマスターオブジェクトを相互変換する実装パターンを紹介します。
#typescript #typeorm #database
課題
データベースには数値IDで保存されているマスターデータ(都道府県、ステータスなど)を、アプリケーション層ではIDと名前を持つオブジェクトとして扱いたい場合があります。
TypeORMのTransformer機能を使うことで、この変換を自動化できます。
実装例:都道府県マスター
1. 汎用的なMasterクラスを定義
IDと名前を持つマスターデータを管理する基底クラスを作成します。
export interface IMaster {
id: number
name: string
}
export class Master {
private readonly masters: IMaster[]
constructor(masters: IMaster[]) {
this.masters = masters
}
findById(id: number): IMaster | undefined {
return this.masters.find((v) => v.id === Number(id))
}
findByName(name: string): IMaster | undefined {
return this.masters.find((v) => v.name === name)
}
}
2. 都道府県マスターを実装
import { IMaster, Master } from './master'
const PREFECTURES: IMaster[] = [
{ id: 1, name: '北海道' },
{ id: 2, name: '青森県' },
{ id: 3, name: '岩手県' },
// ...
{ id: 47, name: '沖縄県' },
]
class PrefectureMaster extends Master {
constructor() {
super(PREFECTURES)
}
}
export const Prefecture = new PrefectureMaster()
3. EntityでTransformerを使用
transformerオプションを指定することで、データベースとアプリケーション間の変換を自動化できます。
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm'
import { Prefecture } from './prefecture.master'
import { IMaster } from './master'
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number
@Column('varchar')
name: string
@Column('tinyint', {
unsigned: true,
comment: '都道府県ID',
transformer: {
// DB → アプリケーション: IDからIMasterオブジェクトへ
from(id: number): IMaster | undefined {
return Prefecture.findById(id)
},
// アプリケーション → DB: IMasterオブジェクトからIDへ
to(value: IMaster): number {
return value.id
},
},
})
prefecture: IMaster
}
使用例
データの取得
const user = await userRepository.findOne({ where: { id: 1 } })
console.log(user.prefecture)
// { id: 13, name: '東京都' }
データの保存
const newUser = userRepository.create({
name: '山田太郎',
prefecture: { id: 13, name: '東京都' },
})
await userRepository.save(newUser)
// DBには prefecture = 13 として保存される
Transformerのメリット
1. 型安全性
IDを直接扱うのではなく、オブジェクトとして扱うことで型安全性が向上します。
// ❌ IDだけでは何のIDか不明
user.prefecture_id = 13
// ✅ オブジェクトなので明確
user.prefecture = { id: 13, name: '東京都' }
2. コードの可読性
マスターデータの名前に直接アクセスできます。
// ❌ 別途マスターを参照する必要がある
const prefectureName = Prefecture.findById(user.prefecture_id)?.name
// ✅ 直接アクセス可能
const prefectureName = user.prefecture.name
3. ビジネスロジックの簡素化
データの変換処理がEntity層で完結するため、ビジネスロジックがシンプルになります。
注意点
Null/Undefinedの扱い
データベースでNULL許可する場合は、Transformerでもそれを考慮する必要があります。
@Column('tinyint', {
nullable: true,
transformer: {
from(id: number | null): IMaster | null {
return id ? Prefecture.findById(id) ?? null : null
},
to(value: IMaster | null): number | null {
return value?.id ?? null
},
},
})
prefecture: IMaster | null
クエリビルダーでの使用
クエリビルダーを使う場合は、変換が自動で行われないため注意が必要です。
// IDで検索する必要がある
const users = await userRepository
.createQueryBuilder('user')
.where('user.prefecture = :prefectureId', { prefectureId: 13 })
.getMany()
まとめ
TypeORMのTransformer機能を使うことで:
- データベースとアプリケーション間のデータ変換を自動化
- 型安全性とコードの可読性が向上
- マスターデータの扱いがシンプルに
マスターデータを多く扱うアプリケーションで特に有効な機能です。