メインコンテンツへスキップ

Prisma ORMレイヤーのアップグレード (PostgreSQL)

概要

このページでは、アップグレードプロセスの最初のステップ、すなわちPrisma 1の設定をPrisma ORM 2にアップグレードする方法について説明します。具体的には、以下の方法を学びます。

  1. Prisma ORM 2 CLIを開発依存関係として追加する
  2. Prisma ORM 2スキーマを作成する
  3. 接続URLを決定し、データベースに接続する
  4. データベースをイントロスペクトする(これまではPrisma 1で管理されていました)
  5. Prisma 1 Upgrade CLIを使用して、新しいPrisma ORM 2データモデルにおけるスキーマの非互換性を解決する
  6. Prisma Clientをインストールして生成する

これらの手順が完了したら、次のガイドに進み、データベースクエリにPrisma Clientを使用するようにアプリケーションレイヤーをアップグレードする方法を学びます。

注意:アップグレードプロセス中、データベースをグラフィカルに表示できると役立つ場合があります。したがって、TablePlusPosticoのようなグラフィカルデータベースクライアントを使用してデータベースに接続することをお勧めします。

1. Prisma ORM 2 CLIをインストールする

Prisma ORM 2 CLIは、npm上のprismaパッケージとして利用でき、prismaコマンドで呼び出されます。

Prisma 1の以前のprismaコマンドはprisma1に名前が変更されています。これについてはこちらで詳しく学ぶことができます。

Prisma ORM 2 CLIは、Node.jsプロジェクトに次のようにインストールできます(package.jsonがあるディレクトリでこのコマンドを呼び出すようにしてください)

npm install prisma --save-dev

注意:Prisma 1では、通常CLIをグローバルにインストールすることが推奨されていました。現在では、バージョン競合を防ぐためにPrisma CLIをローカルにインストールすることをお勧めします。

これで、npxをプレフィックスとして付けて、ローカルにインストールしたprisma CLIを使用できます。

npx prisma

プロジェクト全体を一度にアップグレードする場合は、Prisma 1 CLIをアンインストールすることもできます(そうでない場合は以下を展開してください)。

# remove global installation
npm uninstall -g prisma1

# remove local installation
npm uninstall prisma1

Prisma 1 CLIを並行して使い続けたい場合は展開してください

Prisma 1 CLIを引き続き使用したい場合は、グローバルインストールを削除し、prisma1 CLIを開発依存関係として追加することをお勧めします。

# installs v1.34 of the Prisma 1 CLI
npm uninstall -g prisma
npm install prisma1 --save-dev

これで、次のように呼び出すことができます。

npx prisma1

CLIバージョンが1.34より小さいもの(例:1.30)が必要な場合は、次のようにインストールできます。

# installs v1.30 of the Prisma 1 CLI
npm uninstall -g prisma@1.30
npm install prisma@1.30 --save-dev

これで、次のように呼び出すことができます。

npx prisma

2. Prisma ORM 2スキーマを作成する

このガイドでは、まずprisma initコマンドを使用して新しいPrismaスキーマを作成し、次にイントロスペクションを使用してデータモデルを「埋める」ことを行います。

Prismaスキーマを作成するために以下のコマンドを実行してください(prismaという名前のフォルダが既に存在する場合、このコマンドはエラーをスローします)

npx prisma init

以下のエラーが表示される場合、現在のprismaディレクトリの名前を変更する必要があります。

ERROR  A folder called prisma already exists in your project.
Please try again in a project that is not yet using Prisma.

現在のprismaディレクトリをprisma1にリネームして、これが以前のPrisma 1設定を保持していることを明確にできます。

mv prisma prisma1

これでinitを実行でき、成功します。

npx prisma init

以下の出力が表示されるはずです。

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

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 your `datasource` block in `schema.prisma` to match your database: `postgresql`, `mysql` or `sqlite`.
3. Run `prisma db pull` to turn your database schema into a Prisma data model.
4. Run `prisma generate` to install Prisma Client. You can then start querying your database.

More information in our documentation:
https://pris.ly/d/getting-started

このコマンドは、prismaという新しいフォルダと2つのファイルを作成しました。

初期のPrismaスキーマは以下のようになります。

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"
}

Prisma 1では、prisma.ymlでPrisma Clientのどの言語バリアントを使用するかを指定していました。Prisma ORM 2では、この情報はgeneratorブロックを介してPrismaスキーマ内に指定されるようになりました。

注意:Prisma 1とは異なり、Prisma Client 2.0のTypeScriptおよびJavaScriptバリアントは、prisma-client-jsという同じジェネレーターを使用します。生成された型(index.d.ts)は、プレーンなJavaScriptプロジェクトでも常に含まれます。これにより、TypeScriptを使用していない場合でもVS Codeでのオートコンプリートのような機能が有効になります。

3. 接続URLを決定し、データベースに接続する

