くらげになりたい。

くらげのようにふわふわ生きたい日曜プログラマなブログ。趣味の備忘録です。

TypeScript ORM「Prisma」のはじめかた

Node.js/TypeScript ORMのPrisma
高機能なので理解することがたくさんあるので、はじめ方をまとめてみた。

既存のDBがすでにあって、あとからPrismaを導入するときのイメージ。

全体の流れ

Prismaを使った開発の流れはこんな感じ。

初期設定

prismaコマンドはnpxで実行できるので、それを使っていく

初期ファイルの生成

まずは初期化。実行するとスキーマファイルが生成される。

$ npx prisma init

prisma/
└── schema.prisma

接続情報の設定

次に接続情報を設定する。
生成されたschema.prismaは以下のような感じなので、
datasource dbproviderurlを自分の環境に合わせて設定する

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

datasource db {
  provider = "mysql"
  url      = "mysql://<USER>:<PASSWORD>@<HOST>:<PORT>/<DATABASE>"
}

ConnectionURLの書き方などについては以下。 ・Connection URLs (Reference) | Prisma Docs

既存DBのスキーマの取得

既存のDBにPrisma Migrateを導入するガイドは以下。
Adding Prisma Migrate to an existing project | Prisma Docs

接続情報のDBからスキーマを自動生成できる。
以下を実行すると、schema.prismaが更新されて、
スキーマ定義が追加される。

$ npx prisma db pull

初期状態のマイグレーションファイルを生成

初期状態のスキーマファイルを作成する。
実行すると、migrationsフォルダ配下にSQLファイルが作成される。
(すでにmigrationsフォルダがある場合は、移動や削除が必要。)

初期状態のマイグレーションファイルを作成するためには、
全データを削除するresetを行う必要があるので注意。

$ npx prisma migrate dev --name initial-migration --create-only

prisma/
├── migrations
│   ├── 20210101000000_initial_migration
│   │   └── migration.sql
│   └── migration_lock.toml
└── schema.prisma

以下のコマンドで、作成したマイグレーションファイルを適用できる。

$ npx prisma migrate dev

どのマイグレーションファイルを適用するかどうかは、
reset時に作成される_prisma_migrationsテーブルをみて判断しているよう。

スキーマファイルを変更後に、npx prisma migrate devを実行すると、
マイグレーションファイルを作成した上で、実行もしてくれる。

開発時

スキーマを変更する

Prismaでは独自の書き方でスキーマを定義する。サンプルはこんな感じ。
@idなどで制約などを設定する感じ。enumも使える。

// schema.prisma
datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

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

model User {
  id      Int      @id @default(autoincrement())
  email   String   @unique
  name    String?
  role    Role     @default(USER)
  posts   Post[]
  profile Profile?
}

model Profile {
  id     Int    @id @default(autoincrement())
  bio    String
  user   User   @relation(fields: [userId], references: [id])
  userId Int
}

enum Role {
  USER
  ADMIN
}

細かい書き方は以下のドキュメントを参照。
Data model (Reference) | Prisma Docs
Prisma schema API (Reference) | Prisma Docs

Prisma Clientを生成する

Prismaではnpx prisma generateを実行して、
schema.prismaスキーマ情報に沿ったクライアントを生成する。

Userモデルの作成や取得はこんな感じ。
prisma.userが追加されたり、型のサポートを受けれる。

// run inside `async` function
import { PrismaClient } from '@prisma/client'
const prisma = new PrismaClient()

const newUser = await prisma.user.create({
  data: {
    name: 'Alice',
    email: 'alice@prisma.io',
  },
})

const users = await prisma.user.findMany()

細かい書き方は以下のドキュメントを参照。
Prisma Client (Reference) | Prisma Docs
Prisma Client API (Reference) | Prisma Docs

デフォルトでは、バージョン管理対象外になるように、
./node_modules/.prisma/client配下に配置される。

ただ、@prisma/clientがインストールされると、
prisma generateが呼ばれる仕組みのため、以下をインストールしておけばOK

$ npm i -D prisma
$ npm i @prisma/client

Generating the client (Concepts) | Prisma Docs

