Okarin note

頭の整理もかねて色々と書いていきます

最近流行りのprismaを使ってみた。環境構築編

TypeScript で使える OR マッパー。
別の OR マッパーだと TypeORM がある。

今のプロジェクトでTypeORMは使っているがどう違いがあるのか気になったのでやってみる。

www.prisma.io

prisma のインストール

npm i prisma

初期化する

npx prisma init

このコマンドで prisma を使うための準備ができる。

ルートのファルダに prisma 用のフォルダと.env ファイルが作成される

prisma/migrations/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")
}

.env

# Environment variables declared in this file are automatically made available to Prisma.
# See the documentation for more detail: https://pris.ly/d/prisma-schema#accessing-environment-variables-from-the-schema

# Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB and CockroachDB (Preview).
# See the documentation for all the connection string options: https://pris.ly/d/connection-strings

DATABASE_URL="mysql://username:password@localhost:3306/mydb"

.env の環境設定ファイルから接続情報を読み込むようになっているので修正する。

テーブル情報を追加する

こんな感じで追加してみた。

prisma/migrations/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")
}

model Post {
  id        Int      @id @default(autoincrement())
  content   String
  postedBy  String
  trackingCookie String
  createdAt DateTime @default(now())
}
  • @id
    • 主キーの設定
  • @default()
    • デフォルトでどうするかの設定ができる auto_increment やら初期値やら設定ができる

テーブル定義を使ってテーブルを作成する

以下のコマンドを実行する

npx prisma migrate dev --name init
mysql> show tables;
+-----------------------------+
| Tables_in_bulletin-board-db |
+-----------------------------+
| Post                        |
| _prisma_migrations          |
+-----------------------------+
2 rows in set (0.00 sec)

出来ている!
簡単にできるから便利ね。

もっと色々使っていこう。

テーブル作成時に出るエラー

Environment variables loaded from .env
Prisma schema loaded from prisma/schema.prisma
Datasource "db": MySQL database "bulletin-board-db" at "172.22.0.2:3306"

Error: P3014

Prisma Migrate could not create the shadow database. Please make sure the database user has permission to create databases. Read more about the shadow database (and workarounds) at https://pris.ly/d/migrate-shadow

Original error: Error code: P1010

User `okarin` was denied access on the database `bulletin-board-db`

こんな感じのエラーが出た。
権限エラーのようだがよくわからなかったので shadow database のドキュメントを見に行った。

www.prisma.io

MySQL だと以下の権限が必要らしい

Database user must have CREATE, ALTER, DROP, REFERENCES ON *.* privileges

というわけで実行してみた。

GRANT CREATE, ALTER, DROP, REFERENCES ON `bulletin-board-db`.* TO okarin@%;

変わらない。。。いったいなぜなんだ。。。 しゃあなしでユーザーを root に変えたら動いた。

DATABASE_URL="mysql://root:password@localhost:3306/mydb"

動くだけならいいけど気になる。 自分でも調べますが、分かる方がいたら教えてください。。。

git remote prune originで解消しなかった話

経緯

GWが終わった後の出勤。 私は全部休んだのでブランチの状況が変わっていた。なのでまずはソースコードを最新にしようとしてgit pullをしたんだけれどその時にエラーが出た。

fatal: cannot lock ref ...

原因

ブランチ名の一部が競合したこと。
feature/〇〇のように名前付けしていたらfeatureの名前でディレクトリができ、最後の文字列がファイルになる。

例:

feature/add/hoge

feature
|--add
    |--hoge <- ファイル

feature/add/hogeを作った後に、
feature/add/hoge/fugaのようなブランチを作ろうとすると、gitはfugaファイルを作るためにhogeディレクトリを作ろうとする。
hogeファイルがある状態で作ろうとするため名前が競合しているから作成できない。
これがエラー原因。

解決方法

できるだけ名前が競合しないように名前付けをこだわってあげよう。 「/」で区切りすぎりすぎないのがいいのかもしれない。

Dockerのプッシュが上手くいかなかった

概要

Dockerの入門サンプルをやっていた。
Dockerfileからイメージを作成してそれをDocker Hubにプッシュするまで。
プッシュすると以下のエラーが出て困っていた。

denied: requested access to the resource is denied

原因

コンソールからDockerにログインしていなかった。

以下のような流れになる模様。

docker login

usename ...
password ...

...
Login Succeeded

Logging in with your password grants your terminal complete access to your account.
For better security, log in with a limited-privilege personal access token. Learn more at https://docs.docker.com/go/access-tokens/

このdocker loginというコマンドはDocker Hubにログインするためのコマンドだった。
つまりログイン認証していない状態でプッシュしようとしてエラーではじかれていた模様。

denied: 要求されたリソースへのアクセスは拒否されました

日本語に翻訳するとアクセス拒否なのでここで気づくべきでしたね。

そんなこんなで成功しました

教訓

エラーメッセージをよく読んでなぜダメだったのか考えましょう。

ローカルのコンテナイメージを Container_Registry にプッシュする方法

