Prismaに触れる機会があったので紹介
リポジトリ
スライド
Prismaとは?
- Node.js製のORM
- RDB周りの処理を簡易に扱えるようにする
- Schemaファイルから型情報を自動生成
- 以下のDB対応をサポート
- PostgreSQL、MySQL、SQL Server、SQLite、MongoDB
試してみたモチベーション
CRUD系のAPI開発は、ほぼ単純作業的に実装していることが多いので、Prismaみたいな自動生成してくる系のORMで実装すると、どのくらい工数が削減できるのか知りたかったので調査してみた。
サンプルを作ってみる
以下のチュートリアルをベースに、どんな感じで実装するのか試してみる。
CLIツールで初期設定
以下のコマンドを実行することで初期設定を行なう。
$ npx prisma init
以下のファイルが自動生成される。
. ├── .env └── prisma └── schema.prisma
DBの接続設定
.envファイルにDBの接続情報を設定
■ .env
DATABASE_URL="postgresql://johndoe:randompassword@localhost:5432/mydb?schema=public"
DBのテーブル設定
PrismaではSchemaファイルを修正することでマイグレーションファイルを生成して実行していく。
■ prisma/schema.prisma
model Post { id Int @default(autoincrement()) @id createdAt DateTime @default(now()) updatedAt DateTime @updatedAt title String @db.VarChar(255) content String? published Boolean @default(false) author User @relation(fields: [authorId], references: [id]) authorId Int } model User { id Int @default(autoincrement()) @id email String @unique name String? posts Post[] }
上記のファイルを作成後に以下のコマンドを実行する。
$ npx prisma migrate dev --name init
Schemaファイルを元に以下のSQLファイルを生成する
■ prisma/migrations/20220321025430_init/migration.sql
-- CreateTable CREATE TABLE "Post" ( "id" SERIAL NOT NULL, "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, "updatedAt" TIMESTAMP(3) NOT NULL, "title" VARCHAR(255) NOT NULL, "content" TEXT, "published" BOOLEAN NOT NULL DEFAULT false, "authorId" INTEGER NOT NULL, CONSTRAINT "Post_pkey" PRIMARY KEY ("id") ); -- CreateTable CREATE TABLE "User" ( "id" SERIAL NOT NULL, "email" TEXT NOT NULL, "name" TEXT, CONSTRAINT "User_pkey" PRIMARY KEY ("id") ); -- CreateIndex CREATE UNIQUE INDEX "User_email_key" ON "User"("email"); -- AddForeignKey ALTER TABLE "Post" ADD CONSTRAINT "Post_authorId_fkey" FOREIGN KEY ("authorId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
テーブル作成から外部キーの生成まで作成されたSQLファイルが生成され、上記のファイルを元にマイグレーションが実行される。
Prisma Clientからデータを取得
以下のファイルを作成することでユーザーデータを取得可能。
import {PrismaClient} from "@prisma/client"; const prisma = new PrismaClient() async function main() { const allUsers = await prisma.user.findMany() console.log(allUsers) } main() .catch((e) => { throw e }) .finally(async () => { await prisma.$disconnect() })
以下のコードの部分がデータの取得部分になる。
const allUsers = await prisma.user.findMany()
その他のDBアクセスは以下のような書き方で実装できる。
条件に一致するデータを1件抽出
const user = await prisma.user.findUnique({ where: { id:1, } })
条件に一致するデータ抽出( id >= 20 )
const users = await prisma.user.findMany({ where: { AND :{ id: { gte: 20 } } } })
リレーションのSQLの発行を含める/含めない
const users = await prisma.user.findMany({ include: { posts: false, }, })
sort & selectの指定
const users = await prisma.user.findMany({ select: { email: true, }, orderBy: [ { name: 'desc', }, ], })
Transaction & Rollbackの設定
async function transfer(from: string, to: string, amount: number) { return await prisma.$transaction(async (prisma) => { const sender = await prisma.account.update({ data: { balance: { decrement: amount } }, where: { email: from }, }) if (sender.balance < 0) { throw new Error(`${from} doesn't have enough to send ${amount}`) } const recipient = prisma.account.update({ data: { balance: { increment: amount }, }, where: { email: to }, }) return recipient }) }
その他の設定
SQLの発行のログを見たい
以下のようにクライアントにオプションを設定する。
const prisma = new PrismaClient({ log: ["query"], })
実行すると以下のログが出力される。
prisma:query BEGIN prisma:query INSERT INTO "User" ("email","name") VALUES ($1,$2) RETURNING "User"."id" prisma:query INSERT INTO "Post" ("createdAt","updatedAt","title","published","authorId") VALUES ($1,$2,$3,$4,$5) RETURNING "Post"."id" prisma:query SELECT "User"."id", "User"."email", "User"."name" FROM "User" WHERE "User"."id" = $1 LIMIT $2 OFFSET $3 prisma:query COMMIT prisma:query SELECT "User"."id", "User"."email", "User"."name" FROM "User" WHERE 1=1 OFFSET $1 prisma:query SELECT "Post"."id", "Post"."createdAt", "Post"."updatedAt", "Post"."title", "Post"."content", "Post"."published", "Post"."authorId" FROM "Post" WHERE "Post"."authorId" IN ($1) OFFSET $2
Prisma Studio
以下のコマンドを実行するとDBの状態をブラウザからGUIで確認/操作ができる。
$ npx prisma studio
各DBシステムでサポートしているので、お手軽にDBの中身を操作したい際に使用する。
GraphQLとの親和性
GraphQLとの相性は良さそう。 実際の親和性の話しに関しては以下の参考記事を参照。
まとめ
調査した感じ結構良い気がしたので、どこかで使ってみようかと思います。
ただNode.jsをサーバーで使うと、async/awaitやPromiseを多く記載する必要が合ったりして個人的には余り好きではないのでDenoとかで使えたら良いなあと思ったりした。