Prisma 1では、データベース接続はPrisma ORMサーバーを起動するために使用されるDocker Composeファイルで設定されます。Prisma ORMサーバーは、Prisma ClientアプリケーションコードからのすべてのデータベースリクエストをプロキシするGraphQLエンドポイント(HTTP経由)を公開します。そのHTTPエンドポイントはprisma.ymlに指定されています。

Prisma ORM 2では、HTTPレイヤーはもはや公開されておらず、Prisma Client 2.0はデータベースに対して「直接」リクエストを実行するように設定されています(つまり、リクエストはPrisma ORMのクエリエンジンによってプロキシされますが、余分なサーバーはもはやありません)。

したがって、次のステップとして、Prisma ORM 2にどのようなデータベースを使用しているか(MySQLまたはPostgreSQL)と、それがどこにあるかを伝える必要があります。

まず、schema.prisma内のdatasourceブロックにあるproviderフィールドが、正しいデータベースを使用するように設定されていることを確認する必要があります。

  • PostgreSQLを使用している場合、providerフィールドに値"postgresql"を定義する必要があります。
  • MySQLを使用している場合、providerフィールドに値"mysql"を定義する必要があります。

コードブロックのタブを切り替えて、両方の例を確認してください。

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

providerフィールドが設定されたら、.envファイル内で接続URLを設定できます。

Prisma ORMサーバーをデプロイするために使用したDocker Composeファイル内のデータベース設定が次のようになっていると仮定します。

docker-compose.yml
PRISMA_CONFIG: |
port: 4466
databases:
default:
connector: postgres
host: postgres
port: 5432
user: prisma
password: prisma

また、prisma.ymlendpointが次のように設定されていると仮定します。

prisma.yml
endpoint: http://:4466/myproject/dev

これらの接続詳細に基づいて、.envファイル内のDATABASE_URL環境変数を次のように設定する必要があります。

.env
DATABASE_URL="postgresql://janedoe:randompassword@localhost:5432/prisma?schema=myproject$dev"

schema引数は、通常、サービス名とサービスステージ(prisma.ymlendpointの一部)を$文字で区切って構成されます。

prisma.ymlにサービス名とステージが指定されていない場合もあります。

prisma.yml
endpoint: http://:4466/

その場合、schemaは次のように指定する必要があります。

.env
DATABASE_URL="postgresql://janedoe:randompassword@localhost:5432/prisma?schema=default$default"

接続URLのページで詳細を確認してください。

4. データベースをイントロスペクトする

このガイドの目的のために、以下のPrisma 1データモデルを使用します(データモデルがSQLにどのようにマッピングされるかを見るには、下のSQLタブを選択してください)。

type User {
id: ID! @id
email: String @unique
name: String!
role: Role! @default(value: CUSTOMER)
jsonData: Json
profile: Profile
posts: [Post!]!
}

type Post {
id: ID! @id
createdAt: DateTime! @createdAt
updatedAt: DateTime! @updatedAt
title: String!
content: String
published: Boolean! @default(value: false)
author: User @relation(link: TABLE)
categories: [Category!]!
}

type Profile {
id: ID! @id
bio: String
user: User! @relation(link: INLINE)
}

type Category {
id: ID! @id
name: String!
posts: [Post!]!
}

enum Role {
ADMIN
CUSTOMER
}

このデータモデルには3つのリレーションがあることに注意してください。

  • 1対1: UserProfile
  • 1対多: UserPost (_PostToUserリレーションテーブルを介して維持されます)
  • 多対多: PostCategory (_CategoryToPostリレーションテーブルを介して維持されます)

これで、以下のコマンドでPrisma ORMのイントロスペクションをデータベースに対して実行できます。

npx prisma db pull

db pullが呼び出されたときに何が起こるかを図で示します。

Introspect your database

上記のPrisma 1データモデルの場合、これにより以下のPrisma ORM 2スキーマが生成されます(モデルはPrisma 1データモデルの初期の順序に一致するように再配置されています)。

schema.prisma
model User {
id String @id @default(cuid())
email String? @unique
name String
role String
jsonData String?
Profile Profile[]
Post Post[]
}

model Post {
id String @id @default(cuid())
createdAt DateTime
updatedAt DateTime
title String
content String?
published Boolean
Category Category[]
User User[]
}

model Profile {
id String @id @default(cuid())
bio String?
user String? @unique
User User? @relation(fields: [user], references: [id])
}

model Category {
id String @id @default(cuid())
name String
Post Post[]
}

これはすでに有効なPrisma ORM 2スキーマですが、Prisma 1同等物の一部であった多くの機能が不足しています。

  • PostcreatedAtおよびupdatedAtフィールドに対する自動生成された日付値がない
  • Userroleフィールドにデフォルト値がない
  • Postpublishedフィールドにデフォルト値がない

