Prisma Migrationの動作を確認してみた

Prisma Migration の挙動確認
2021.02.03

はじめに

CX事業本部の佐藤智樹です。

今回はPrismaから最近(2020年12月)プレビュー版で提供開始されたMigration機能の挙動を確認してみます。少し動作やファイル構成を確認しただけなので、さらに使って分かったことがあれば追記や別記事でアップしていきます。

2021年2月3日時点の情報なので基本的には以下の公式チュートリアルやドキュメントを確認してください。実際動かした際のファイル構成がさらっと見たい場合などは参考になるかと思います。基本備忘録用の記事です。

環境情報

種別 バージョン
Prisma 2.15.0
yarn 1.21.1
Node 14.4.0
TypeScript 4.1.3
Docker 3.1.0
Postgres 11(on Docker)

サンプルコード

今回の記事で実行した結果のコードをリポジトリに置いてあります。全体的に構成が見たければこちらをご確認ください。

Prismaの導入

記事の最初で上げたチュートリアルに従いつつ、一部変更(npm->yarnなど)しながら試していきます。ほぼチュートリアルのままで、実際に生成されたファイルの紹介などがメインになります。

まずディレクトリを作成して、プロジェクトを立ち上げます。

$ mkdir hello-prisma-migration
$ cd hello-prisma-migration
$ yarn
$ yarn add @prisma/cli typescript ts-node @types/node --save-dev

次にtsconfig.jsonファイルを生成しておきます。

tsconfig.json

{
  "compilerOptions": {
    "sourceMap": true,
    "outDir": "dist",
    "strict": true,
    "lib": ["esnext"],
    "esModuleInterop": true
  }
}

prisma initコマンドでPrisma実行用のファイルが生成できます。

yarn prisma init
yarn run v1.21.1
$ /Users/sato.tomoki/Documents/hogehoge/hello-prisma-migration/node_modules/.bin/prisma init
Environment variables loaded from .env

✔ Your Prisma schema was created at prisma/schema.prisma.
  You can now open it in your favorite editor.

warn Prisma would have added DATABASE_URL="postgresql://johndoe:randompassword@localhost:5432/mydb?schema=public" but it already exists in .env

Next steps:
1. Set the DATABASE_URL in the .env file to point to your existing database. If your database has no tables yet, read https://pris.ly/d/getting-started
2. Set the provider of the datasource block in schema.prisma to match your database: postgresql, mysql or sqlite.
3. Run yarn prisma introspect to turn your database schema into a Prisma data model.
4. Run yarn prisma generate to install Prisma Client. You can then start querying your database.

More information in our documentation:
https://pris.ly/d/getting-started
    
✨  Done in 2.53s.

DBの接続設定

チュートリアルでもCLIの結果でも書かれてるように、まず接続する先のDATABASE_URLを設定します。.envファイル内に書かれているものを編集します。今回は後ほど記載するPostgresの設定に合わせて記載します。

DATABASE_URL="postgresql://postgres:postgres@localhost:5433/test_db?schema=public"

初期modelの設定とマイグレーション

prisma init時に生成されたprismaディレクトリ内のschema.prismaファイルを編集します。providerは既にpostgresになっており、clientも変更不要なので今回使用するmodelの追加だけ行います。

schema.prisma

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

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

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

// add model
model prisma_test {
  id         Int      @id @default(autoincrement())
  timestamp  DateTime
  created_at DateTime @default(now())
  updated_at DateTime @default(now())
}

この状態で一度移行(初回実行時は新規作成)用のSQLを生成して実行します。

$ yarn prisma migrate dev --name init --preview-feature

実行すると以下のように移行用のmigration.sqlファイルとmigration_lock.tomlファイルが生成されます。

注意:この時点でDATABASE_URLの指定が誤っている場合エラーは出ませんが、DBに接続できないためmigration用のファイルだけ生成されてDB自体は更新されません。

migration.sql

-- CreateTable
CREATE TABLE "prisma_test" (
    "id" SERIAL NOT NULL,
    "timestamp" TIMESTAMP(3) NOT NULL,
    "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
    "updated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,

    PRIMARY KEY ("id")
);

migration_lock.toml

# Please do not edit this file manually
provider = "postgresql"

移行用のSQLファイルは出来ていますが更新履歴などがファイルでは確認できません。DBのスキーマを確認してみます。すると以下のように作成したテーブルと付随するシーケンス以外に、migration用の_prisma_migrationsテーブルが確認できます。

test_db=# \d
                 List of relations
 Schema |        Name        |   Type   |  Owner   
--------+--------------------+----------+----------
 public | _prisma_migrations | table    | postgres
 public | prisma_test        | table    | postgres
 public | prisma_test_id_seq | sequence | postgres
(3 rows)

_prisma_migrationsの中身を見ると以下のようになっています。

test_db=# \x
Expanded display is on.
test_db=# select * from _prisma_migrations;
-[ RECORD 1 ]-------+----------------------------------------------------------------
id                  | 302a2e38-652a-4b28-95a7-476f45ecc66e
checksum            | 806aa07f736c2f963fa81d6b3930faf415a23cd7ce3363cadd24acb1768a12c
finished_at         | 2021-02-02 14:44:42.525554+00
migration_name      | 20210202144442_init
logs                | 
rolled_back_at      | 
started_at          | 2021-02-02 14:44:42.507204+00
applied_steps_count | 1

いつ更新されたのか、適応したmigrationの名前などが記録されています。

マイグレーション(2回目)

modelに項目を追加して再度マイグレーションしてみます。modelへ以下のようにhogehogeカラムを追加します。

