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

DrizzleからPrisma ORMへの移行方法

15分

はじめに

このガイドでは、DrizzleからPrisma ORMにアプリケーションを移行する方法を説明します。Drizzle Next.jsのサンプルをベースにしたサンプルプロジェクトを使用して、移行手順を説明します。このガイドで使用されているサンプルはGitHubで確認できます。

Prisma ORM vs Drizzleページで、Prisma ORMとDrizzleの比較について学ぶことができます。

前提条件

このガイドを始める前に、以下を確認してください。

  • 移行したいDrizzleプロジェクト
  • Node.jsがインストールされていること (バージョン16以上)
  • PostgreSQLまたはその他のサポートされているデータベース
  • DrizzleとNext.jsの基本的な知識
注意

この移行ガイドでは、Neon PostgreSQLを例のデータベースとして使用していますが、Prisma ORMでサポートされている他のリレーショナルデータベースにも同様に適用できます。

Prisma ORM vs Drizzleページで、Prisma ORMとDrizzleの比較について学ぶことができます。

移行プロセスの概要

DrizzleからPrisma ORMへの移行手順は、どのような種類のアプリケーションやAPIレイヤーを構築しているかに関わらず、常に同じであることに注意してください。

  1. Prisma CLIのインストール
  2. データベースのイントロスペクション
  3. ベースラインマイグレーションの作成
  4. Prisma Clientのインストール
  5. DrizzleのクエリをPrisma Clientで段階的に置き換える

これらの手順は、REST API(Express、koa、NestJSなど)、GraphQL API(Apollo Server、TypeGraphQL、Nexusなど)、またはDrizzleをデータベースアクセスに使用するその他の種類のアプリケーションを構築しているかどうかに関わらず適用されます。

Prisma ORMは**段階的な導入**に非常に適しています。これは、プロジェクト全体をDrizzleからPrisma ORMに一度に移行する必要はなく、データベースクエリをDrizzleからPrisma ORMへ**段階的に**移行できることを意味します。

ステップ1. Prisma CLIのインストール

Prisma ORMを導入する最初のステップは、プロジェクトにPrisma CLIをインストールすることです。

npm install prisma --save-dev && npm install @prisma/client

ステップ2. データベースのイントロスペクション

2.1. Prisma ORMのセットアップ

データベースをイントロスペクトする前に、Prismaスキーマを設定し、Prismaをデータベースに接続する必要があります。プロジェクトのルートで以下のコマンドを実行して、基本的なPrismaスキーマファイルを作成します。

npx prisma init --output ../generated/prisma

このコマンドは、`prisma`という新しいディレクトリを以下のファイルとともに作成します。

  • `schema.prisma`: データベース接続とモデルを指定するPrismaスキーマ
  • `.env`: データベース接続URLを環境変数として設定するための`dotenv`ファイル
注意

すでに`.env`ファイルがあるかもしれません。その場合、`prisma init`コマンドは新しいファイルを作成するのではなく、行を追加します。

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

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"
}
ヒント

VS Codeを使用している場合は、構文のハイライト表示、フォーマット、自動補完、その他多くの便利な機能のためにPrisma VS Code拡張機能をインストールしてください。

2.2. データベースへの接続

PostgreSQLを使用していない場合は、`datasource`ブロックの`provider`フィールドを現在使用しているデータベースに合わせて調整する必要があります。

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

これが完了したら、`.env`ファイルでデータベース接続URLを設定できます。DrizzleとPrisma ORMは接続URLに同じ形式を使用するため、既存の接続URLで問題なく動作するはずです。

2.3. Prisma ORMを使用したデータベースのイントロスペクション

接続URLが設定されたら、データベースをイントロスペクトしてPrismaモデルを生成できます。

npx prisma db pull

サンプルプロジェクトを使用している場合、以下のモデルが作成されます。

prisma/schema.prisma
model todo {
id Int @id
text String
done Boolean @default(false)
}

生成されたPrismaモデルはデータベーステーブルを表します。Prismaモデルは、データベースにクエリを送信できるプログラム的なPrisma Client APIの基盤となります。

2.4. ベースラインマイグレーションの作成

Prisma Migrateを継続して使用してデータベーススキーマを進化させるには、データベースのベースラインを設定する必要があります。

まず、`migrations`ディレクトリを作成し、その中にマイグレーションに好きな名前のディレクトリを追加します。この例では、`0_init`をマイグレーション名として使用します。

mkdir -p prisma/migrations/0_init

次に、`prisma migrate diff`コマンドでマイグレーションファイルを生成します。以下の引数を使用します。

  • `--from-empty`: 移行元のデータモデルが空であると仮定します。
  • `--to-schema-datamodel`: `datasource`ブロック内のURLを使用して現在のデータベース状態を指します。
  • `--script`: SQLスクリプトを出力します。
npx prisma migrate diff --from-empty --to-schema-datamodel prisma/schema.prisma --script > prisma/migrations/0_init/migration.sql

生成されたマイグレーションを確認し、すべてが正しいことを確認します。