さらに、よりイディオム的/人間工学的なPrisma Client APIをもたらすいくつかの矛盾があります。

  • UserProfile は1対1ではなく1対多のリレーションです
  • UserPost は多対多ではなく1対多のリレーションです
  • リレーションフィールドが大文字になっています(例: User上のProfilePost
  • UserjsonDataフィールドはJson型ではなくString型です
  • UserroleフィールドはRole型ではなくString型であり、roleのenum定義が完全に欠落しています

これらの矛盾は、Prisma Client APIで利用できる「機能セット」に実際には影響しませんが、以前に存在していた特定の制約/保証が失われます。

例えば、Prisma ORMは、Userが最大1つのProfileに接続されていることを保証しなくなります。なぜなら、テーブル間のリレーションはイントロスペクション中に1対多として認識されたため、1つのUserレコードが複数のProfileレコードに接続できるようになるからです。

もう1つの問題は、jsonDataおよびroleフィールドに、それが有効なJSONであるか、またはRole enumの値であるかに関係なく、任意のテキストを保存できることです。

これらの矛盾の詳細については、スキーマの非互換性のページを参照してください。

以下では、これらの非互換性をPrismaスキーマアップグレードCLIを使用して1つずつ修正していきます。

5. PrismaスキーマアップグレードCLIを使用してスキーマの非互換性を解決する

Prisma 1 Upgrade CLIは、Prismaスキーマをアップグレードし、上記に挙げたほとんどの矛盾を解消するのに役立つインタラクティブなツールです。

Prisma 1 Upgrade CLIは、主に2つのフェーズで動作します。

  1. プレーンSQLを介してデータベーススキーマを修正する
  2. Prisma ORM 2スキーマに欠落している属性やその他のスキーマの修正を追加する

最初のフェーズでは、データベーススキーマを調整するためにデータベースに対して実行すべきSQLステートメントが生成され、表示されます。2番目のフェーズに進む前に、これらのステートメントのすべてまたは一部を実行できます。

2番目のフェーズでは、手動で何かをする必要はありません。Upgrade CLIは、特定のPrisma ORMレベルの属性(例: @default(cuid))@updatedAt)を追加したり、リレーションフィールドの名前をPrisma 1データモデルのものと一致するように調整したり、Prisma 1データモデルで両側で必須だった1対1リレーションがPrisma ORM 2スキーマでも必須であることを確認したりすることで、Prismaスキーマに変更を加えます。

プロセス中いつでもやり直すことができ、2番目のフェーズから1番目のフェーズに戻ることができることに注意してください。

この図では、緑色の領域が最初のフェーズを、青色の領域が2番目のフェーズを示しています。フェーズ間にオプションでprisma db pullを実行して、Prisma ORMデータモデルを更新できることに注意してください。

Fixing the schema incompatibilities

Upgrade CLIを使用するには、プロジェクトにローカルにインストールするか、ここに示されているようにnpxを使用してインストールせずに一度呼び出すことができます。

npx prisma-upgrade prisma1/prisma.yml prisma/schema.prisma

CLIは以下のメッセージを表示します。

◮ Welcome to the interactive Prisma Upgrade CLI that helps with the
upgrade process from Prisma 1 to Prisma ORM 2.

Please read the docs to learn more about the upgrade process:
https://pris.ly/d/how-to-upgrade

➤ Goal
The Upgrade CLI helps you resolve the schema incompatibilities
between Prisma 1 and Prisma ORM 2. Learn more in the docs:
https://pris.ly/d/schema-incompatibilities

➤ How it works
Throughout the process, you'll need to adjust your database schema by sending
SQL statements to it. The SQL statements are provided by the Upgrade CLI.

Note that the Upgrade CLI never makes changes to your database,
you are in full control over any operations that are executed against it.

You can stop and re-run the Upgrade CLI at any time.

These are the different steps of the upgrade process:

1. The Upgrade CLI generates SQL commands for you to run on your database.
2. You run the SQL commands against your database.
3. You run the `npx prisma db pull` command again.
4. You run the `npx prisma-upgrade` command again.
5. The Upgrade CLI adjusts the Prisma ORM 2 schema by adding missing attributes.

➤ Note
It is recommended that you make a full backup of your existing data before starting
the upgrade process. If possible, the migration should be performed in a staging
environment before executed against a production environment.

➤ Help
If you have any questions or run into any problems along the way,
please create an issue at:
https://github.com/prisma/prisma1-upgrade/issues

Are you ready? [Y/n]

Yボタンを押し、キーボードのRETURNを押して確認し、続行します。

確認すると、CLIはデータベースに対して実行すべきSQLステートメントを出力します。

➤ Adjust your database schema
Run the following SQL statements against your database:

Fix columns with ENUM data types
https://pris.ly/d/schema-incompatibilities#enums-are-represented-as-text-in-database

CREATE TYPE "default$default"."Role" AS ENUM ('ADMIN', 'CUSTOMER');
ALTER TABLE "default$default"."User" ALTER COLUMN "role" SET DATA TYPE "default$default"."Role" using "role"::"default$default"."Role";


Add missing `DEFAULT` constraints to the database
https://pris.ly/d/schema-incompatibilities#default-values-arent-represented-in-database

ALTER TABLE "default$default"."User" ALTER COLUMN "role" SET DEFAULT 'CUSTOMER';
ALTER TABLE "default$default"."Post" ALTER COLUMN "published" SET DEFAULT false;


