DrizzleからPrisma ORMへの移行方法
はじめに
このガイドでは、アプリケーションをDrizzleからPrisma ORMに移行する方法を説明します。移行手順を示すために、Drizzle Next.jsの例に基づいたサンプルプロジェクトを使用します。このガイドで使用されている例はGitHubにあります。
Prisma ORMとDrizzleの比較については、Prisma ORM vs Drizzleのページをご覧ください。
前提条件
このガイドを開始する前に、以下を確認してください
- 移行したいDrizzleプロジェクト
- Node.js がインストールされていること(バージョン16以上)
- PostgreSQLまたはその他のサポートされているデータベース
- DrizzleとNext.jsの基本的な知識
この移行ガイドでは、Neon PostgreSQLを例示データベースとして使用していますが、Prisma ORMでサポートされている他のリレーショナルデータベースにも同様に適用できます。
Prisma ORMとDrizzleの比較については、Prisma ORM vs Drizzleのページをご覧ください。
移行プロセスの概要
DrizzleからPrisma ORMへの移行手順は、アプリケーションやAPIレイヤーの種類に関わらず常に同じであることに注意してください
- Prisma CLIをインストールする
- データベースをイントロスペクションする
- ベースライン移行を作成する
- Prisma Clientをインストールする
- 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
ステップ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スキーマは現在次のようになっています
// 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
フィールドを現在使用しているデータベースに合わせて調整する必要があります
- PostgreSQL
- MySQL
- Microsoft SQL Server
- SQLite
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
datasource db {
provider = "mysql"
url = env("DATABASE_URL")
}
datasource db {
provider = "sqlserver"
url = env("DATABASE_URL")
}
datasource db {
provider = "sqlite"
url = env("DATABASE_URL")
}
それが完了したら、.env
ファイルでデータベース接続URLを設定できます。DrizzleとPrisma ORMは接続URLに同じ形式を使用しているため、既存の接続URLは問題なく動作するはずです。
2.3. Prisma ORMを使用してデータベースをイントロスペクションする
接続URLが設定されたら、データベースをイントロスペクションしてPrismaモデルを生成できます
npx prisma db pull
サンプルプロジェクトを使用している場合、次のモデルが作成されます
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
このコマンドは、_prisma_migrations
テーブルに追加することにより、0_init
を適用済みとしてマークします。
これで、現在のデータベーススキーマのベースラインができました。データベーススキーマにさらに変更を加えるには、Prismaスキーマを更新し、prisma migrate dev
を使用して変更をデータベースに適用できます。
2.5. Prismaスキーマを調整する(オプション)
イントロスペクションによって生成されたモデルは、現在、データベーステーブルに正確にマッピングされています。このセクションでは、Prismaモデルの名前を調整して、Prisma ORMの命名規則に準拠させる方法を学びます。
これらの調整はすべて完全にオプションであり、今のところ何も調整したくない場合は、次のステップにスキップしてもかまいません。後でいつでも戻って調整を行うことができます。
Drizzleモデルの現在のcamelCase表記とは対照的に、Prisma ORMの命名規則は次のとおりです
- モデル名にはPascalCase
- フィールド名にはcamelCase
@@map
および@map
を使用して、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
をインスタンス化し、後でルートハンドラーで使用できるようにファイルからエクスポートします
import { PrismaClient } from '@prisma/client'
export const prisma = new PrismaClient()
4.1. getDataクエリの置き換え
フルスタックNext.jsアプリには、getData
を含むいくつかのアクションがあります。
getData
アクションは現在次のように実装されています
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を使用して実装された同じアクションを次に示します
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
アクションは現在次のように実装されています
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を使用して実装された同じアクションを次に示します
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
アクションは現在次のように実装されています
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を使用して実装された同じアクションを次に示します
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
アクションは現在次のように実装されています
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を使用して実装された同じアクションを次に示します
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
アクションは現在次のように実装されています
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を使用して実装された同じアクションを次に示します
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を比較した例を次に示します
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スキーマと同等です
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リレーションテーブルの規則に従うことで、リレーションは次のようになります
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[]
}
これにより、このリレーションのレコードを変更するための、より人間工学的で冗長性の少ないPrisma Client APIも実現します。PostToCategories
モデルを最初にトラバースする必要がなく、Post
からCategory
への直接パス(およびその逆)があるためです。
データベースプロバイダーがテーブルに主キーを要求する場合、明示的な構文を使用し、主キーを持つ結合モデルを手動で作成する必要があります。これは、暗黙的な構文を使用して多対多リレーション用にPrisma ORM(@relation
を介して表現)によって作成されたリレーションテーブル(JOINテーブル)には主キーがないためです。
Prismaとのつながりを保つ
以下とつながってPrismaの旅を続けましょう 活発なコミュニティ。常に情報を入手し、参加し、他の開発者と協力しましょう
- Xでフォローする お知らせ、ライブイベント、役立つヒントをお届けします。
- Discordに参加する 質問をしたり、コミュニティと話したり、会話を通じて積極的なサポートを得ることができます。
- YouTubeで購読する チュートリアル、デモ、ストリームをお届けします。
- GitHubで交流する リポジトリにスターを付けたり、問題を報告したり、問題に貢献したりできます。