次に、`prisma migrate resolve`コマンドに`--applied`引数を使用して、マイグレーションが適用済みとしてマークします。

npx prisma migrate resolve --applied 0_init

このコマンドは、`0_init`を`_prisma_migrations`テーブルに追加することで、適用済みとしてマークします。

これで、現在のデータベーススキーマのベースラインができました。データベーススキーマにさらに変更を加えるには、Prismaスキーマを更新し、`prisma migrate dev`を使用して変更をデータベースに適用できます。

2.5. Prismaスキーマの調整 (オプション)

イントロスペクションによって生成されたモデルは、現在、データベーステーブルに**正確に**マッピングされます。このセクションでは、Prisma ORMの命名規則に準拠するようにPrismaモデルの命名を調整する方法を学びます。

これらの調整はすべて完全にオプションであり、現時点では何も調整したくない場合は、次のステップに進んでも構いません。後でいつでも戻って調整を行うことができます。

Drizzleモデルの現在のcamelCase表記とは対照的に、Prisma ORMの命名規則は以下のとおりです。

  • モデル名にはPascalCase
  • フィールド名にはcamelCase

`@@map`と`@map`を使用して、Prismaモデルとフィールド名を基礎となるデータベースの既存のテーブル名とカラム名に**マッピング**することで、命名を調整できます。

上記のモデルをどのように変更できるかの例を次に示します。

prisma/schema.prisma
model Todo {
id Int @id
text String
done Boolean @default(false)

@@map("todo")
}

ステップ3. Prisma Clientのインストールと生成

次のステップとして、プロジェクトにPrisma Clientをインストールし、現在Drizzleで行われているデータベースクエリの置き換えを開始できます。

npm install @prisma/client

インストール後、スキーマがTypeScriptの型と自動補完に反映されるように、`generate`を実行する必要があります。

npx prisma generate

ステップ4. DrizzleのクエリをPrisma Clientで置き換える

このセクションでは、サンプルREST APIプロジェクトの例のルートに基づいて、DrizzleからPrisma Clientに移行されているいくつかのサンプルクエリを示します。Prisma Client APIがDrizzleとどのように異なるかについての包括的な概要は、比較ページをご覧ください。

まず、様々なルートハンドラーからデータベースクエリを送信するために使用する`PrismaClient`インスタンスを設定します。`db`ディレクトリに`prisma.ts`という新しいファイルを作成します。

touch db/prisma.ts

次に、`PrismaClient`をインスタンス化し、後でルートハンドラーで使用できるようにファイルからエクスポートします。

db/prisma.ts
import { PrismaClient } from '@prisma/client'

export const prisma = new PrismaClient()

4.1. `getData`クエリの置き換え

フルスタックNext.jsアプリには、`getData`を含むいくつかの`actions`があります。

`getData`アクションは現在、以下のように実装されています。

actions/todoActions.ts
import db from "@/db/drizzle";
import { todo } from "@/db/schema";

export const getData = async () => {
const data = await db.select().from(todo);
return data;
};

ここに、Prisma Clientを使用して実装された同じアクションを示します。

src/controllers/FeedAction.ts
import { prisma } from "@/db/prisma";

export const getData = async () => {
const data = await prisma.todo.findMany();
return data;
};

4.2. `POST`リクエスト内のクエリの置き換え

サンプルプロジェクトには、`POST`リクエスト中に利用される4つのアクションがあります。

  • `addTodo`: 新しい`Todo`レコードを作成します。
  • `deleteTodo`: 既存の`Todo`レコードを削除します。
  • `toggleTodo`: 既存の`Todo`レコードのブール型`done`フィールドを切り替えます。
  • `editTodo`: 既存の`Todo`レコードの`text`フィールドを編集します。

`addTodo`

`addTodo`アクションは現在、以下のように実装されています。

actions/todoActions.ts
import { revalidatePath } from "next/cache";

import db from "@/db/drizzle";
import { todo } from "@/db/schema";

export const addTodo = async (id: number, text: string) => {
await db.insert(todo).values({
id: id,
text: text,
});
revalidatePath("/");
};

ここに、Prisma Clientを使用して実装された同じアクションを示します。

actions/todoActions.ts
import { revalidatePath } from "next/cache";

import { prisma } from "@/db/prisma";

export const addTodo = async (id: number, text: string) => {
await prisma.todo.create({
data: { id, text },
})
revalidatePath("/");
};

`deleteTodo`

`deleteTodo`アクションは現在、以下のように実装されています。

actions/todoActions.ts
import { eq } from "drizzle-orm";
import { revalidatePath } from "next/cache";

import db from "@/db/drizzle";
import { todo } from "@/db/schema";

export const deleteTodo = async (id: number) => {
await db.delete(todo).where(eq(todo.id, id));
revalidatePath("/");
};

ここに、Prisma Clientを使用して実装された同じアクションを示します。

actions/todoActions.ts
import { revalidatePath } from "next/cache";

import { prisma } from "@/db/prisma";

export const deleteTodo = async (id: number) => {
await prisma.todo.delete({ where: { id } });
revalidatePath("/");
};

`toggleTodo`