Fix columns with JSON data types
https://pris.ly/d/schema-incompatibilities#json-type-is-represented-as-text-in-database

ALTER TABLE "default$default"."User" ALTER COLUMN "jsonData" SET DATA TYPE JSONB USING "jsonData"::TEXT::JSONB;


Replicate `@createdAt` behavior in Prisma ORM 2
https://pris.ly/d/schema-incompatibilities#createdat-isnt-represented-in-database

ALTER TABLE "default$default"."Post" ALTER COLUMN "createdAt" SET DEFAULT CURRENT_TIMESTAMP;


Fix 1-1 relations by adding `UNIQUE` constraints
https://pris.ly/d/schema-incompatibilities#inline-1-1-relations-are-recognized-as-1-n-missing-unique-constraint

ALTER TABLE "default$default"."Profile" ADD UNIQUE ("user");


Migrate IDs from varchar(25) to varchar(30)
https://pris.ly/d/schema-incompatibilities#mismatching-cuid-length

ALTER TABLE "default$default"."Category" ALTER COLUMN "id" SET DATA TYPE character varying(30);
ALTER TABLE "default$default"."Post" ALTER COLUMN "id" SET DATA TYPE character varying(30);
ALTER TABLE "default$default"."Profile" ALTER COLUMN "id" SET DATA TYPE character varying(30);
ALTER TABLE "default$default"."Profile" ALTER COLUMN "user" SET DATA TYPE character varying(30);
ALTER TABLE "default$default"."User" ALTER COLUMN "id" SET DATA TYPE character varying(30);

➤ Breaking changes detected

In order to fully optimize your database schema, you'll need to run a few SQL
statements that can break your Prisma 1 setup. Note that these changes are optional
and if you are upgrading gradually and running Prisma 1 and Prisma ORM 2 side-by-side,
you should not perform these changes yet. Instead, you can perform them whenever
you are ready to completely remove Prisma 1 from your project.
If you are upgrading all at once, you can safely perform these changes now.

Learn more in the docs:
https://pris.ly/d/how-to-upgrade'

注意:破壊的変更に関する注意が表示されても、今は無視してください。これについては後で説明します。

表示されたSQLステートメントはいくつかの「バケット」に分類されており、すべて特定のスキーマの非互換性を解決することを目的としています。

  • ENUMデータ型の列を修正する
  • データベースに欠落しているDEFAULT制約を追加する
  • JSONデータ型の列を修正する
  • Prisma 2で@createdAtの動作を再現する
  • UNIQUE制約を追加して1対1リレーションを修正する

次のステップとして、SQLステートメントをデータベースに送信し始めることができます。これらの変更はすべて非破壊的であり、Prisma 1とPrisma ORM 2を並行して引き続き使用できます。

次のセクションでは、データベースに個別に送信するさまざまな種類のSQLステートメントについて説明します。

5.1. プレーンSQLによるデータベーススキーマの修正(非破壊的)

このセクションでは、出力されたSQLステートメントを順に見ていき、データベースに対して実行します。

5.1.1. ENUMデータ型の列を修正する

このツールが最初に行うことは、Prisma 1データモデルのenum定義が、基となるデータベースで実際のENUM型として表現されるようにすることです。現状では、それらはプレーンな文字列(例: MySQLのMEDIUMTEXT)として表現されています。

CLIは現在以下の出力を表示しています。

Fix columns with ENUM data types
https://pris.ly/d/schema-incompatibilities#enums-are-represented-as-text-in-database

CREATE TYPE "default$default"."Role" AS ENUM ('ADMIN', 'CUSTOMER');
ALTER TABLE "default$default"."User" ALTER COLUMN "role" SET DATA TYPE "default$default"."Role" using "role"::"default$default"."Role";

⚠️ 警告:Prisma 1とPrisma ORM 2を並行して実行している場合、これらのSQLステートメントはPrisma 1のセットアップを破壊します。この点については、ドキュメントが間もなく更新される予定です。

これらのステートメントを今すぐデータベースに対して実行してください。

Altering columns to use ENUM with SQL

5.1.2. データベースに欠落しているDEFAULT制約を追加する

次に、Upgrade CLIは、デフォルト値がデータベースに表現されていないという問題を解決するために、データベースに直接関連するDEFAULT制約を追加するSQLステートメントを生成することで支援します。

この場合、ツールによって2つのDEFAULT制約が不足していることが示唆されています。

Add missing `DEFAULT` constraints to the database
https://pris.ly/d/schema-incompatibilities#default-values-arent-represented-in-database

ALTER TABLE "default$default"."User" ALTER COLUMN "role" SET DEFAULT 'CUSTOMER';
ALTER TABLE "default$default"."Post" ALTER COLUMN "published" SET DEFAULT false;

これらのSQLステートメントをコマンドラインクライアントまたはPosticoのようなGUIを使用してデータベースに対して実行できます。

Adding missing DEFAULT constraints to columns

5.1.3. JSONデータ型の列を修正する

