TypeScriptのORMはどれが良いのか
TypeScriptでORMを選定するとき、候補になったのは以下の2つです。
一度はPrismを選択したけども、結果的にはTypeORMで開発し直しています。
Prisma
ここ最近勢いあって割と何でもできるORM。独自に定義するスキーマーファイルで、複数DBの接続やモデルの定義ができ、マイグレーションも差分管理してくれるし、コマンドもシンプル。言うことはありませんね。
ただサーバーレス環境からの利用は、Prisma Data Proxyの利用が前提になります。サーバーレス環境だと、短期的にインスタンスが立ち上がり、都度接続を生成しては破棄するといったことを非効率に行うことになります。性能のボトルネックがDB接続にならないためにも、コネクションプールをうまく活用して、クエリを捌いていくことが求められますが、Prismaクライアントを利用するときは、Eege用のクライアントの利用が必要です。ただそのときに求められるDBのURLがprisma://
スキーマーになるため、Data Proxyの利用が必須になります。
- @prisma/client
+ @prisma/client/edge
const prisma = new PrismaClient({
datasources: {
db: {
- url: "postgresql://postgres:postgres@localhost:5432/my-db?schema=public",
+ url: "prisma://<リージョン>.prisma-data.com/?api_key=<APIキー>"
}
}
})
コネクションプール関係の課題をも解決するプロダクトですが、以下の理由で選定を辞めました。
- 可能な限りロックインしない方針で作っている
- コールドスタートなので、数百ミリ秒(長いと数秒)かかる
- 現在リージョンが、ドイツ・フランクフルトとアメリカ・ウェストバージニアにしかないので、国内利用だとコールドスタート + 物理的距離の問題がある
- ローカルテスト環境構築が出来ない、はず(別途方法あり)
Alternative Prisma Data Proxy
試してはいませんが、このプロジェクトを活用すると、ローカル環境や日本ロケーション含めて自由にセルフホストできるようです。
公式のData Proxyよりも早い結果が出ているようです。
TypeORM
こちらも有名どころだと思いますが、こちらも割と何でもできる系のORMです。独自ファイルで定義するのではなく、モデルやマイグレーションはTypeScriptのコードで表現されます。
コネクションプールについては、同様にサーバーレス環境であれば懸念があるので、PostgreSQLであれば PgBouncer あたりを使うのがいいのではと思います。
ドキュメント他、色々情報がネット上にもあるので、Prisma同様に作っていく上で困ることはなさそうですが、ハマったところがあるので、いくつか紹介します。
typeormの実態
公式が作るプロジェクトのひな形やネット上の情報も、package.json
のscripts
にtypeorm
を作ってマイグレーションなどを行うようになっています。
"typeorm": "typeorm-ts-node-commonjs"
ただcommonjsではないESMなプロジェクトについては、typeorm-ts-node-esm
がありますので、指定に注意が必要です。
データソースに指定するエンティティ
typeorm init --name FirstProject --database mysql
を使うと、プロジェクトのひな形が生成されますが、データソースの定義は以下のようにTypeScriptで行います。
適切なデータベースのタイプ(MySQL等)や接続情報を追記する必要がありますが、ここでentities
に渡せるのは、インポートしたエンティティかエンティティが存在するパスのどちらかになります。このようにUser
をそのまま指定できる環境もあれば、エンティティのパスを指定しないと、エンティティが存在しないといった警告を受けて、マイグレーションデータが生成されないことがあります。
import "reflect-metadata"
import { DataSource } from "typeorm"
import { User } from "./entity/User"
export const AppDataSource = new DataSource({
synchronize: true,
logging: false,
entities: [User],
migrations: [],
subscribers: [],
})
私の場合は、パスの指定で生成できました。
entities: ["./db/entity/*.ts"],
最後に
ORMは他にもあるようなので、ニーズにあった選択が出来ればと思います。
以下沢山掲載ありました。
Discussion