Webアプリケーション開発において欠かせないデータベース操作。SQLを直接書く代わりに「ORM(Object-Relational Mapping)」を使うことで、開発効率や保守性を劇的に向上させることができます。
本記事では、現代のTypeScript開発において圧倒的な人気を誇るモダンORM「Prisma(プリズマ)」について、特徴やメリット・デメリット、そして実際のサンプルコードまで詳しく解説します。
Neonについては以下の記事で入門記事を書いていますのでぜひ参考にしてください。
また、Next.jsの始め方は以下の記事から連載していますので参考にしてください。
1. ORM(オブジェクトリレーショナルマッピング)とは?
ORMとは、プログラムで扱う「オブジェクト」と、データベースの「リレーショナルテーブル」を自動的に変換してくれる仕組み、およびそのツールのことです。
通常、プログラムとデータベースの間ではデータの持ち方が異なりますが、ORMを使うことで以下のようなメリットが生まれます。
- SQLを直接書かずに、直感的なプログラムコード(メソッド)だけでCRUD操作ができる
- データベースの種類(PostgreSQL、MySQL、SQLiteなど)を変更しやすい
- コードの保守性が上がる

2. TypeScriptにおける代表的なORMの紹介
TypeScriptエコシステムでは、主に以下の2つのORMが主流として使われています。
- Prisma(プリズマ)
- 特徴: 独自のスキーマファイルによる高い型安全性、直感的なリレーション操作、公式GUI(Prisma Studio)など、圧倒的な開発体験(DX)を誇るモダンORM。
- Drizzle ORM(ドリズル)
- 特徴: SQLに近い書き心地を重視した、非常に軽量で高速なTypeScriptファーストのORM。余計なコード生成ステップがないのが魅力。
3. Prisma(プリズマ)の特徴とは?
Prismaが多くの開発者に選ばれている理由は、主に以下の3つの要素に集約されます。
- Prisma Schema (schema.prisma):
データ構造(テーブルやリレーション)を一元管理する設計図ファイル。 - Prisma Client:
スキーマから自動生成される、完全型安全なクエリビルダー。存在しないカラム名を指定すると即座にエディタがエラーを教えてくれます。 - Prisma Migrate:
データベースの構造変更(マイグレーション)を安全かつ簡単に管理できる仕組み。
4. Prismaのメリット・デメリット
メリット
- 圧倒的な型安全性: TypeScriptとの相性が抜群で、補完が強力。
- リレーションが直感的: 複数テーブルの結合(JOIN)や同時作成(ネスト作成)がコードベースで簡単に書ける。
- Prisma Studio: コマンド一つでリッチなブラウザ上のDBビュワーが立ち上がり、データの確認・編集が容易。
デメリット
- オーバーヘッド: 超軽量なクエリビルダ(Drizzleなど)に比べると、パフォーマンス面でわずかにオーバーヘッドがある。
- 独自構文の学習コスト: schema.prisma の独自の記述ルールを覚える必要がある。
5. テストプロジェクトを作成
今回はprismaを使ってデータベースのNeonと接続して、Next.jsで表示するテストプロジェクトを作成します。
以下のステップで進めていきます。
- Neonデータベース作成、接続情報の取得
- Next.jsプロジェクトの作成とprismaの初期設定
- Prismaの初期設定とスキーマ定義(ユーザー名、メールアドレス、役職/ロール)
- シードデータ(初期データ)の投入
- Next.jsプロジェクトの作成とダッシュボード画面でのデータベース表示実装
ステップ1:Neonデータベースの作成と接続情報の取得
お使いの環境(ターミナルなど)で、NeonのCLIツール(neonctl)を使ってデータベースを作成し、接続URLを取得します。
まだログインしていない場合は、Neonへログインします。
npx neonctl authプロジェクトを作成します(例としてプロジェクト名を prisma-test-db とします)。
npx neonctl projects create --name prisma-test-db接続文字列(Connection String)を取得します。コマンドの出力結果や以下のコマンドで得られる postgresql://… から始まるURLをコピーしておいてください。
npx neonctl connection-stringまた、お使いのバージョンによってはプロジェクトルートに.env.localが生成されると思いますので、それを今後使用していきます。
ステップ2:Next.jsプロジェクトの作成とprismaの初期設定
次に、ローカル環境でNext.jsプロジェクトを作成し、Prismaを組み込みます。
- Next.jsプロジェクトの作成(App Router、TypeScript、Tailwind CSS等を選択してください)
npx create-next-app@latest prisma-dashboard-test cd prisma-dashboard-test- PrismaおよびPrisma Clientのインストール
# Next.jsプロジェクトディレクトリに移動
cd prisma-dashboard-test
# prisma バージョン6(安定版)をインストール
npm install prisma@6 --save-dev
npm install @prisma/client@6
Prisma最新版(v8系など)のCLI変更点と、安定版を選ぶべき理由
実際にPrismaを使った開発を進める中で、ハマりやすいポイントとして「バージョンによるCLIコマンドの大きな違い」があります。
2026年時点の最新プレリリース版(Prisma v8系など)では、CLIの仕様やコマンド体系が大きく刷新されています。例えば、従来の定番だった以下のようなコマンドが、そのままでは通用しなくなっています。
- npx prisma migrate dev –name init
→ 新しい体系ではコマンド名や使い方が変更- npx prisma db push
→ コマンドが変わる、または prisma.config.ts による設定管理へ移行また、初期化時に prisma.config.ts が自動生成され、AIエディタ向けのスキル設定が含まれるなど、内部的な挙動も大きく変わっています。
今から始めるなら「v6(安定版)」がおすすめ
もしこれから学習や新規プロジェクトでPrismaを導入する場合、最新のRC版をインストールするとネット上の古い(あるいは一般的な)解説記事やチュートリアルのコマンドが動かず、混乱する原因になります。
そのため、余計なトラブルを避けてスムーズに開発を進めたい場合は、以下のように明示的に安定版(v6系)を指定してインストールするのがおすすめです。
従来の解説記事と同じように npx prisma db push や migrate dev がスムーズに利用できるようになります。新機能を先取りしたい上級者を除いては、当面は安定版をベースに環境を構築するのが最も確実で安全な選択肢です。
- Prismaの初期化
npx prisma initこれでプロジェクト内に prisma/schema.prisma と .env ファイルが生成されます。
環境変数の設定 (.env)
生成された .env ファイルを開き、先ほどNeonで取得した接続URLを設定します。
DATABASE_URL="postgresql://ユーザー名:パスワード@ホスト名/データベース名?sslmode=require"ステップ3:スキーマの定義
「社内ユーザー名」「メールアドレス」「役職(ロール)」を持つユーザーテーブルを定義します。
/prisma/schema.prisma を以下の内容に書き換えます。
datasource db {
provider = "postgresql"
// .envに定義した DATABASE_URL という環境変数から安全に読み込む
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
}
model User {
id Int @id @default(autoincrement())
name String // 社内ユーザー名
email String @unique // メールアドレス (重複して登録されないようにユニーク制約)
role String // 役職(ロール)
createdAt DateTime @default(now()) // データを追加した瞬間の現在時刻が自動で入る
}以下のコマンドでNeon データベースにテーブルを作成します。
npx prisma db pushステップ4:シードデータの投入
- package.json にシード設定を追加する
package.json を開き、一番外側の閉じ括弧 } の手前あたりに以下の prisma 設定を追加します。
{
"name": "prisma-dashboard-test",
// ...
},
"dependencies": {
// ...
},
"devDependencies": {
// ...
// ここを追加
},
"prisma": {
"seed": "node prisma/seed.js"
}
}
- prisma/seed.js を作成する
prisma フォルダの中に seed.js ファイルを作成し、以下のコードを書き込んで保存します。
const { PrismaClient } = require('@prisma/client')
const prisma = new PrismaClient()
async function main() {
// 既存データをクリア
await prisma.user.deleteMany({})
// シードデータの投入
// createManyを使って、初期テスト用のユーザーデータをまとめて複数件登録する
await prisma.user.createMany({
data: [
{ name: '鈴木 一郎', email: '[email protected]', role: '課長' },
{ name: '佐藤 健一', email: '[email protected]', role: '営業' },
{ name: '高橋 花子', email: '[email protected]', role: '事務' },
],
})
console.log('Seeded database with initial users.')
}
main()
.catch((e) => {
// 途中でエラーが発生した場合はエラー内容を出力し、異常終了(exit code 1)させる
console.error(e)
process.exit(1)
})
.finally(async () => {
// 成功・失敗にかかわらず、処理の最後に必ずデータベースとの接続を切断する
await prisma.$disconnect()
})- シードコマンドを実行する
以下のコマンドを実行して、データベースに初期データを投入します。
npx prisma db seedここまで完了したら、次はNext.jsの画面からデータを取得してダッシュボードに表示させましょう!
ステップ5:Next.jsプロジェクトの作成とダッシュボード画面でのデータベース表示実装
データベースとシードデータの準備が整いましたので、いよいよ最後のステップである「Next.jsのダッシュボードページ作成」に進みます。
以下の2つのファイルを作成・配置しましょう。
1. Prisma Clientのインスタンス化ファイル作成 (lib/prisma.ts)
プロジェクトのルートに lib フォルダを作成し、その中に prisma.ts を置きます。これにより、アプリ内で重複したDB接続が走るのを防げます。
⚠️ 開発中の問題(なぜ重複してしまうのか?)
Next.jsの開発サーバーを動かしているとき、コードを保存して書き換えると、ファイルが読み込まれるたびに新しいデータベース接続(コネクション)が何個も作られてしまいます。
データベース(Neonなど)には「同時に接続できる数(コネクション数)の上限」があるため、開発中に何度もファイルを保存しているうちに上限に達し、「Too many connections」を起こしてエラーを出してしまいます。
- フォルダ作成
mkdir -p lib- lib/prisma.ts を作成
import { PrismaClient } from '@prisma/client'
// パソコン全体のメモリ領域(global)に、すでにデータベース接続用のインスタンスがあるか確認
const globalForPrisma = global as unknown as { prisma: PrismaClient }
// まだなければ新しく作って、次のためにメモリ領域に保存しておく
export const prisma =
globalForPrisma.prisma || new PrismaClient()
// すでにあれば既存のものをそのまま使う
if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma2. ダッシュボードページの作成 (app/dashboard/page.tsx)
Next.jsのApp Routerを使って、サーバー側でデータベースからユーザー一覧を取得し、テーブル形式で美しく表示するページを作ります。
- フォルダ・ファイル作成: すでに app フォルダがあるので、その中に dashboard フォルダを作り、page.tsx を配置します。
mkdir -p app/dashboard- app/dashboard/page.tsx の中身:
import { prisma } from '@/lib/prisma'
import { Suspense } from 'react'
// データベースからユーザー一覧を取得してテーブルを表示する内部コンポーネント
async function UserTable() {
const users = await prisma.user.findMany({
orderBy: { createdAt: 'desc' },
})
return (
<div className="bg-white shadow-md rounded-lg overflow-hidden">
<div className="px-6 py-4 border-b border-gray-200">
<h2 className="text-xl font-semibold text-gray-700">登録ユーザー一覧</h2>
</div>
<table className="min-w-full divide-y divide-gray-200">
<thead className="bg-gray-50">
<tr>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">ID</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">ユーザー名</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">メールアドレス</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">役職 (ロール)</th>
</tr>
</thead>
<tbody className="bg-white divide-y divide-gray-200">
{users.map((user) => (
<tr key={user.id}>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{user.id}</td>
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">{user.name}</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{user.email}</td>
<td className="px-6 py-4 whitespace-nowrap text-sm">
<span className="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-blue-100 text-blue-800">
{user.role}
</span>
</td>
</tr>
))}
</tbody>
</table>
</div>
)
}
// ページ本体
export default function DashboardPage() {
return (
<div className="min-h-screen bg-gray-50 p-8">
<div className="max-w-4xl mx-auto">
<header className="mb-8">
<h1 className="text-3xl font-bold text-gray-800">社内ダッシュボード</h1>
<p className="text-gray-600">Neon + Prisma + Next.js 連携テスト</p>
</header>
{/* Suspenseで囲むことで、データ読み込み中のプレースホルダーを表示しつつエラーを回避 */}
<Suspense fallback={<div className="p-6 bg-white rounded-lg shadow">データを読み込んでいます...</div>}>
<UserTable />
</Suspense>
</div>
</div>
)
}コードの中で使われている<Suspense>(サスペンス) について、その役割と仕組みをわかりやすく解説します。
1. <Suspense> とは何か?
React(およびNext.js)に備わっている機能で、データの準備ができるまで、代わりのローディング画面などを出しておく機能です。
2. なぜこのコードで <Suspense> が必要なのか?
このコードの中にある UserTable は、async/await を使ってデータベース(Neon)から非同期でデータを取得しています。
Next.jsのApp Routerでは、このように「データが届くまでに時間がかかる非同期コンポーネント」をそのままページに置くと、ビルド時やページ表示時にエラーになってしまうことがあります。
そこで <Suspense> の出番になります。
3. 実際の動き
- アクセス直後:
ブラウザがページを開いたとき、データベースからのデータ取得(prisma.user.findMany)はまだ完了していません。- fallback の表示:
Reactは「まだデータが届いていないな」と検知し、<Suspense> の fallback プロパティに指定されている部分を画面に出します。- データ取得完了後
バックグラウンドでデータベースからユーザーデータが無事に届くと、Reactは「読み込み中メッセージ」を消して、<UserTable/>に切り替えて表示します。メリット
- ユーザー体験の向上: 真っ白な画面で固まるのを防ぎ、「データを読み込んでいます…」という安心感をユーザーに与えられます。
- ストリーミング配信: Next.jsの強力な機能である「ページの他の部分(ヘッダーなど)を先に表示し、データの部分だけ後から読み込んで表示する(ストリーミング)」という効率的な表示ができるようになります。
3. 動作確認
コードが準備できたら、Next.jsの開発サーバーを起動します。
npm run devブラウザで http://localhost:3000/dashboard にアクセスしてみてください! シードデータとして投入したユーザー情報が、以下のようにダッシュボードに一覧表示されるはずです。

まとめ
今回はORMの基本から、TypeScript環境で圧倒的な人気を誇るPrismaの特徴、そしてNeon、Next.jsを組み合わせた実践的な接続サンプルまでを解説しました。
Prismaを採用することで、型安全でバグの少ないデータベース操作をスムーズに実装できるようになります。モダンなWebアプリケーション開発を検討している方は、ぜひ導入を試してみてください!



コメント