次に、このツールは、Prisma 1データモデルのJsonフィールドが、基となるデータベースでJSON列として表現されるように支援します。現状では、それらはプレーンな文字列(例: MySQLのMEDIUMTEXT)として表現されています。

列の型をJSONに変更することで、Prisma ORM 2のイントロスペクション中にフィールドが適切にJsonとして認識されるようになります。

CLIは現在以下の出力を表示しています。

Fix columns with JSON data types
https://pris.ly/d/schema-incompatibilities#json-type-is-represented-as-text-in-database

ALTER TABLE "default$default"."User" ALTER COLUMN "jsonData" TYPE JSON USING "jsonData"::json;

⚠️ 警告:Prisma 1とPrisma ORM 2を並行して実行している場合、これらのSQLステートメントはPrisma 1のセットアップを破壊します。この点については、ドキュメントが間もなく更新される予定です。

これらのSQLステートメントをコマンドラインクライアントまたはPosticoのようなGUIを使用してデータベースに対して実行できます。

Adding missing DEFAULT constraints to columns

5.1.4. Prisma ORM 2で@createdAtの動作を再現する

次に、このツールは、@createdAtの動作がデータベースに反映されていないという問題を解決するのに役立ちます。

CLIは現在以下の出力を表示しています。

Replicate `@createdAt` behavior in Prisma ORM 2.0
https://pris.ly/d/schema-incompatibilities#createdat-isnt-represented-in-database

ALTER TABLE "default$default"."Post" ALTER COLUMN "createdAt" SET DEFAULT CURRENT_TIMESTAMP;

これらのSQLステートメントをコマンドラインクライアントまたはPosticoのようなGUIを使用してデータベースに対して実行できます。

Running an SQL command to alter a column

5.1.5. UNIQUE制約を追加して1対1リレーションを修正する

さて、このツールは、データベースの外部キー列user(Prisma 1データモデルのリレーションフィールドにちなんで名付けられています)にUNIQUE制約を追加することで、UserProfile間の現在の1対多リレーションを1対1リレーションに戻すのに役立ちます。

CLIは現在以下の出力を表示しています。

Fix 1-1 relations by adding `UNIQUE` constraints
https://pris.ly/d/schema-incompatibilities#inline-1-1-relations-are-recognized-as-1-n-missing-unique-constraint

ALTER TABLE "default$default"."Profile" ADD UNIQUE ("user");

これらのSQLステートメントをコマンドラインクライアントまたはPosticoのようなGUIを使用してデータベースに対して実行できます。

Running an SQL command to alter a column

5.1.6. CUID長の不一致を修正する

注意:これらのSQLステートメントは、基となるデータベースの列タイプを変更した後でも、Upgrade CLIに表示され続けます。これは現在のUpgrade CLIの制限です。

最後に、このツールは、データベースの外部キー列user(Prisma 1データモデルのリレーションフィールドにちなんで名付けられています)にUNIQUE制約を追加することで、現在のVARCHAR(25)型のID列をVARCHAR(30)型に変更するのに役立ちます。

CLIは現在以下の出力を表示しています。

Migrate IDs from varchar(25) to varchar(30)
https://pris.ly/d/schema-incompatibilities#mismatching-cuid-length

ALTER TABLE "default$default"."Category" ALTER COLUMN "id" SET DATA TYPE character varying(30);
ALTER TABLE "default$default"."Post" ALTER COLUMN "id" SET DATA TYPE character varying(30);
ALTER TABLE "default$default"."Profile" ALTER COLUMN "id" SET DATA TYPE character varying(30);
ALTER TABLE "default$default"."Profile" ALTER COLUMN "user" SET DATA TYPE character varying(30);
ALTER TABLE "default$default"."User" ALTER COLUMN "id" SET DATA TYPE character varying(30);

これらのSQLステートメントをコマンドラインクライアントまたはPosticoのようなGUIを使用してデータベースに対して実行できます。

Running an SQL command to alter a column

5.1.7. 破壊的変更の検出

Upgrade CLIが破壊的変更に関する通知を出力した場合、データベーススキーマを完全に最適化するためには、Prisma 1の互換性を損なう可能性のある調整が必要です。

破壊的変更が検出されなかった場合、セクション5.2をスキップできます。

アップグレード戦略によっては、これらの変更を今すぐ実行するか、Upgrade CLIの次のフェーズにスキップするかを選択できます。

  • 段階的な並行アップグレード戦略に従っている場合、これらの変更はPrisma 1のセットアップを破壊するため、まだ実行しないでください。その場合、nを入力してRETURNを押すことで、Upgrade CLIの次のフェーズに進むことができます。
  • 一度にすべてアップグレードする戦略に従っている場合、これらの変更を今すぐ実行できます。その場合、Yを入力してRETURNを押して続行してください。

5.2. プレーンSQLによるデータベーススキーマの修正(破壊的)

このセクションでは、Prisma 1のセットアップを破壊するスキーマの非互換性を解決します。プロジェクトでPrisma 1をまだ実行している場合は、これらの変更を実行しないでください!