`ToggleTodo`アクションは現在、以下のように実装されています。

actions/todoActions.ts
import { eq, not } from "drizzle-orm";
import { revalidatePath } from "next/cache";

import db from "@/db/drizzle";
import { todo } from "@/db/schema";

export const toggleTodo = async (id: number) => {
await db
.update(todo)
.set({
done: not(todo.done),
})
.where(eq(todo.id, id));
revalidatePath("/");
};

ここに、Prisma Clientを使用して実装された同じアクションを示します。

actions/todoActions.ts
import { revalidatePath } from "next/cache";

import { prisma } from "@/db/prisma";

export const toggleTodo = async (id: number) => {
const todo = await prisma.todo.findUnique({ where: { id } });
if (todo) {
await prisma.todo.update({
where: { id: todo.id },
data: { done: !todo.done },
})
revalidatePath("/");
}
};

Prisma ORMにはブール型フィールドを「インプレース」で編集する機能がないため、事前にレコードをフェッチする必要があることに注意してください。

`editTodo`

`editTodo`アクションは現在、以下のように実装されています。

actions/todoActions.ts
import { eq } from "drizzle-orm";
import { revalidatePath } from "next/cache";

import db from "@/db/drizzle";
import { todo } from "@/db/schema";

export const editTodo = async (id: number, text: string) => {
await db
.update(todo)
.set({
text: text,
})
.where(eq(todo.id, id));

revalidatePath("/");
};

ここに、Prisma Clientを使用して実装された同じアクションを示します。

actions/todoActions.ts
import { revalidatePath } from "next/cache";

import { prisma } from "@/db/prisma";

export const editTodo = async (id: number, text: string) => {
await prisma.todo.update({
where: { id },
data: { text },
})
revalidatePath("/");
};

その他

暗黙的な多対多リレーション

Drizzleとは異なり、Prisma ORMでは多対多リレーションを**暗黙的に**モデル化できます。つまり、スキーマ内でリレーションテーブル(またはJOINテーブルとも呼ばれます)を**明示的に**管理する必要がない多対多リレーションです。DrizzleとPrisma ORMを比較した例を次に示します。

schema.ts
import { boolean, integer, pgTable, serial, text } from "drizzle-orm/pg-core";

export const posts = pgTable('post', {
id: serial('serial').primaryKey(),
title: text('title').notNull(),
content: text('content'),
published: boolean('published').default(false).notNull(),
});

export const categories = pgTable('category', {
id: serial('serial').primaryKey(),
name: text('name').notNull(),
});

export const postsToCategories = pgTable('posts_to_categories', {
postId: integer('post_id').notNull().references(() => users.id),
categoryId: integer('category_id').notNull().references(() => chatGroups.id),
});

このスキーマは、以下のPrismaスキーマと同等です。

schema.prisma
model Post {
id Int @id @default(autoincrement())
title String
content String?
published Boolean @default(false)
postsToCategories PostToCategories[]

@@map("post")
}

model Category {
id Int @id @default(autoincrement())
name String
postsToCategories PostToCategories[]

@@map("category")
}

model PostToCategories {
postId Int
categoryId Int
category Category @relation(fields: [categoryId], references: [id])
post Post @relation(fields: [postId], references: [id])

@@id([postId, categoryId])
@@index([postId])
@@index([categoryId])
@@map("posts_to_categories")
}

このPrismaスキーマでは、多対多リレーションはリレーションテーブル`PostToCategories`を介して**明示的に**モデル化されています。

代わりにPrisma ORMのリレーションテーブルの規約に従うと、リレーションは以下のようになります。

schema.prisma
model Post {
id Int @id @default(autoincrement())
title String
content String?
published Boolean @default(false)
categories Category[]
}

model Category {
id Int @id @default(autoincrement())
name String
posts Post[]
}

これにより、`PostToCategories`モデルをまず横断する必要がなく、`Post`から`Category`へ(そしてその逆も)直接パスがあるため、このリレーションのレコードを変更するためのPrisma Client APIがより人間工学的に優れ、冗長でなくなります。

警告

データベースプロバイダーがテーブルにプライマリキーを要求する場合、明示的な構文を使用し、プライマリキーを持つ結合モデルを手動で作成する必要があります。これは、Prisma ORM(`@relation`で表現される)によって暗黙的な構文で作成される多対多リレーションのリレーションテーブル(結合テーブル)にはプライマリキーがないためです。


Prismaとつながり続ける

Prismaの旅を続けるには、以下とつながりましょう 私たちの活発なコミュニティ。情報を入手し、参加し、他の開発者と協力しましょう

  • Xでフォローする アナウンス、ライブイベント、役立つヒントについて。
  • Discordに参加する 質問したり、コミュニティと話したり、会話を通じて積極的にサポートを受けたりするために。
  • YouTubeを購読する チュートリアル、デモ、ストリームについて。
  • GitHubで関わる リポジトリをスターしたり、問題を報告したり、問題に貢献したりすることで。
皆様の参加を心から歓迎し、コミュニティの一員となることを楽しみにしています!

© . All rights reserved.