
【Prisma入門】ORMとSQLの違いと4つの利点・4つの注意点
この記事は10分で読めます
こんにちは。今日も記事をご覧いただきありがとうございます。
今回は、Prisma ORM の基礎を初心者向けにまとめました。
prisma ORMとはNode.jsおよびTypeScript環境で使われる、型安全を意識した ORM です。
ORMとSQLは何が違うのかピンとこない。そんな方も多いのではないでしょうか。
この記事ではprisma ORMを例として利点やSQLとの違い、prismaの注意点などを解説していきます。
この記事で分かること:
- Prisma の主な利点
- ORM とは何か、SQL との違い
- 使うときに気をつけたいポイント
Prisma の利点
スキーマファイルで一元管理
Prisma では、schema.prisma というファイルにスキーマを書きます。スキーマとは、データベースにどんなテーブルや項目があるかを定義した「設計図」のことです。
model User {
id Int @id @default(autoincrement())
name String
email String @unique
createdAt DateTime @default(now())
posts Post[]
}
model Post {
id Int @id @default(autoincrement())
title String
content String?
authorId Int
author User @relation(fields: [authorId], references: [id])
}
String? の「?」は「空(null)でもよい」という意味です。@unique は重複を許さない項目、@relation はテーブル同士のつながりを表しています。
テーブルの構造がこのファイル1つにまとまるので、「今どんなデータを持っているのか」をすぐに確認できます。
なお、ファイルの先頭には「どの DB につなぐか(datasource)」と「どんなクライアントを生成するか(generator)」の設定も書きます。この部分の書き方はバージョンによって変わっているので、公式ドキュメントの手順に合わせて設定してください。
型安全な Prisma Client
Prisma Client は、スキーマをもとに自動生成される「データベース操作用のクライアント(DB にリクエストを送るためのライブラリ)」です。
大きな魅力は型安全であることです。型安全とは、データの型の食い違いをプログラムの実行前に見つけられる状態のことです。
const user = await prisma.user.findUnique({
where: { email: "[email protected]" },
});
// user の型は「User または null」として自動で推論される
console.log(user?.name);
// スキーマにない項目を書くと、実行前にエディタが教えてくれる
// prisma.user.findMany({ where: { nickname: "taro" } });
エディタでは項目名が補完されるので、カラム名のタイプミスにも早めに気づけます。
マイグレーションで DB の変更履歴を管理
マイグレーションとは、テーブルの追加や項目の変更といった DB 構造の変化を、ファイルとして記録しながら反映していく仕組みです。
npx prisma migrate dev --name add_post_table
このコマンドを実行すると、prisma/migrations フォルダに変更内容の SQL ファイルが作られ、開発用の DB に反映されます。
このファイルを Git で管理しておけば、「いつ、どんな変更をしたか」をチームで共有できます。
Prisma Studio でデータを画面から確認できる
npx prisma studio
このコマンドで Prisma Studio が起動し、ブラウザ上でテーブルの中身を表形式で見たり、編集したりできます。
SQL を書かずにデータを確認できるので、動作確認のときにとても便利です。
ORM とは?SQL との違い
ORM(Object-Relational Mapping)は、データベースのテーブルとプログラムのオブジェクトを対応づける仕組みのことです。
たとえるなら、ORM は「データベースとプログラムの間に立つ通訳さん」のような存在です。
データベースはデータを表(テーブル)の形で持ち、TypeScript はデータをオブジェクトとして扱います。ORM がこの2つの間を取り持ってくれるので、テーブルの1行を「id や name を持つ1つのオブジェクト」として自然に扱えるようになります。
SQL と Prisma で書き比べてみる
まずは SQL で、ユーザー一覧の取得と条件付きの取得を書いてみます(テーブル名やカラム名は説明用の例です)。
-- ユーザー一覧を取得
SELECT * FROM users;
-- メールアドレスが @example.com で終わるユーザーを新しい順に取得
SELECT id, name, email
FROM users
WHERE email LIKE '%@example.com'
ORDER BY created_at DESC;
同じ処理を Prisma で書くと、次のようになります。
// ユーザー一覧を取得
const users = await prisma.user.findMany();
// メールアドレスが @example.com で終わるユーザーを新しい順に取得
const filtered = await prisma.user.findMany({
where: { email: { endsWith: "@example.com" } },
select: { id: true, name: true, email: true },
orderBy: { createdAt: "desc" },
});
SQL を文字列で書くのではなく、TypeScript のメソッドとオブジェクトで条件を組み立てているのがポイントです。
違いを表で整理
| 観点 | SQL を直接書く | Prisma |
|---|---|---|
| 書き方 | SQL 文を文字列で書く | TypeScript のメソッドで書く |
| 型の補完 | 基本的には効かない | カラム名などが補完される |
| 結果の型 | 自分で型を付ける | 自動で型が付く |
| DB ごとの違い | 方言を意識して書く | 多くを Prisma が吸収 |
| 細かい制御 | 自由度が高い | 必要なら生 SQL を併用 |
ここで1つお伝えしたいのは、ORM は SQL の代わりではないということです。
Prisma も裏側では最終的に SQL を実行しています。そのため、SQL の知識があるほど「このコードはどんな SQL になるのかな」とイメージしやすくなり、より上手に使いこなせるようになります。
使うときに気をつけたいポイント
スキーマを変えたら prisma generate
ここがいちばん大事なポイントです。
先ほどお伝えしたとおり、Prisma Client はスキーマをもとに自動生成されるコードです。
そのため、schema.prisma を書き換えただけでは、生成済みのクライアントは古いままになります。すると、次のようなことが起こります。
- 新しい項目が補完に出てこない
- 追加したはずの項目で型エラーになる
スキーマを変更したら、次のコマンドでクライアントを作り直しましょう。
npx prisma generate
おすすめの流れは次のとおりです。
- schema.prisma を編集する
prisma migrate devで DB に反映するprisma generateでクライアントを更新する
バージョンによっては migrate dev のときに generate も自動で実行されますが、自動では実行されないバージョンもあります。挙動はバージョンによって異なる場合があるので、公式ドキュメントも確認しつつ、迷ったら自分で generate を実行しておくと安心です。
それでもエディタの補完が更新されないときは、エディタの TypeScript サーバーを再起動すると反映されることがあります。
開発と本番でマイグレーションのコマンドを使い分ける
マイグレーションのコマンドは、環境に合わせて使い分けます。
- migrate dev: 開発用。マイグレーションファイルの作成と反映を行う
- migrate deploy: 本番用。作成済みのマイグレーションを反映するだけ
それぞれのコマンドは次のとおりです。
# 開発環境
npx prisma migrate dev --name add_profile
# 本番環境(作成済みのマイグレーションを反映)
npx prisma migrate deploy
migrate dev は、状況によって DB のリセット(データの削除)を提案してくることがあります。本番のデータを守るためにも、本番では migrate deploy を使うようにしましょう。
複雑なクエリは生 SQL も選択肢
集計などの複雑なクエリは、Prisma のメソッドだけでは書きにくいこともあります。そんなときは、生 SQL(ORM を通さずにそのまま書く SQL)を使う方法もあります。
const minCount = 5;
const result = await prisma.$queryRaw`
SELECT author_id, COUNT(*) AS post_count
FROM posts
GROUP BY author_id
HAVING COUNT(*) >= ${minCount}
`;
$queryRaw に埋め込んだ値は自動でパラメータとして扱われるので、SQL インジェクション(入力値を悪用して意図しない SQL を実行させる攻撃)への対策になります。
文字列をつなげて SQL を組み立てる書き方は避けておきましょう。
PrismaClient のインスタンスは使い回す
PrismaClient は、内部で DB との接続をまとめて管理しています。処理のたびに new すると接続がどんどん増えて、DB の接続数の上限に達してしまうことがあります。
特に Next.js の開発環境では、ホットリロード(ファイルを保存するたびにコードを読み込み直す機能)のたびに new されやすいため、グローバルに保持するパターンがよく使われます。
// lib/prisma.ts
import { PrismaClient } from "@prisma/client";
const globalForPrisma = globalThis as unknown as {
prisma: PrismaClient | undefined;
};
export const prisma = globalForPrisma.prisma ?? new PrismaClient();
if (process.env.NODE_ENV !== "production") {
globalForPrisma.prisma = prisma;
}
あとはこのファイルから prisma を import して使い回します。
なお、import 元のパスやコンストラクタに渡す設定は、バージョンや generator の設定によって異なる場合があります。公式ドキュメントもあわせて確認してください。
まとめ
今回のポイントを振り返ります。
- ORM は SQL の代わりではなく、DB 操作を型安全に書くための道具
- Prisma は schema.prisma を中心に開発を進められる
- スキーマを変えたら prisma generate
- 本番では migrate deploy を使う
- 複雑なクエリは生 SQL も選択肢
- PrismaClient は使い回す
最初は覚えることが多く感じるかもしれませんが、「スキーマを書いて、反映して、generate する」という流れに慣れれば、ぐっと扱いやすくなるはずです。
SQL の知識も活かしながら、少しずつ Prisma に親しんでいってもらえたらうれしいです。
記事を最後まで読んでいただきありがとうございました。また次の記事でお会いしましょう。
