Prismaでのスキーマの様々な記述方法解説!

はじめに

こんにちは、株式会社TOKOSのツキヤです!
今回はJavaScriptでサーバーサイドを記述する際に使うPrismaというライブラリについて、特にschema(スキーマ)の記述について解説します!

Prismaの概要

まずは、Prismaについて簡単に説明します!

PrismaはDB(データベース)の操作に必要な実装を1つのライブラリで全てできるような、かなりいい感じのJavaScriptのライブラリです!
具体的には、ざっくり「マイグレーション」「クエリビルダー」「GUIツール」の3つの機能を提供してくれます!

マイグレーション (Prisma Migrate)

データベースのスキーマ変更を管理するためのツールです!
マイグレーションファイルを使って、データベーススキーマの変更を追跡し、バージョン管理をすることが可能です。

ツキヤ
ツキヤ

Railsで言うところの、マイグレーションファイル + rails db:migrate だね!

クエリビルダー (Prisma Client)

メインとなる機能で、直感的な記述方法でDBからデータを取得するものです!
(Railsで例えるとActiveRecordに相当します)

GUIツール (Prisma Studio)

データベースの内容を視覚的に閲覧し、編集できるGUIツールです。
データベースの管理(編集)やデバッグが簡単に可能となります!

スキーマとは?

スキーマについても、少し説明をします!
簡単に言うと、「DBの構造を定義したファイル」です😎
このファイルを見ることで、人間はDBの内容を簡単に把握できるし、プログラミング言語側はDBに実際にデータ構造を設定することができます✨

今回試すテーブル

それでは、今回定義するテーブル群を説明します!
今回は簡単な投稿サービスと定義します!
また、「型」に関してはMySQLという前提で説明します。

usersテーブル

カラム名説明
idbigint主キー
namevarcharユーザー名
emailvarcharメールアドレス
ageinteger, Allow NULL年齢
ユーザーのテーブル

postsテーブル

カラム名説明
idbigint主キー
user_idbigintusersテーブルに対する外部キー
titlevarchar投稿タイトル
bodytext投稿本文
ユーザーの投稿テーブル

tagsテーブル

カラム名説明
idbigint主キー
namevarcharタグ名
投稿(posts)に対して付与できるタグのテーブル

posts_tagsテーブル

カラム名説明
idbigint主キー
post_idbigintpostsテーブルに対する外部キー
tag_idbiginttagsテーブルに対する外部キー
postsテーブルとtagsテーブルは「多対多」であるため、その中間テーブル

上記の4つテーブルを定義したスキーマを定義していきます!!

ツキヤ
ツキヤ

今回は「多対多」等の説明は省きます🙏

schemaの記述

早速、定義ファイルを記述していきます!
まずは、下記コマンドを実行してください。 (必要なライブラリのprisma@prisma/clientはインストール済みという前提です🙏)

$ npx prisma init

これで、./prisma/schema.prismaというファイルができているはずです。

それでは、schema.prismaの内容を記載していきます!
(データベースサービスとの接続については割愛します🙇‍♂️)

// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema

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

datasource db {
  provider     = "mysql"
  url          = env("DATABASE_URL")
  relationMode = "prisma"
}

model User {
  id            Int      @id @default(autoincrement())
  name          String
  email         String   @unique
  age           Int?

  posts         Post[]

  @@map("users")
}

model Post {
  id            Int      @id @default(autoincrement())
  userId        Int      @map("user_id")
  title         String
  body          String   @db.Text

  user          @relation(fields: [userId], references: [id], onDelete: Cascade)
  tags          Tag[]

  @@index([userId])
  @@map("posts")
}

model Tag {
  id            Int      @id @default(autoincrement())
  name          String

  posts         Post[]
}

上記の記述をすることで、先程のテーブル群を作成することができます!!
それでは次章から解説します!

解説

全体の設定

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

datasource db {
  provider     = "mysql"
  url          = env("DATABASE_URL")
  relationMode = "prisma"
}

この部分は、使用するRDBMS等に合わせて書き換えます!
今回自分は「PlanetScale」というサービスを使うので、このような記述にしました!

usersテーブル

テーブルの定義

model User {
  //...
}

このような記述で、各テーブルの宣言を行います!

idカラム

  id            Int      @id @default(autoincrement())

基本は1つ目にカラム名、2つ目に型、3つ目の塊にその他設定を記載します!

ここでは、「idというカラム名で、Int型で、主キーで(オートインクリメントして)」という意味です!
この記述で、1から始まってオートインクリメントされる主キーのidカラムを登録できます!

nameカラム

  name          String

これはシンプルにnameというVARCHAR型のカラムを定義 という意味になります!

emailカラム

  email         String   @unique

こちらは、nameカラムに@uniqueというものが追加されています。

@uniqueは、そのカラムの値を一意にするという意味になります!
メールアドレスは同じものは使ってほしく無いのでこのような制約が必要ですね👍

ageカラム

  age           Int?

こちらは、「integer型で、NULLも許可する」という意味です!
NULLを許可するなら ? を追加するだけでOKです😎

「postsテーブルを複数持つ」という宣言

  posts         Post[]

これは、postsテーブルを多数持つ という意味で、Prisma Clientの使用時にusersとpostsを上手く取得できるようにするための設定です!

実テーブル名の変更

  @@map("users")
}

このように記述することで、JavaScript側から扱う時はUser、実DBのテーブル名はusersにすることができます!

ツキヤ
ツキヤ

テーブル名は基本的にスネークケースが良いので、@@mapの記述はした方がいいかも!

postsテーブル

ここからはまだ説明していない部分について解説します!

実カラム名の変更

  userId        Int      @map("user_id")

このように記述することで、JavaScript側から扱う時はuserId、実カラム名はuser_idとすることができます!

bodyカラム

  body          String   @db.Text

@db.Textと記述することで、VARCHARではなくTEXT型として登録することができます!
(VARCHARは最大文字数が255文字なので、それ以上の文字数を扱いたい場合はこの設定をします)

リレーションの設定

  user          @relation(fields: [userId], references: [id], onDelete: Cascade)

@relationの記述をすることで、他のテーブルに対してのリレーションを設定できます!

fields: どのカラムを外部キーとするか

references: 親テーブルのどのカラムを見るか

onDelete: 親テーブルが削除された時にどのような挙動をさせるか
Cascadeの場合、親のレコードが削除された場合は子のレコードも削除される

インデックスの設定

  @@index([userId])

これで、userIdカラムに対してインデックスを設定することができます!

ツキヤ
ツキヤ

インデックスについてよく分からない人は、一旦外部キーには上記のようにしてインデックスを設定するようにしておこう!

tagsテーブル

tagsテーブルについては、今まで説明したものの組み合わせで記述可能なので、割愛します。

posts_tagsテーブル

最後に、postsテーブルとtagsテーブルを多対多にするための中間テーブルを作成します!
…なのですが、実は中間テーブルはmodelのような定義をしなくても自動で作成されます!

model Post {
  // ....
  tags          Tag[]
}

model Tag {
  // ....
  posts         Post[]
}

ポイントは上記の設定箇所です!
このように、お互いがお互いを複数持つように設定をすることで、Prisma側が自動で中間テーブルを設定してくれるのです✨

コマンド

schemaファイルの記述が完了したら、

$ npx prisma db push

上記コマンドを打つことでDBへの反映が完了します!
DBのテーブルを確認すると、意図したテーブル群ができているはずです💪

おわりに

今回は、Prismaの中でもschemaに焦点を当てて解説をしました!
この記事だけでは網羅できていないので、詳しくは公式サイトを参考にしてみてください🙏