5.2.1. 不正確な多対多リレーションを修正する

次に、Upgrade CLIは、Prisma 1がリレーションテーブルで表現している、現在新しいPrisma ORM 2スキーマでは多対多リレーションとしてのみ存在するすべての1対1および1対多リレーションを修正するのに役立ちます。具体的には、これは現在多対多として定義されているが、本来1対多リレーションであるべきUserPostリレーションの場合に該当します。

これを修正するには、以下のマイグレーションを実行する必要があります。

  1. Postに新しい外部キー列を作成し、Userテーブルに直接リンクさせます。
  2. リレーションテーブルから外部キーの値をPostの新しい外部キー列に移行します。
  3. リレーションテーブルを削除します。

これらの指示は現在CLIによって出力されています。

➤ Adjust your database schema
Run the following SQL statements against your database:

Fix one-to-many table relations
https://pris.ly/d/schema-incompatibilities#all-non-inline-relations-are-recognized-as-m-n

ALTER TABLE "default$default"."Post" ADD COLUMN "authorId" character varying(25) ;
ALTER TABLE "default$default"."Post" ADD CONSTRAINT "author" FOREIGN KEY ("authorId") REFERENCES "default$default"."User"("id");
UPDATE "default$default"."Post" SET "authorId" = "default$default"."_PostToUser"."B" FROM "default$default"."_PostToUser" WHERE "default$default"."_PostToUser"."A" = "default$default"."Post"."id";
DROP TABLE "default$default"."_PostToUser";


➤ Next Steps

After you executed one or more of the previous SQL statements against your database,
please run the following two commands to refresh your Prisma ORM 2 schema and check
the changes.

1. Run `npx prisma db pull` again to refresh your Prisma ORM 2 schema.
2. Run `npx prisma-upgrade` again.

If you can't or don't want to execute the remaining SQL statements right now, you can
skip to the last step where the Upgrade CLI adds missing attributes to your Prisma ORM 2
schema that are not picked up by introspection.

Skip to the last step? [Y/n]?

この修正には、3つのSQLステートメントを実行する必要があります。

  1. Postテーブルに新しい列authorIdを作成します。この列は、Userテーブルのidフィールドを参照する外部キーである必要があります。
    ALTER TABLE `Post` ADD COLUMN `authorId` VARCHAR(25);
    ALTER TABLE `Post` ADD FOREIGN KEY (`authorId`) REFERENCES `User` (`id`);
  2. _PostToUserリレーションテーブルからすべての行を読み取り、各行に対して以下の処理を行うSQLクエリを作成します。
    1. Aの値を見て、対応するPostレコードを見つけます。
    2. Bの値をそのPostレコードのauthorIdの値として挿入します。
    UPDATE Post, _PostToUser
    SET Post.authorId = _PostToUser.B
    WHERE Post.id = _PostToUser.A
  3. _PostToUserリレーションテーブルを削除します。
    DROP TABLE `_PostToUser`;

Fixing incorrect m-n relations with SQL

これらのコマンドを実行すると、リレーションテーブルの列BからのレコードのユーザーID値が新しいauthorId列に移行されます。

5.2. データベースを再イントロスペクトしてPrismaスキーマを更新する

この時点で、Upgrade CLIを使用してスキーマの非互換性を解決しました。これで、nを入力してRETURNを押すことで、一旦Upgrade CLIを終了できます。

このセクションでは、もう一度イントロスペクションを行い、Prismaスキーマを更新します。今回は、データベーススキーマが調整されたため、以前のPrismaスキーマの欠陥が解消されます。

npx prisma db pull

今回は、結果として得られるPrismaスキーマは以下のようになります。

schema.prisma
model User {
id String @id
name String
email String? @unique
jsonData Json?
role Role @default(CUSTOMER)
Post Post[]
Profile Profile?
}

model Post {
id String @id
createdAt DateTime @default(now())
updatedAt DateTime
title String
content String?
published Boolean @default(false)
authorId String?
User User? @relation(fields: [authorId], references: [id])
Category Category[] @relation(references: [id])
}

model Category {
id String @id
name String
Post Post[] @relation(references: [id])
}

model Profile {
bio String?
id String @id
user String? @unique
User User? @relation(fields: [user], references: [id])
}

enum Role {
ADMIN
CUSTOMER
}

このスキーマではほとんどの問題が解決されていますが、まだ以下の点が不足しています。

5.2. Prisma 2スキーマに欠落している属性やその他のスキーマ修正を追加する

CLIは現在以下を出力しています。

➤ What happens next
As a last step, some final adjustments will be made to your Prisma ORM 2 schema
to carry over some Prisma ORM-level attributes that aren't picked up by introspection.

As a last step, some final adjustments will be made to your Prisma ORM 2.0
schema to carry over some Prisma ORM-level attributes that aren't picked
up by introspection.

Warning
Your current Prisma ORM 2.0 schema will be overwritten, so please
make sure you have a backup!

Are you ready? [Y/n]

