Blitz.jsを触る
フロントもバックエンドも一貫したい => TypeScript使うのが良さそう => フルスタックフレームワークあるか? => frourioとblitz.jsがある => mizchiさんはfrourioを2020年末には推していた。しかし開発コミュニティの成熟度的にblitz.jsの方に軍配がありそう => よってblitz.jsを使ってみる、という流れ。触ってみる。
ScaffoldはRailsライクなコマンドが使える。interactiveにファイル名を聞いてくるのかわいい。(page, api, query , mutationは予約語なのでmodel名に使えない。)
コマンドのタイプにall
を指定でmodel
,queries
,mutation
,page
が生成される。
blitz generate all question text:string
コマンドのタイプにresource
を指定でqueries
,mutation
のみ生成される。
blitz g resource choice text votes:int:default=0 belongsTo:question
関連をschemaファイルに追加する。
model Question {
id Int @id @default(autoincrement())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
text String
choices Choice[] <= これ
}
model Choice {
id Int @id @default(autoincrement())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
text String
votes Int @default(0)
question Question @relation(fields: [questionId], references: [id])
questionId Int
}
migrateの実行。
blitz prisma migrate dev
dbをいじるconsleを立ち上げる。なんかprismaのtypeが合わんというエラーが出たが実行自体はできて謎。
blitz console
mutationsに関連のカスケード削除を定義。railsのmodelのhookみたいな感じ。
export default resolver.pipe(resolver.zod(DeleteQuestion), resolver.authorize(), async ({ id }) => {
await db.choice.deleteMany({ where: { questionId: id } })
const question = await db.question.deleteMany({ where: { id } })
return question
})
mutationsに関連のレコード作成を定義。
export default resolver.pipe(resolver.zod(CreateQuestion), resolver.authorize(), async (input) => {
const question = await db.question.create({
data: {
...input,
choices: { create: input.choices }, <= ここ
},
})
return question
})
あとは表示、更新とかをmutationいじってpagesいじってを繰り返していくだけだった。
色々ブラックボックスでわからんことがあったので調べていく。
-
queries
とmutation
とは?- queryは外部リソースへのGET系の操作。mutationは外部リソースへの変更の操作。
- ReactQueryとは?
- ReactQueryは外部サーバーとのデータ通信周りやキャッシュ・状態管理などを便利にする機能を提供してくれるライブラリ
- useMutationとは?
- ReactQueryのmutation系のhooks。
-
refetch
とは?- useQuery,useMutationで取得したもしくは更新されたデータを再取得してキャッシュを更新する
-
zod
とは- zodはTypeScriptでスキーマの型定義と検証を行えるようにするライブラリ。GraphQueryみたいなことがpureにできる。
-
resolver.pipe
とは- resolver.pipeはBlitzのutility関数。関数合成を行える
-
resolver.pipe(func1(input1), func2(func1Output), func3(func2Output));
みたいなことができるようになる - mutations/queriesのvalidationの適用で利用されてた
-
model
のファイルはどれ?modelディレクトリとかないが?- modelはprisma.schemaファイルに記載されるだけ。Railsでいうとこところのmodelが生成されるわけではない。コマンドごとに何が生成されるかはblitz generateのDocを参照
-
OOOはローカルで宣言していますが...
というLintのエラーが出てるっぽい。compileは通るけど。- exportの無いschema定義をimportしようとしてる。ドキュメントのミスっぽい。Update tutorial.mdx #569でPRが出てるのでいずれ直るはず。
簡単な個人サイトのようなものを作ってみる
簡単なサイトを作ってみることでフレームワークに馴染めると良さそう。
Chakra UIを使う
yarnで直接入れてもいいがBlitz専用のRecipeというものがあるみたいなのでそれで入れてみる。<Provider/>
を_app.tsx
に勝手に書き換えて設定してくれたりするので便利。
blitz install chakra-ui
Chakra UI自体初めて触るのでまずはChakra UIでのレイアウトの組み方など0からやってみる。FlutterのWidgetみたいな感じのコンポーネントがたくさんあるので一つずつ使ってみる。
zodのバリデーションでエラーが...
mutationでバリデーションエラーが出る。prismaのschema定義にUserとDiaryで関連を書いたがそれが原因か。prismaへの習熟も必要そう...。
forwardRefってなに
デフォルト引数どう渡せば...
interface DiaryTitleProps {
date: Date
fontSize?: string
}
export const DiaryTitle = ({ date, fontSize = "2xl" }: DiaryTitleProps) => {
return <Text fontSize={fontSize}>{date.toDateString()}</Text>
}
markdown -> html
下記を使った。
が、syntax highlightが厳しいことが分かったので下記に変更。
postgresを利用するように変更
公式でも初手でSQLiteから変更するように書いてるっぽいからほんとは早めにやっておくのが良さそう
DB用のdocker-compose.yamlを追加。
version: "3.1"
services:
db:
image: postgres:13-alpine
container_name: blitz-blog-postgres
ports:
- "5432:5432"
restart: always
environment:
POSTGRES_USER: admin
POSTGRES_PASSWORD: password
POSTGRES_DB: testdb
volumes:
- dbdata:/var/lib/postgresql/data
volumes:
dbdata:
docker-compose up -d
db/schema.prisma
を編集。sqliteからpostgresqlに変更。
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
.local.env
を編集。
DATABASE_URL=postgresql://admin:password@localhost:5432/testdb
DBのマイグレーション。すでにmigrationディレクトリがあるなら消す。
blitz prisma migrate dev
GUIからテーブルが作成されていることを確認。
blitz prisma studio
アプリのDocker化
とりあえず何かに使えそうではあるのでDocker化だけはやっておく。
- READMEに書いてあるDockefileでrdocker buildがうまくいかない。
Module '"db"' has no exported member 'Prisma'.
というエラーが出る。-
blitz prisma generate
が必要だった。Dockerfileの中にRUN ./node_modules/blitz/bin/blitz prisma generate
を追加してprismaのclientを生成する必要があった。
-
-
Build optimization failed: found page without a React Component as default export in
エラーが出た。-
validation.ts
ファイルをpages
配下に置いてたがこれをpages
の外に出した - Blitzの公式Tutorialがそうなってたのがよくない
-
- DBに接続できない
- envファイルが読み込まれてなかった
-
docker run --env-file=.env.local -p 3000:3000 docker-name
で起動
デプロイ
GCPのサービスはあんまり馴染みがないのでCloudRun + Cloud SQLで構築してみるか。
独自ドメインもサクッと当てられるみたいなので良さそう(選択するリージョンには注意)。
誤算
ここでDBを作成しようと価格を調べたら最低レベルのCloud SQLで$12/monthなので高いことが判明...。ちょっと別の方法考える。
Blitz公式ではVercel + PlanetScaleとRender、RailsWay、その他Herokuを紹介してる。
RenderはDBを使うのに$7/monthかかる。RailsWayはDBを使用するアプリのシミュレーションで$1.2/month。PlanetScaleは10GBのStorageで100million read,10million writeまで無料。ちょっと面倒かなと思ってたけどDB hostingとしてPlanetScaleを使うのが一番安そう。
Vercel + PlanetScale
PlanetScale公式のDeploy to Vercelを参考にする。Blitzの場合はPrismaのAuto Migration設定もしておいたほうが良さそう。
- CLIのinstall
brew install planetscale/tap/pscale
- ログイン
pscale auth login
- dbの作成
pscale database create --region ap-northeast YOUR_DB_NAME
pscale branch create YOUR_DB_NAME development
- テーブルの作成
ここがよくわからなかったんだけど、db/migrations/xxxxxxxxx/migration.sql
の中のテーブルをshellで追加した。もっといい方法がないのかな。
pscale shell personal-site development
とぽちぽち進めていったら外部キー制約が使えないと判明。クソ....betaだから仕方ないか。
-
dbのmainブランチをProductionに設定する
webのGUIからPromote Branchみたいなやつがあるのでそれでmainを設定する。 -
production dbのパスワードを作成
https://docs.planetscale.com/tutorials/connect-any-application#connect-to-your-database -
Vercelにデプロイする準備
VercelでNew Projectから。 -
5で作成したパスワード等を使って
DATABASE_URL
を環境変数に設定
こんな感じ↓
DATABASE_URL=mysql://USER_NAME:PASSWORD@HOST_NAME:3306/DB_NAME?sslmode=require&sslcert=/etc/pki/tls/certs/ca-bundle.crt
-
SESSION_SECRET_KEY
を環境変数に追加
セッションの署名用のkeyを追加する。下記で生成できる。
openssl rand -hex 16
- デプロイ
カスタムドメインを当てる
vercel側のdomainsからカスタムドメインを設定する。AレコードのIPを取得して、お名前.comにAレコードに設定。10分くらいでVercel側の確認がvalidになる。
完
できた↓
リファレンス実装みたいな奴が少なすぎるのでBlitz.jsを使ってアプリを作ってみてる人のリポジトリをまとめていく。blitzjs-awesomeに一つしかなくて心許なすぎる...
SSR,SSGとかどうやるか
SSRの便利関数とか
getServersideProps使う時に便利。
getStaticProps, getStaticPaths
静的に生成しておく時のサーバー側でデータ生成用に使われる関数。
動的ルーティングの静的生成が面倒...。ページネーションを作りたいんだけどどうやるのが正解かまだよく分かってない。
個人サイトの日記を書くためのエディタ用に使ってたreact-markdownを7系にアップロードしたらそれを使用してるpages/
配下のファイルのファイル生成においてVercelのデプロイが失敗するようになった。
エラーメッセージはfound page without a React Component as default export
という感じ。公式の文章を見るとどうもPageのReact Componentが正しくexport default出来てないよということらしい。
しかしながら該当するファイルを確認してもBlitzPageは正しく実装出来ているように見える。
とここでreact-markdownが怪しいと思い、当該ライブラリのChangeLogを読んだところ7系からESMのみの実装に切り替わったらしい。
ESMについて詳しくないので調べた。CJSとESMについては下記が完結にまとまってた。
Next.jsはv11.1からexperimentalであるところのesmExternals: true
を設定するとESMのパッケージに対応できるらしい。が、今使ってるBlitz.jsのNext.jsはv11.1だがesmExternals: true
になってない(ように見える)。
ということで、つまり現状のBlitz.jsではESMのみ対応のreact-markdownのv7系はVercelでは使えない。
この理解で合ってるか...?
確認のためreact-markdownのバージョンをv6.0.3に落としてみた。すると動いた。