BlitzJSのTutorialをやってみた②

技術ネタ
この記事は約17分で読めます。

こんばんわ、hisayukiです❗

前回に引き続き、BlitzJSを進めていこうかと思います。
今回はDatabase周りを進めていこうと思います。

スポンサーリンク

Database setup

ここではPrisma StudioでDBの中身を見るフェーズです。
ただ前回も書いちゃったんで端折ります。

また、DBの種類をSQLitghtから別のRDBに切り替えるフェーズでもありますが、それは次回MySQLの切り替えを1記事書こうかなと思います。

Scaffolding(スキャフォールディング )

BlitzはCLIのgenerateコマンドから、Page、Model、CRUD(Queries、Mutations)を自動生成してくれます。
今回はQuestionモデルとChoiceモデルを作成していきます。

Question

まずはQuestionモデル

blitz generate all question text:string

generateコマンドで作成されるModelは、デフォルトでid、createdAt、updatedAtフィールドが作成されます。
questionモデルには追加でtextフィールドを追加します。

CREATE    app/pages/questions/[questionId].tsx
CREATE    app/pages/questions/[questionId]/edit.tsx
CREATE    app/pages/questions/index.tsx
CREATE    app/pages/questions/new.tsx
CREATE    app/questions/components/QuestionForm.tsx
CREATE    app/questions/queries/getQuestion.ts
CREATE    app/questions/queries/getQuestions.ts
CREATE    app/questions/mutations/createQuestion.ts
CREATE    app/questions/mutations/deleteQuestion.ts
CREATE    app/questions/mutations/updateQuestion.ts
 
✔ Model for 'question' created in schema.prisma:

> model Question {
>   id        Int      @default(autoincrement()) @id
>   createdAt DateTime @default(now())
>   updatedAt DateTime @updatedAt
>   text      String   
> }
 
? Run 'prisma migrate dev' to update your database? (Y/n) ‣ true

マイグレーションをするか聞かれるので、YESで進めます

✔ Run 'prisma migrate dev' to update your database? (Y/n) · true
Environment variables loaded from .env
Prisma schema loaded from db/schema.prisma
Datasource "db": SQLite database "db.sqlite" at "file:./db.sqlite"

? Name of migration › 

マイグレーションに名前をつけるように言われるので、今回はadd questionと付けます。

✔ Name of migration … add question
The following migration(s) have been created and applied from new schema changes:

migrations/
  └─ 20210504065423_add_question/
    └─ migration.sql

Your database is now in sync with your schema.

✔ Generated Prisma Client (2.20.1) to ./node_modules/@prisma/client in 107ms

これでQuestionテーブルを作成するマイグレーションファイルが出来上がります。
migration.sqlの中身はこんな感じになっています。

CREATE TABLE "Question" (
    "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
    "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    "updatedAt" DATETIME NOT NULL,
    "text" TEXT NOT NULL
);

コードを確認すると、このように追加されています。
先程付けたadd questionはmigrationsディレクトリの中に作られています。

schema.prismaも更新されています。

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

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

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

// --------------------------------------

model User {
  id             Int      @id @default(autoincrement())
  createdAt      DateTime @default(now())
  updatedAt      DateTime @updatedAt
  name           String?
  email          String   @unique
  hashedPassword String?
  role           String   @default("USER")

  tokens   Token[]
  sessions Session[]
}

model Session {
  id                 Int       @id @default(autoincrement())
  createdAt          DateTime  @default(now())
  updatedAt          DateTime  @updatedAt
  expiresAt          DateTime?
  handle             String    @unique
  hashedSessionToken String?
  antiCSRFToken      String?
  publicData         String?
  privateData        String?

  user   User? @relation(fields: [userId], references: [id])
  userId Int?
}

model Token {
  id          Int      @id @default(autoincrement())
  createdAt   DateTime @default(now())
  updatedAt   DateTime @updatedAt
  hashedToken String
  type        String
  // See note below about TokenType enum
  // type        TokenType
  expiresAt   DateTime
  sentTo      String

  user   User @relation(fields: [userId], references: [id])
  userId Int

  @@unique([hashedToken, type])
}

// NOTE: It's highly recommended to use an enum for the token type
//       but enums only work in Postgres.
//       See: https://blitzjs.com/docs/database-overview#switch-to-postgresql
// enum TokenType {
//   RESET_PASSWORD
// }

model Question {
  id        Int      @default(autoincrement()) @id
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
  text      String   
}

Choice

次にChoiceモデルを作成します。
先程はgenerateコマンドの際にallを指定していました。

blitz generate all question text:string

このallの部分は変更することで、欲しいファイルのみに変えることが出来ます。
こちらのページを参考にしてください。

ChoiceモデルではPageは不要のため、resourcegenerateコマンドを実行します。

blitz generate resource choice text:string votes:int:default=0 belongsTo:question

追加でtextとvotesフィールドを追加します。
加えて、Questionモデルとリレーションを張るようにしています。

CREATE    app/choices/queries/getChoice.ts
CREATE    app/choices/queries/getChoices.ts
CREATE    app/choices/mutations/createChoice.ts
CREATE    app/choices/mutations/deleteChoice.ts
CREATE    app/choices/mutations/updateChoice.ts
 
✔ Model for 'choice' created in schema.prisma:

> model Choice {
>   id         Int      @default(autoincrement()) @id
>   createdAt  DateTime @default(now())
>   updatedAt  DateTime @updatedAt
>   text       String   
>   votes      Int      @default(0)
>   question   Question @relation(fields: [questionId], references: [id])
>   questionId Int      
> }
 
 
✔ Run 'prisma migrate dev' to update your database? (Y/n) · false
 