この時点で、CLIが出力したすべてのSQLステートメントを実行したか、またはその一部をスキップしたかのいずれかです。いずれにしても、最後のステップに進み、Upgrade CLIに欠落しているPrisma ORM 2属性を追加させることができます。通常、これらは以下の通りです。

  • @idフィールドの@default(cuid())
  • Prisma 1でこの属性を使用していたフィールドの@updatedAt
  • Prisma 1の@dbおよび@@dbの代替としての@mapおよび@@map

このステップで、Upgrade CLIはPrisma ORM 2への移行で発生したその他の問題も修正します。

  • Prisma 1で両側で必須だった1対1リレーションがPrisma ORM 2スキーマでも必須であることを確認します。
  • リレーションフィールドの名前をPrisma 1データモデルと同じ名前に変更します(近日公開予定)。

これらの変更を適用するには、Upgrade CLIを再実行します。

npx prisma-upgrade prisma1/prisma.yml prisma/schema.prisma

すべてのスキーマの非互換性を解決しなかった場合、Upgrade CLIは残りのSQLステートメント(およびID移行用のステートメント)を出力します。この時点ではそれらを無視し、プロンプトが表示されたときにYを連続して入力し、RETURNを押すことで最後のステップに進むことができます。

すべてのスキーマの非互換性を解決した場合、SQLステートメントは出力されず、Upgrade CLIは以下の内容のみを出力します。

$ npx prisma-upgrade prisma1/prisma.yml prisma/schema.prisma

➤ Next Steps

After you executed one or more of the previous SQL statements against your database,
please run the following two commands to refresh your Prisma ORM 2 schema and check
the changes.

1. Run `npx prisma db pull` again to refresh your Prisma ORM 2 schema.
2. Run `npx prisma-upgrade` again.

If you can't or don't want to execute the remaining SQL statements right now, you can
skip to the last step where the Upgrade CLI adds missing attributes to your Prisma ORM 2
schema that are not picked up by introspection.

Skip to the last step? [Y/n]?

もう一度、Yを入力し、RETURNを押して確認します。

Upgrade CLIの最後のプロンプトでは、上記で述べたPrismaスキーマへの変更を確認するように求められます。

➤ What happens next
As a last step, some final adjustments will be made to your Prisma ORM 2 schema
to carry over some Prisma ORM-level attributes that aren't picked up by introspection.

As a last step, some final adjustments will be made to your Prisma ORM 2.0
schema to carry over some Prisma ORM-level attributes that aren't picked
up by introspection.

Warning
Your current Prisma ORM 2.0 schema will be overwritten, so please
make sure you have a backup!

Are you ready? [Y/n]

最後に、Yを入力し、RETURNを押して確認します。

これはUpgrade CLIの最終出力です。

Updating prisma/schema.prisma...
Done updating prisma/schema.prisma!

✔ Congratulations, you're all set!

➤ Note
If you didn't execute all generated SQL commands against your database,
you can re-run the Upgrade CLI at any time.

Note that the Upgrade CLI doesn't resolve all of the schema incompatibilities
between Prisma 1 and Prisma ORM 2. If you want to resolve the remaining ones,
you can do so manually by following this guide:
https://pris.ly/d/upgrading-the-prisma-layer

➤ Next steps
Otherwise you can continue your upgrade process by installing Prisma Client 2:
npm install @prisma/client

You can find guides for different upgrade scenarios in the docs:
https://pris.ly/d/upgrade-from-prisma-1

5.3. 最終結果

Prismaスキーマの最終バージョンは以下のようになるはずです。

schema.prisma
model User {
id String @id @default(cuid())
name String
email String? @unique
jsonData Json?
role Role @default(CUSTOMER)
Post Post[]
Profile Profile?
}