schema.prisma

…
// add model
model prisma_test {
  id         Int      @id @default(autoincrement())
  timestamp  DateTime
  hogehoge   String
  created_at DateTime @default(now())
  updated_at DateTime @default(now())
}

再度マイグレーションを実行します。

$ yarn prisma migrate dev --name init --preview-feature

すると再度マイグレーション用のSQLファイルが生成されます。

migration.sql

/*
  Warnings:

  - Added the required column `hogehoge` to the `prisma_test` table without a default value. This is not possible if the table is not empty.

*/
-- AlterTable
ALTER TABLE "prisma_test" ADD COLUMN     "hogehoge" TEXT NOT NULL;

コメントにもあるようにこのSQLはもしテーブルにデータがあれば失敗します。今回はテーブルが空で成功しているようなのでDBを確認します。

test_db=# select * from _prisma_migrations;
-[ RECORD 1 ]-------+----------------------------------------------------------------
id                  | 302a2e38-652a-4b28-95a7-476f45ecc66e
checksum            | 806aa07f736c2f963fa81d6b3930faf415a23cd7ce3363cadd24acb1768a12c
finished_at         | 2021-02-02 14:44:42.525554+00
migration_name      | 20210202144442_init
logs                | 
rolled_back_at      | 
started_at          | 2021-02-02 14:44:42.507204+00
applied_steps_count | 1
-[ RECORD 2 ]-------+----------------------------------------------------------------
id                  | 91b0e424-5588-440d-8d1a-3fda77689006
checksum            | 3bb3bb7f849e5c99ee94b231860bdcaddd4b6b121c5bf717cea8080cc6aab2a
finished_at         | 2021-02-03 02:11:54.061427+00
migration_name      | 20210203021154_init
logs                | 
rolled_back_at      | 
started_at          | 2021-02-03 02:11:54.052891+00
applied_steps_count | 1

test_db=# \d prisma_test
                                          Table "public.prisma_test"
   Column   |              Type              | Collation | Nullable |                 Default                 
------------+--------------------------------+-----------+----------+-----------------------------------------
 id         | integer                        |           | not null | nextval('prisma_test_id_seq'::regclass)
 timestamp  | timestamp(3) without time zone |           | not null | 
 created_at | timestamp(3) without time zone |           | not null | CURRENT_TIMESTAMP
 updated_at | timestamp(3) without time zone |           | not null | CURRENT_TIMESTAMP
 hogehoge   | text                           |           | not null | 
Indexes:
    "prisma_test_pkey" PRIMARY KEY, btree (id)

先ほど実行したmigrationの結果が新たに追加されており、テーブル定義も変更されています。

失敗するマイグレーションの挙動確認

最後にテーブルにデータが入っている状態で、マイグレーションが失敗するパターンを確認してみます。以下のSQLでデータを1件追加します。

INSERT INTO
  prisma_test
  (timestamp, hogehoge)
VALUES
  (now(), 'fugafuga')
;
INSERT 0 1
test_db=# select * from prisma_test;
-[ RECORD 1 ]-----------------------
id         | 1
timestamp  | 2021-02-03 02:27:11.754
created_at | 2021-02-03 02:27:11.754
updated_at | 2021-02-03 02:27:11.754
hogehoge   | fugafuga

この状態でmodelにカラム(必須属性)を追加します。

schema.prisma

…
// add model
model prisma_test {
  id         Int      @id @default(autoincrement())
  timestamp  DateTime
  hogehoge   String
  name       String
  created_at DateTime @default(now())
  updated_at DateTime @default(now())
}
yarn prisma migrate dev --name init --preview-feature
yarn run v1.21.1
$ /Users/sato.tomoki/Documents/hogehoge/hello-prisma-migration/node_modules/.bin/prisma migrate dev --name init --preview-feature
Environment variables loaded from .env
Prisma schema loaded from prisma/schema.prisma
Datasource "db": PostgreSQL database "test_db", schema "public" at "localhost:5433"


Error: 
⚠️ We found changes that cannot be executed:

  • Step 0 Added the required column `name` to the `prisma_test` table without a default value. There are 1 rows in this table, it is not possible to execute this migration.

error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.

既にデータがテーブルに存在するのでデフォルト値の無い必須属性追加はエラーとなりました。DB側でのマイグレーション実行記録もなく、テーブル定義の変更も入りませんでした。

実行自体が失敗したことでマイグレーションファイル自体が生成されず、ファイルの追加/変更もありませんでした。

NULL許容するように追加カラムの設定を変更したところマイグレーションが正常に動作しました。

schema.prisma

…
// add model
model prisma_test {
  id         Int      @id @default(autoincrement())
  timestamp  DateTime
  hogehoge   String
  name       String? // 変更
  created_at DateTime @default(now())
  updated_at DateTime @default(now())
}

上記の仕組みからマイグレーションできないような定義変更の場合は、マイグレーションファイルが生成されず、実行結果も残らないことが分かります。

本番での実行

前章まではマイグレーションファイル生成とDBへの適応を同時に行うコマンドを使っていました。本番環境の場合は、開発環境で作成したマイグレーションファイルを使って以下のコマンドを実行することでDBへの適応だけを行うことができます。

$ yarn prisma migrate deploy --preview-feature

感想

まだ複雑な挙動を試したわけでは無いですが、簡単な用途であればプレビュー版でも使えるような感触でした。Prismaは便利なものだと思うので今後も使用してみたいと思います。

この記事を見て使って不具合などあった場合は、以下のissueでフィードバックを募集しているそうなので是非コメントしてください。