マイグレーションファイルを生成せず、スキーマを同期する

npx prisma migrate devを実行すると、毎回スキーマファイルが作成されるので、
開発時など頻繁に変更するときには少しめんどくさい。

開発用のコマンドもあり、npx prisma db pushを使うと、
マイグレーションファイルを生成せず同期できる。

いろいろ試して、変更点が整理できたら、
マイグレーションファイルを作成するのがよい感じ。

本番環境

マイグレーション履歴を設定する

Prismaを既存のDBへ導入する際、初期状態のマイグレーションファイルなど、
特定のマイグレーションファイルを適用したくない場合がある。

マイグレーション履歴をあらかじめ設定しておくことで、
実行されないようにする仕組みが用意されている。

上の図の場合、以下のコマンドで、
20210426141759_initial-migrationを適用済みとして設定する。

$ prisma migrate resolve --applied 20210426141759-initial-migration

時系列は見ていないので、20210322091837-new-field/migration.sqlまで、
適用済みとしたい場合は、以下のように両方設定が必要。

$ prisma migrate resolve --applied 20210426141759-initial-migration
$ prisma migrate resolve --applied 20210322091837-new-field

Adding Prisma Migrate to an existing project | Prisma Docs

マイグレーションを実行する

本番環境へ適用されていないマイグレーションをすべて実行する。

$ npx prisma migrate deploy

もし、マイグレーションに失敗した場合は、
prisma migrate resolve --rolled-back
を使って履歴を戻し、修正後再度実行すればOK。

Migration troubleshooting in production | Prisma Docs

小ネタ

複数のデータベースを利用できるようにする

schema.prismaを複数用意すれば実現できるよう。
Multiple Connections / Databases · Issue #2443 · prisma/prisma

prisma/
│── schema1.prisma
└── schema2.prisma

クライアントの出力先がかぶってしまうので、
client.outputもそれぞれ設定が必要。

// schema1.prisma
datasource db {
  provider = "postgres"
  url      = env("DB1_URL")
}

generator client {
  provider = "prisma-client-js"
  output   = "./generated/client1"
}
// schema2.prisma
datasource db {
  provider = "postgres"
  url      = env("DB2_URL")
}

generator client {
  provider = "prisma-client-js"
  output   = "./generated/client2"
}

クライアントを生成するときは、スキーマを指定して実行する。

prisma generate --schema prisma/schema1.prisma
prisma generate --schema prisma/schema2.prisma

利用するときは、各クライアントをインポートすればOK

import { PrismaClient as PrismaClient1 } from '../prisma/client1'
import { PrismaClient as PrismaClient2 } from '../prisma/client2'

const client1 = new PrismaClient1()
const client2 = new PrismaClient2()

ただ、元のIssueがオープンであったり、関連するIssueもまだある感じ。
特に性能面(速度・メモリ使用量)などが課題のよう。

Reuse query engine for multiple PrismaClient instances · Issue #5050 · prisma/prisma
Shared query-engine instances for integration testing · Issue #6707 · prisma/prisma

複数の.envファイルを扱う

以下のようにenv("DATABASE_URL")を使って、
接続先情報を環境変数から利用できるが、
開発時/本番時など切り替えるのがめんどう。

// schema.prisma
datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

公式ドキュメントでは、dotenv-cilを使った方法が紹介されている。
Using multiple .env files. | Prisma Docs

# .env.development
DATABASE_URL="postgresql://prisma:prisma@localhost:5433/dev"
# .env.test
DATABASE_URL="postgresql://prisma:prisma@localhost:5433/tests"
# グローバルでインストール
$ npm install -g dotenv-cli

# dotenvで.envファイルを指定して実行
$ dotenv -e .env.test -- npx prisma migrate dev --name postgres-init

以上!! 便利(´ω`)

SSSAPIではβ版ユーザを募集しています!!

GoogleスプレッドシートをサクッとAPI化するサービスを開発してます!
β期間中は最上位プランが無料なので、この機会にぜひぜひお試しください(´ω`)

■SSSAPI
https://sssapi.app

f:id:wannabe-jellyfish:20210824124009p:plain

参考にしたサイト様