← NestJS コース
27. リポジトリでCRUD
リポジトリは、1つのエンティティに対する読み書き(CRUD)をまとめて担当するオブジェクトです。TypeOrmModule.forFeature([User]) でそのエンティティのリポジトリを使えるようにし、サービスのコンストラクタで @InjectRepository(User) を付けて注入します。これは既習の DI の一種で、自分で new せずフレームワークからリポジトリを受け取る点は通常のサービス注入と同じです。
実務では find()(全件)・findOne()(条件で1件)・save()(新規作成と更新の両方)・delete()(削除)といったメソッドを組み合わせて使います。コントローラには処理を書かず、リポジトリ操作はサービスに寄せるという既習の役割分担をそのまま踏襲します。メソッド名や細部は ORM ごとに違っても、「エンティティ専用の窓口を通して CRUD する」という構造は共通です。
つまずきポイントは save() の挙動です。id を持たないオブジェクトを渡すと新規作成(INSERT)、既存の id を持つオブジェクトを渡すと更新(UPDATE)になり、1つのメソッドが両方を兼ねます。また findOne() は該当なしのとき null を返すため、見つからない前提で 404 を返すなどの分岐を必ず入れます。
サンプルコード(フレームワーク環境が必要なため表示のみ)
import { Injectable, NotFoundException } from "@nestjs/common";
import { InjectRepository } from "@nestjs/typeorm";
import { Repository } from "typeorm";
import { User } from "./user.entity";
@Injectable()
export class UsersService {
// エンティティ専用のリポジトリを注入(DI の一種)
constructor(
@InjectRepository(User)
private readonly usersRepo: Repository<User>,
) {}
findAll(): Promise<User[]> {
return this.usersRepo.find(); // SELECT * FROM users
}
async findOne(id: number): Promise<User> {
const user = await this.usersRepo.findOne({ where: { id } });
if (!user) throw new NotFoundException(); // null なら 404
return user;
}
create(name: string, email: string): Promise<User> {
// id なしで渡すと INSERT、id ありなら UPDATE になる
return this.usersRepo.save({ name, email });
}
}
サンプルコードで身につける(5本) →
学んだ概念を、素朴な例から実務寄りの例まで5つの具体例で確認します。
公式ドキュメントで詳しく ↗