Prismaの所感
Prismaはクラウドサービスだと思っていましたが、Next-generation ORMとのこと。 もともとそこまで興味はなかったのですが、Discordで話題に出たので私もやってみたいと思いました。 公式サイトのドキュメントのGetting startedのQuickstartとSet up Prismaを一通り終えたばかりなのですが、とても直感的だったのでブログにも残そうと思いました。
Prismaの特徴
Node.jsでSQLを扱う選択肢でいえばKnex.js、最近ではObjection.jsなどがありました。
Knex.jsはクエリビルダーでしばらく愛用していました。これ単体でも十分に使いやすいのですが、特に複雑なリレーションシップやjoin
配列をオブジェクトに変換するのが若干厄介です。
Objection.jsはKnex.jsの使いにくい部分を改良してこちらも同様に使いやすいのですが、このライブラリを使い始めたときにTypeScriptがまだ使えなかったこともありあまり使い込んでいません。
Knex.jsはクエリビルダーですがmigrationの機能も持ち合わせているのでこれを利用してデータベースを構築していくことになります。 OAuth2の認証サーバーをMongoDBで実装していて思ったのはSQLのようにmigrationがないのが良くも悪くもこれまでと異なります。 テーブルの変更に毎回migrationを実行する必要がないのは良いのですが、既存のデータを変換したいときはやはりmigrationがあったらいいのにと思っていました。
PrismaはSQLなのでmigrationは必要なのですが、prisma introspect
コマンドを実行するとスキーマが生成されます。
まるで魔法のようですが、このスキーマをもとにprisma generate
を実行すると型ファイルも定義されるのでエディタの補完機能を使うこともできます。
このアプローチは今までになかったのでNext-generationなのかも。
既存のプロジェクトを置き換えてみる
このブログの初期に取り組んでいたRailsでEleventyという静的サイトジェネレーターを使うためのプロジェクトを作っていました。 現在はRailsの運用をやめて単純なJekyllに戻しているのでしばらく放置されていたのですが、題材としては良さそうなので再び取り組んでみることにしました。
まずはプロジェクトをcloneしてrails db:structure:dump
を実行してstructure.sql
ファイルを生成しました。
現時点ではこのファイルを使うことはなかったのですが、念の為残しておくことにします。
Dockerで書いてあったので、rails db:setup
実行後にPostgresのコンテナ以外は消しました。
docker-compose down --remove-orphans
を実行するとリンクが切れたコンテナを消してくれるので便利ですね。
続いてちょっともったいない気もしますがRailsはもう使わないのでdb
ディレクトリ以外はすべて消してしまいました。
チュートリアルにかかれている通りデータベースの接続をします。
SCHEMA
は普段意識することがないのですが、ここは?schema=public
のままで問題ありませんでした。
先程書き出したSQLファイルに書かれているのでやはり書き出しておいてよかったのかも。
prisma introspect
を実行するとschema.prisma
ファイルが生成されます。
VSCodeでエクステンションが用意されているのでシンタックスハイライトできるのが有り難いですね。
model articles {
id Int @id @default(autoincrement())
slug String? @unique
title String?
description String?
body String?
favorites_count Int? @default(0)
user_id Int
created_at DateTime
updated_at DateTime
date DateTime?
users users @relation(fields: [user_id], references: [id])
@@index([user_id], name: "index_articles_on_user_id")
}
articles
テーブルを抜粋してみました。
これを以下のように書き換えます:
model Article {
id Int @id @default(autoincrement())
slug String? @unique
title String?
description String?
body String?
favoritesCount Int? @default(0) @map("favorites_count")
userId Int @map("user_id")
createdAt DateTime @map("created_at")
updatedAt DateTime @map("updated_at")
date DateTime?
user User @relation(fields: [userId], references: [id])
@@index([userId], name: "index_articles_on_user_id")
@@map("articles")
}
ここでのポイントは@map()
と@@map()
です。
ドキュメントにも書かれていましたが、Railsはsnake_case
でテーブルが定義されているのでcamelCase
に変換します。
この書き換えが最も時間がかかりましたが、新規で用意したプロジェクトならこの書き換え作業は少なくて済みそうですね。
const prisma = new PrismaClient()
async function main() {
const articles = await prisma.article.findMany({
include: { user: true },
})
console.log(articles)
}
main()
.catch((e) => {
throw e
})
.finally(async () => {
await prisma.$disconnect()
})
あとは実際にプログラムを動かしてみました。 本当にこれだけでいいのかと思ってしまうほど簡潔なコードです。 KnexではKnexfileで定義していましたが、環境変数にすることでDBの接続すら書かなくていいのかと思うと感動です。
チュートリアルでは単純なCRUDしか見ていませんが、複数のモデルの作成もとても簡潔で直感的です。 極端なことを言えば言語の指定がなければ今後はすべてPrismaでいいんじゃないかと思えるほど。 こういったライブラリが登場するとWebアプリケーションを作るのに生のSQLを書いていたことが一種の語りぐさになっていくでしょうね。