model Post {
id String @id @default(cuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
title String
content String?
published Boolean @default(false)
authorId String?
User User? @relation(fields: [authorId], references: [id])
Category Category[] @relation(references: [id])
}

model Profile {
id String @id @default(cuid())
bio String?
user String? @unique
User User? @relation(fields: [user], references: [id])
}

model Category {
id String @id @default(cuid())
name String
Post Post[] @relation(references: [id])
}

enum Role {
ADMIN
CUSTOMER
}

5.4. リレーションフィールドの名前変更

このバージョンのPrisma ORM 2スキーマで気づくことの1つは、すべてのリレーションフィールドがそれぞれのモデルにちなんで名付けられていることです(例:)。

schema.prisma
model User {
Post Post[]
Profile Profile?
}

model Post {
User User? @relation(fields: [authorId], references: [id])
Category Category[] @relation(references: [id])
}

model Profile {
User User? @relation(fields: [user], references: [id])
}

model Category {
Post Post[] @relation(references: [id])
}

これは理想的ではないため、実際にはそれらすべてを以前のバージョンに手動で名前変更できます。

すべてのリレーションフィールドは仮想的であり、データベースには現れないため、好きなように名前を付けることができます。この場合、すべてのリレーションフィールドは小文字になり、場合によっては複数形になります。

名前変更後の状態は以下のとおりです。

schema.prisma
model User {
posts Post[]
profile Profile?
}

model Post {
author User? @relation(fields: [authorId], references: [id])
categories Category[] @relation(references: [id])
}

model Profile {
user String? @unique
owner User? @relation(fields: [user], references: [id])
}

model Category {
posts Post[] @relation(references: [id])
}

注意UserProfile間の1対1リレーションの場合、リレーションフィールドに古い名前userを設定することはできませんでした。これは、既存の外部キーを持つリレーションスカラーフィールドとの名前の競合が発生するためです。その場合、別の名前を選択するか、SQLを介してデータベースで外部キー列の名前を直接変更することもできます。

5.5. 残りのスキーマ非互換性の解決

Upgrade CLIによってまだ解決されていないスキーマの非互換性がいくつかあります。この時点では、スカラリストはまだ修正されていません。これやその他の推奨される回避策は、スキーマの非互換性のページで見つけることができます。

6. Prisma Clientのインストールと生成

Prisma ORM 2スキーマの準備ができたので、以下のコマンドでPrisma Clientをインストールできます。

npm install @prisma/client

7. 次のステップ

おめでとうございます。これでPrisma ORMレイヤーをPrisma ORM 2にアップグレードしました。ここからは、以下のガイドのいずれかを使用してアプリケーションコードを更新できます。

  • 旧Nexusから新Nexusへ:現在Prisma 1とGraphQL Nexusを実行している場合は、このガイドを選択してください。
  • prisma-bindingからNexusへ:現在Prisma 1とprisma-bindingを実行しており、Nexus(およびTypeScript)にアップグレードしたい場合は、このガイドを選択してください。
  • prisma-bindingからSDL-firstへ:現在Prisma 1とprisma-bindingを実行しており、SDL-first GraphQLサーバーにアップグレードしたい場合は、このガイドを選択してください。
  • REST API:現在Prisma Client 1を使用してPrisma 1を実行しており、REST APIを構築している場合は、このガイドを選択してください。

ボーナス:Prisma Client APIの比較

このセクションには、Prisma 1とPrisma ORM 2のPrisma Client APIの概要と並列比較が含まれています。新しいPrisma Client APIの詳細については、Prisma Clientドキュメントをご覧ください。

単一レコードの読み取り

Prisma Client (v1)
const user = await prisma.user({ id: 1 })
Prisma Client (v2)
await prisma.user.findUnique({
where: { id: 1 },
})

レコードリストの読み取り

Prisma Client (v1)
const user = await prisma.users()
Prisma Client (v2)
await prisma.user.findMany()

リストのフィルタリング

Prisma Client (v1)
const users = await prisma.users({
where: {
name: 'Alice',
},
})
Prisma Client (v2)
await prisma.user.findMany({
where: {
name: 'Alice',
},
})

リストのページネーション

Prisma Client (v1)
const posts = await prisma.posts({
skip: 5,
first: 10,
})
Prisma Client (v2)
await prisma.user.findMany({
skip: 5,
take: 10,
})

注意:新しいページネーションAPIの詳細については、それぞれのリリースノートまたはドキュメントのページネーションページをご覧ください。

リストのソート

Prisma Client (v1)
await prisma.posts({
orderBy: 'title_ASC',
})
Prisma Client (v2)
await prisma.posts({
orderBy: {
title: 'asc',
},
})

レコードの作成

Prisma Client (v1)
await prisma.createUser({
name: 'Alice',
})
Prisma Client (v2)
await prisma.user.create({
data: {
name: 'Alice',
},
})

レコードの更新

Prisma Client (v1)
await prisma.updateUser({
where: { id: 1 },
data: {
name: 'James',
email: 'james@prisma.io',
},
})
Prisma Client (v2)
await prisma.user.update({
where: { id: 1 },
data: {
name: 'James',
email: 'james@prisma.io',
},
})

レコードの削除

Prisma Client (v1)
await prisma.deleteUser({ id: 1 })
Prisma Client (v2)
await prisma.user.delete({
where: { id: 1 },
})

フィールドの選択とリレーションの読み込み

Prisma 1では、特定のフィールドを選択したり、オブジェクトのリレーションを読み込んだりする唯一の方法は、文字列ベースの$fragment関数と$graphql関数を使用することでした。Prisma ORM 2では、selectincludeを使用して、クリーンで型安全な方法でこれを行うことができます。

このアプローチのもう1つの利点は、selectinclude任意のPrisma Clientクエリ(例: findUnique()findManycreateupdatedeleteなど)で使用できることです。

Prisma Client (v1)
await prisma.user({ id: 1 }).$fragment(`
fragment NameAndEmail on User { id email }`
`)
Prisma Client (v2)
await prisma.user.findUnique({
where: { id: 1 },
select: {
id: true,
email: true,
},
})

例として、新しいレコードを作成し、返されたオブジェクトでidのみを取得することはPrisma 1では不可能でした。Prisma ORM 2では、次のようにしてこれを実現できます。

await prisma.user.create({
data: {
name: 'Alice',
},
select: {
id: true,
},
})
© . All rights reserved.