めちゃくちゃ詰まったのでメモ。 GCP のコンソール上でできるかとおもったけれども Google Cloud SDK が必要で CLI で gcloud コマンドが必要だった。ドキュメントでも gcloud コマンドを使ってたのでそういうやり方しかないのだろう。
備忘録として残しておく。

環境

Windows11


Google Cloud SDK をインストールする

インストール方法は以下のリンクを参照。
https://cloud.google.com/sdk/docs/install?hl=ja


Google Cloud SDK の初期化処理を行う

インストーラーを実行後にコマンドプロンプトが開き、gcloud init が実行される。 選択肢を番号で押していく。
自分の環境だけかもしれないが、Firebase のプロジェクトも表示されたが GCP で作ったプロジェクトを選ぶ。そうしないとこの後に実行する処理が認証エラーで失敗する。


プッシュしたいイメージに tag を付ける。

docker tag push対象のimage [HOSTNAME]/[PROJECT-ID]/[IMAGE]
  • [HOSTNAME] : 以下から選ぶ。これでイメージを保存する場所が決まる。
    • gcr.io は米国内のデータセンター
    • us.gcr.io は米国内のデータセンター
    • eu.gcr.io は、欧州連合のデータセンター
    • asia.gcr.io は、アジアのデータセンター
  • [PROJECT-ID] : 対象のGoogle CloudプロジェクトのプロジェクトID
  • [IMAGE] : コンテナイメージの名前

ちなみにここでものすごく詰まった。 ドキュメントの理解に時間がかかった。


プッシュする

gcloud auth configure-docker // gcloudの認証でdockerが使えるようになる設定

docker push 作成したイメージ

Node.jsのnextTick()について

nextTick()

Node.jsのイベントループを提供しているlibuvではなくNode.js自体が提供している機能。

全ての非同期処理の中で最速で実行される。

実行箇所はイベントループの各フェーズごと。

nextTick()の処理が重いとイベントループ自体をブロックする。

ブロックするとどうなるか?

→ サーバーが停止するためリクエストに応答できない

もしバックグラウンドで動作させたい処理があるならnextTick()よりsetImmediate()を使った方がいい。

setImmediate()はnextTick()と似ているがイベントループの最後のcheckフェーズで実行されるのでイベントループをブロックすることがない。

Next.jsでプロジェクト生成直後にTypeScript化する

どうも、okarinです。 Next.jsを使う時にデフォルトだとJavaScriptなので変更方法を忘れた時のためのメモです。

そのうちNext.js側で対応しそう

対応していた。。。

公式のGetting Startedを見たら以下のコマンドでよかった

nextjs.org

npx create-next-app@latest プロジェクト名 --typescript

プロジェクト名はキャメルケースを許容してないのでそこだけ注意です。
ということで以降の作業は一切無駄になりましたが一応残しておきます。。。

以下コマンドでプロジェクトを生成する。

npm init next-app <プロジェクト名>

生成直後にbuildをしてサーバーを起動する。

next build

next start

問題なければ以下のようなページが表示されるはず。

f:id:SazanamiN:20211222223140p:plain

tsconfig.json のファイルを空で作成する。

touch tsconfig.json

ファイルの作成後に再度、ビルドをする。

このタイミングで空のtsconfig.jsonに自動で中身が書き込まれる!

(めちゃくちゃすごい。。。

next build

自動生成されたtsconfig.jsの中身

{
  "compilerOptions": {
    "target": "es5",
    "lib": [
      "dom",
      "dom.iterable",
      "esnext"
    ],
    "allowJs": true,
    "skipLibCheck": true,
    "strict": false,
    "forceConsistentCasingInFileNames": true,
    "noEmit": true,
    "esModuleInterop": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "jsx": "preserve"
  },
  "include": [
    "next-env.d.ts",
    "**/*.ts",
    "**/*.tsx"
  ],
  "exclude": [
    "node_modules"
  ]
}

/pages以下にある.jsの拡張子を.tsxに変更する

以上で完了

TypeORMでレコードを消す際に使うdelete()とclear()の違い(MySQL)

TypeORMのエンティティマネージャーに定義されている削除用のメソッドは二つあるので名称も違うし違いがあるだろうということで調べました。
それと業務で使っていて、実際にDBからレコードを消す際にどちらを使うか迷ったのも調べた理由の一つです。

clear()

TRUNCATE TABLEを実行してレコードを消す。
MySQL8.0ではTRUNCATE TABLEはDMLではなくDDLに変わったようで暗黙的にコミットが走る。
そのため、トランザクションを張ったときに上手く機能してくれない可能性がある。
https://stackoverflow.com/questions/5972364/mysql-truncate-table-within-transaction/5972738#5972738

delete()

DELETE FROMを実行してレコードを消す。
DMLなのでトランザクション中、エラーが発生したらロールバックしてくれる。

まとめ

暗黙的にコミットされる可能性のあるclear()よりもdelete()を使った方が使い勝手がよさそうだなと思います。
テストデータを用意するようなコミットされても問題ない処理ならclear()を使ってもよさそうです。