❤️‍🔥

Prisma大好き!

2022/12/05に公開

おはようございます!こんにちは!こんばんは!はじめまして!
スターフェスティバルの DPontaro です!

この記事は スターフェスティバル Advent Calendar 2022 の5日目の記事です!!!!!

https://qiita.com/advent-calendar/2022/stafes

昨日はあひるさんterraform のお話でした。
私もterraformはまだまだ慣れておらずで、こうした実例はめっちゃ参考になりますね!

さてさて本日はインフラからは離れてPrismaというORMについて紹介します!!!!!

Prismaとは

https://www.prisma.io/

Node.js、TypeScript用のORMです。
仲良くなるまで多少の苦労は伴いますが、それに見合うメリットがあります!

migration と Prisma Client

まずは Prisma schemaを定義します。
このschemaファイルは、DBテーブルの定義とPrisma Clientが生成するmodelの定義を行うことができます。
簡単に3テーブルほど定義してみます。

generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "sqlite"
  url      = env("DATABASE_URL")
}

model User {
  id            String    @id
  name          String
  mailAddress   String    @map("mail_address")
  cart          Cart[]
  createdAt     DateTime  @default(now()) @map("created_at")
  updatedAt     DateTime  @default(now()) @updatedAt @map("updated_at")

  @@map("user")
}

model Products {
  id            String    @id
  name          String
  description   String
  price         Int
  cart          Cart[]
  createdAt     DateTime  @default(now()) @map("created_at")
  updatedAt     DateTime  @default(now()) @updatedAt @map("updated_at")

  @@map("products")
}

model Cart {
  id            String    @id
  user          User      @relation(fields: [userId], references: [id])
  userId        String    @map("user_id")
  product       Products  @relation(fields: [productId], references: [id])
  productId     String    @map("product_id")
  createdAt     DateTime  @default(now()) @map("created_at")

  @@map("cart")
}

model間の関係性は @relation で定義します。
これはテーブルの外部キー制約になるのと
クエリでデータを取得する際に重要な要素となります。
他の細かいルールは公式ドキュメントを確認ください。

prisma migrateコマンドを実行すると

  • migration用のsqlファイル生成
  • migrationの実行
  • Prisma Clientの生成

が行われます。

$ npx prisma migrate dev --name init
Environment variables loaded from .env
Prisma schema loaded from prisma/schema.prisma
Datasource "db": SQLite database "dev.db" at "file:./dev.db"

SQLite database dev.db created at file:./dev.db

Applying migration `20221201143200_init`

The following migration(s) have been created and applied from new schema changes:

migrations/
  └─ 20221201143200_init/
    └─ migration.sql

Your database is now in sync with your schema.

✔ Generated Prisma Client (4.7.0 | library) to ./node_modules/@prisma/client in 33ms

生成されたsqlがこちら

-- CreateTable
CREATE TABLE "user" (
    "id" TEXT NOT NULL PRIMARY KEY,
    "name" TEXT NOT NULL,
    "mail_address" TEXT NOT NULL,
    "created_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    "updated_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);

-- CreateTable
CREATE TABLE "products" (
    "id" TEXT NOT NULL PRIMARY KEY,
    "name" TEXT NOT NULL,
    "description" TEXT NOT NULL,
    "price" INTEGER NOT NULL,
    "created_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    "updated_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);

-- CreateTable
CREATE TABLE "cart" (
    "id" TEXT NOT NULL PRIMARY KEY,
    "user_id" TEXT NOT NULL,
    "product_id" TEXT NOT NULL,
    "created_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    CONSTRAINT "cart_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "user" ("id") ON DELETE RESTRICT ON UPDATE CASCADE,
    CONSTRAINT "cart_product_id_fkey" FOREIGN KEY ("product_id") REFERENCES "products" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
);

Prisma Clientに生成された一部モデルが下記になります。

/**
 * Model User
 *
 */
export type User = {
  id: string
  name: string
  mailAddress: string
  createdAt: Date
  updatedAt: Date
}

migrationも実行でき、定義したmodelのtypeも生成されTypeScriptの恩恵も存分に受けられます!

クエリがさくさく書ける

findUniqueやfindManyなどでシンプルにデータを取得できます

  const user = prisma.user.findUnique({
    where: {
      id: "test",
    },
  });

  const users = prisma.user.findMany();

またschema定義で@relationを定義したmodelであればincludeを指定してJOINした状態で取得することもできます

  const cart = await prisma.cart.findUnique({
      where: {
        id: "test",
      },
      include: {
        user: true,
        product: {
          select: {
            id: true,
            name: true,
          }
        },
      },
    });
  console.log(cart);

console.logの結果が↓になります。

{
  id: 'test',
  userId: 'userTestId',
  productId: 'productTestId',
  createdAt: 2022-12-02T02:05:53.764Z,
  user: {
    id: 'userTestId',
    name: 'DPontaro',
    mailAddress: '',
    createdAt: 2022-12-02T02:04:22.682Z,
    updatedAt: 2022-12-02T02:04:22.682Z
  },
  product: { id: 'productTestId', name: 'テスト商品' }
}

include中のuserはtrueが指定されているので、全カラムを取得していますが、
productはselectで、idとnameのみを指定しているのでカラムが絞られた状態で取得されているのがわかります。

慣れるのにある程度時間はかかりますが、schemaのrelationをしっかり把握すると、
さくさく必要なデータを取得するクエリを書くことができ、非常に開発体験が良いです。

GUIツール Prisma Studio

https://www.prisma.io/studio
標準でGUI管理ツールも搭載されています。

$ npx prisma studio

これでサクッと立ち上がります。
手入力でデータも作れますし

relationがあれば、データを選択してポチポチっと作成することもできます。

関連パッケージあれこれ

prisma-erd-generator

https://github.com/keonik/prisma-erd-generator

schemaからER図出力することができます。
schemaに下記定義を追加

generator erd {
  provider = "prisma-erd-generator"
  output = "../ERD.md"
}

prisma generateを実行。


こんな感じに仕上がります。
導入時不具合もあったので、修正のPullRequestも出してそれが取り込まれたりもしました。
OSSコミットは良いことづくめなので、積極的に取り組んでいくことをオススメします!
https://zenn.dev/stafes_blog/articles/first-oss-commit

jest-prisma

Prismaを使用したテストについては、こちらの記事がすごく参考になりました。
https://www.mizdra.net/entry/2022/11/24/153459

特に記事中に出てくるjest-prismaはテストスコープ毎にトランザクションが貼られて
勝手にロールバックされるので、テストデータの後片付けなどが不要となり非常に便利です!
https://github.com/Quramy/jest-prisma

まとめ

ということで慣れるのに苦労もありましたが、
今はガリガリクエリ書けるようになって非常に楽しいです!
まだまだ使い込んでいくつもりなので、他にもこんな使い方あるよ!
とかあれば是非教えてください!

採用とか宣伝とか

プロダクト開発部のtwitterアカウント
https://twitter.com/stafes_tech

Podcast
https://anchor.fm/stafes

などなど情報発信しております!
スターフェスティバルまだまだ積極採用中ですので、興味ありましたらカジュアル面談から是非是非!
ご応募お待ちしてます!
ウェルウェルカムカム!!

スタフェステックブログ

Discussion