この段階では、Question側のモデルにChoiseフィールドがないのでマイグレーションはNoにしておきます。

このコマンドでChoiseのModel、Queries、Mutationsが出来ています。

QuestionとChoiceを紐付ける

この紐付けはschema.prismaを手動で更新します
QuestionのフィールドにChoiceの配列を追加します。

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

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

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

// --------------------------------------

model User {
  id             Int      @id @default(autoincrement())
  createdAt      DateTime @default(now())
  updatedAt      DateTime @updatedAt
  name           String?
  email          String   @unique
  hashedPassword String?
  role           String   @default("USER")

  tokens   Token[]
  sessions Session[]
}

model Session {
  id                 Int       @id @default(autoincrement())
  createdAt          DateTime  @default(now())
  updatedAt          DateTime  @updatedAt
  expiresAt          DateTime?
  handle             String    @unique
  hashedSessionToken String?
  antiCSRFToken      String?
  publicData         String?
  privateData        String?

  user   User? @relation(fields: [userId], references: [id])
  userId Int?
}

model Token {
  id          Int      @id @default(autoincrement())
  createdAt   DateTime @default(now())
  updatedAt   DateTime @updatedAt
  hashedToken String
  type        String
  // See note below about TokenType enum
  // type        TokenType
  expiresAt   DateTime
  sentTo      String

  user   User @relation(fields: [userId], references: [id])
  userId Int

  @@unique([hashedToken, type])
}

// NOTE: It's highly recommended to use an enum for the token type
//       but enums only work in Postgres.
//       See: https://blitzjs.com/docs/database-overview#switch-to-postgresql
// enum TokenType {
//   RESET_PASSWORD
// }

model Question {
  id        Int      @default(autoincrement()) @id
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
  text      String
  choices   Choice[] //これを追加
}

model Choice {
  id         Int      @default(autoincrement()) @id
  createdAt  DateTime @default(now())
  updatedAt  DateTime @updatedAt
  text       String
  votes      Int      @default(0)
  question   Question @relation(fields: [questionId], references: [id])
  questionId Int
}

schema.prismaを更新したら、マイグレーションコマンドを実行します。

blitz prisma migrate dev
Environment variables loaded from .env
Prisma schema loaded from db/schema.prisma
Datasource "db": SQLite database "db.sqlite" at "file:./db.sqlite"

? Name of migration › add choice

マイグレーションに名前をつけるように言われるので、add choiceと付けます。

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

migrations/
  └─ 20210504112340_add_choice/
    └─ migration.sql

Your database is now in sync with your schema.

✔ Generated Prisma Client (2.20.1) to ./node_modules/@prisma/client in 129ms

これでChoiceを追加した際のマイグレーションファイルが出来ました。

Blitz consoleからDBを更新する

Blitz CLIにはデフォルトでPrisma用データベースクライアントが用意されています。
データベースクライアントを使って、データ操作をしていきます。

blitz console
You have entered the Blitz console
Tips: - Exit by typing .exit or pressing Ctrl-D
      - Use your db like this: await db.project.findMany()
      - Use your queries/mutations like this: await getProjects({})
Loading Modules 14/18
⚡️ > 

最初にQuestionテーブルのレコードを全件取得します。

⚡ > await db.question.findMany()
[]

まだデータが入ってないので[]で返ってきます

次にQuestionテーブルに1件レコードを追加し、結果を変数qに入れます

⚡️ > let q = await db.question.create({data: {text: "What's new?"}})
undefined
Loading Modules 14/18
Loading Modules 12/18
Loading Modules 15/18

変数qの中身を確認します。

⚡️ > q
{
  id: 1,
  createdAt: 2021-05-04T11:44:31.978Z,
  updatedAt: 2021-05-04T11:44:31.979Z,
  text: "What's new?"
}

変数qの中のtextだけを見てみます。

⚡️ > q.text
"What's new?"

追加したレコードのidは1なので、id:1のレコードを更新します。

⚡️ > q = await db.question.update({where: {id: 1}, data: {text: "What's up?"}})
{
  id: 1,
  createdAt: 2021-05-04T11:44:31.978Z,
  updatedAt: 2021-05-04T11:49:40.804Z,
  text: "What's up?"
}
Loading Modules 13/18
Loading Modules 15/18
Loading Modules 15/18
Loading Modules 14/18

undefined

最後にまた全件レコード取得をします。

⚡ > await db.question.findMany()
[
  {
    id: 1,
    createdAt: 2021-05-04T11:44:31.978Z,
    updatedAt: 2021-05-04T11:49:40.804Z,
    text: "What's up?"
  }
]

このように、ちょっとしたレコードの参照や、追加などはCLIから行えます。
といっても、Prisma Studioが強力なのでそっちでやっちゃっても良いわけですが・・・😅

一応、CLI派用、GUI派用どちらからでもDBの操作はBlitzのみで行なえます。

まとめ

今回はスキャフォールディングを含めたデータベース周りのチュートリアルを進めました。
generateコマンドで一気に永続化層からPageまでを作ってくれるのは強力ですね。
気になったのはCRUDを一気に作るのは良いけど、利用しないのもあるのでは?って思った。

そもそもRailsやってきてないから、こういうものと言われたらそうなのかもしれないけど・・・
CRUDは必要になったときに、必要なものを作ればいい派なので使うかわからないもの作られてもなーって感じです。

一応、generateコマンド時にQueryとMutationどちらかしか作らないとかはできそうだけど🤔
取得系と更新系くらいは分けて作るようにしたほうがよさそうかなーって思いました。

にしても、やっぱPrismaが強力すぎるな・・・😅

コメント