wheatandcatの開発ブログ

React Nativeで開発しているペペロミア & memoirの技術系記事を投稿してます

Prismaを試してみた

Prismaに触れる機会があったので紹介

リポジトリ

github.com

スライド

speakerdeck.com

Prismaとは?

  • Node.js製のORM
  • RDB周りの処理を簡易に扱えるようにする
  • Schemaファイルから型情報を自動生成
  • 以下のDB対応をサポート
    • PostgreSQL、MySQL、SQL Server、SQLite、MongoDB

www.prisma.io

試してみたモチベーション

CRUD系のAPI開発は、ほぼ単純作業的に実装していることが多いので、Prismaみたいな自動生成してくる系のORMで実装すると、どのくらい工数が削減できるのか知りたかったので調査してみた。

サンプルを作ってみる

以下のチュートリアルをベースに、どんな感じで実装するのか試してみる。

www.prisma.io

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()

www.prisma.io

その他の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の中身を操作したい際に使用する。

f:id:wheatandcat:20220405233636p:plain:w600

GraphQLとの親和性

GraphQLとの相性は良さそう。 実際の親和性の話しに関しては以下の参考記事を参照。

まとめ

調査した感じ結構良い気がしたので、どこかで使ってみようかと思います。
ただNode.jsをサーバーで使うと、async/awaitやPromiseを多く記載する必要が合ったりして個人的には余り好きではないのでDenoとかで使えたら良いなあと思ったりした。