公式チュートリアル中ではあまり深く触れられない「generateやmigrationで何が行われているのか」や、recipeを使ってライブラリを導入してみるといったことを書きます。

Blitz入門記事はこちら

https://tech.mof-mof.co.jp/blog/blitz-introduction/

generate resourceやmigrationの挙動を見てみる

Blitz上でクエリの発行やmigration周辺を担当してくれるORM、Prismaを軽く見ていきます。

まずはチュートリアル完了時点のDB schemaを見てみましょう。

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

datasource db {
  provider = "sqlite"
  url = "***"
}

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

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

model User {
  id             Int       @default(autoincrement()) @id
  createdAt      DateTime  @default(now())
  updatedAt      DateTime  @updatedAt
  name           String?
  email          String    @unique
  hashedPassword String?
  role           String    @default("user")
  sessions       Session[]
}

model Session {
  id                 Int       @default(autoincrement()) @id
  createdAt          DateTime  @default(now())
  updatedAt          DateTime  @updatedAt
  expiresAt          DateTime?
  handle             String    @unique
  user               User?     @relation(fields: [userId], references: [id])
  userId             Int?
  hashedSessionToken String?
  antiCSRFToken      String?
  publicData         String?
  privateData        String?
}

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      
}

init時のschemaも db/migrations 下にあると思うので見比べてみると良いでしょう。UserとSessionが生成されているはずです。

modelを生成する blitz generate コマンドによって schema.prisma が更新され、migration実行時にそのスナップショットがとられるということになっています。

上記の挙動を確認するために、試しにQuestionにコメントを付けるためのCommentモデルを生成してみましょう。

$ blitz generate resource comment text:string belongsTo:question

✔ Model for 'comment' created successfully:

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

Now run blitz db migrate to add this model to your database

CREATE    app/comments/queries/getComment.ts
CREATE    app/comments/queries/getComments.ts
CREATE    app/comments/mutations/createComment.ts
CREATE    app/comments/mutations/deleteComment.ts
CREATE    app/comments/mutations/updateComment.ts

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       @default(autoincrement()) @id
  createdAt      DateTime  @default(now())
  updatedAt      DateTime  @updatedAt
  name           String?
  email          String    @unique
  hashedPassword String?
  role           String    @default("user")
  sessions       Session[]
}

model Session {
  id                 Int       @default(autoincrement()) @id
  createdAt          DateTime  @default(now())
  updatedAt          DateTime  @updatedAt
  expiresAt          DateTime?
  handle             String    @unique
  user               User?     @relation(fields: [userId], references: [id])
  userId             Int?
  hashedSessionToken String?
  antiCSRFToken      String?
  publicData         String?
  privateData        String?
}

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      
}

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

既存のschemaにCommentが追記されました。

続けてmigrateしてみます。migration名はadd commentで。

$ blitz db migrate

✔ Name of migration … add comment
📼  migrate save --name add comment

Local datamodel Changes:

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

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

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

Prisma Migrate just created your migration 20201209020644-add-comment in

migrations/
  └─ 20201209020644-add-comment/
    └─ steps.json
    └─ schema.prisma
    └─ README.md

略

これにより db/migrations 以下に新たにファイルが追加されました。

schema.prisma はこの時点でのschema、README.md は前回のmigrationとの差分が記載されています。今回であれば、Commentが追加された旨が書いてありますね。

この一連の操作は、 db/migrations/migrate.lock に記録されます。

# Prisma Migrate lockfile v1

20201208064339-initial-migration
20201208073113-init-db
20201209020644-add-comment

Railsではschema_migrationsテーブルに保存されてるやつですね。up / downはここを見て行うんでしょうか。

Recipeを使ってBlitzを拡張する Tailwind編

Blitzにはコマンドひとつでライブラリを導入できる、Recipeというコマンドがあります。Railsでいうところの gem install deviserails g devise:install を一気にやってくれるようなイメージでしょうか。

現在公式で用意されているrecipeはこちら。 https://github.com/blitz-js/blitz/tree/canary/recipes

また、recipeは自分で作ることもできるようです。

今回はtailwindを試してみます。途中で何回か確認でenterを押す必要があります。ファイル名これでいい?とか聞かれます。

$ blitz install tailwind

✅ Installed 3 dependencies
✅ Successfully created postcss.config.js, tailwind.config.js
✅ Successfully created app/styles/index.css
✅ Modified 1 file: app/pages/_app.tsx
✅ Modified 1 file: app/pages/_app.tsx

🎉 The recipe for Tailwind CSS completed successfully! Its functionality is now fully configured in your Blitz app.

試しに [questionId].tsx で使っているボタンにスタイルを当ててみます。

  return (
    <div>
      <h1>{question.text}</h1>
      <ul>
        {question.choices.map((choice) => {
          return (
            <li>
              {choice.text} - {choice.votes} votes
              <button
                className="border border-indigo-500 bg-indigo-500 text-white px-4 py-2 m-2 hover:bg-indigo-600"
                onClick={() => handleVote(choice.id, choice.votes)}
              >
                Vote
              </button>

blitz-tailwind

ちゃんと反映されましたね。すごく手軽!