Next.jsでPrisma ORMを使用する方法
はじめに
このガイドでは、フルスタックReactフレームワークであるNext.js 15でPrisma ORMを使用する方法を説明します。Next.jsでPrisma ORMをセットアップし、マイグレーションを処理し、アプリケーションをVercelにデプロイする方法を学びます。デプロイ可能な例はGitHubにあります。
前提条件
このガイドを開始する前に、以下を用意してください。
- Node.js 18+ がインストールされていること
- Prisma Postgresデータベース(または任意のPostgreSQLデータベース)
- Vercelアカウント(アプリケーションをデプロイする場合)
1. プロジェクトのセットアップ
プロジェクトを作成するディレクトリから、create-next-app
を実行して、このガイドで使用する新しいNext.jsアプリを作成します。
npx create-next-app@latest my-app
プロジェクトに関するいくつかの質問に答えるように求められます。すべてデフォルトを選択してください。
完全を期すために、それらは以下のとおりです。
- TypeScript
- ESLint
- Tailwind CSS
src
ディレクトリなし- App Router
- Turbopack
- カスタマイズされたインポートエイリアスなし
次に、プロジェクトディレクトリに移動します
cd my-app
2. Prisma ORMのセットアップ
2.1 Prisma ORMのインストールと最初のモデルの作成
まず、いくつかの依存関係をインストールする必要があります。プロジェクトのルートディレクトリで、ターミナルで以下を実行します。
npm install prisma --save-dev
npm install tsx --save-dev
npm install @prisma/extension-accelerate
Prisma Postgresデータベースを使用していない場合は、@prisma/extension-accelerate
パッケージは不要です。
次に、prisma init
を実行して、プロジェクトでPrisma ORMを初期化します。
npx prisma init --output ../src/app/generated/prisma
これにより、プロジェクトに新しいprisma
ディレクトリが作成され、その中にschema.prisma
ファイルが作成されます。schema.prisma
ファイルは、データベースモデルを定義する場所です。
prisma init
コマンドは、データベース接続文字列を保存するための.env
ファイルもプロジェクトルートに作成し、生成されたクライアントを/app/generated/prisma
に出力します。
次に、schema.prisma
ファイルに2つのモデルを追加しましょう。User
モデルとPost
モデルです。
generator client {
provider = "prisma-client-js"
output = "../src/app/generated/prisma"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model User {
id Int @id @default(autoincrement())
email String @unique
name String?
posts Post[]
}
model Post {
id Int @id @default(autoincrement())
title String
content String?
published Boolean @default(false)
authorId Int
author User @relation(fields: [authorId], references: [id])
}
これは、ユーザーと投稿を含むシンプルなブログを表しています。各Post
はUser
を著者として持ち、各User
は多数のPost
を持つことができます。
Prismaスキーマとモデルができたので、Prisma Postgresデータベースに接続しましょう。
2.2 データベース接続文字列の保存
Prismaスキーマができたので、スキーマを適用するデータベースが必要になります。
まだデータベースがない場合は、以下から新しいデータベースを作成できます。。ステップごとの手順は、入門ガイドにあります。
Prisma Postgresプロジェクトを作成すると、DATABASE_URL
が取得されます。それを.env
ファイルに保存します。
DATABASE_URL="prisma+postgres://accelerate.prisma-data.net/?api_key=eyJhbGciOiJIU...""
2.3 データベーススキーマの更新
既存のデータを持つデータベースに接続する場合は、prisma db pull
コマンドを使用し、Prisma Clientのセットアップに進んでください。
データベース接続文字列を保存したので、prisma migrate dev
コマンドを使用してスキーマをデータベースに適用できます。
npx prisma migrate dev --name init
これにより、User
テーブルとPost
テーブルを作成する最初のマイグレーションが作成され、そのマイグレーションがデータベースに適用されます。
次に、初期データをデータベースに追加しましょう。
2.4 データベースのシード
Prisma ORMには、初期データでデータベースをシードするための組み込みサポートがあります。これを行うには、prisma
ディレクトリにseed.ts
という新しいファイルを作成します。
import { PrismaClient, Prisma } from '../src/app/generated/prisma'
const prisma = new PrismaClient()
const userData: Prisma.UserCreateInput[] = [
{
name: 'Alice',
email: 'alice@prisma.io',
posts: {
create: [
{
title: 'Join the Prisma Discord',
content: 'https://pris.ly/discord',
published: true,
},
{
title: 'Prisma on YouTube',
content: 'https://pris.ly/youtube',
},
],
},
},
{
name: 'Bob',
email: 'bob@prisma.io',
posts: {
create: [
{
title: 'Follow Prisma on Twitter',
content: 'https://www.twitter.com/prisma',
published: true,
},
],
},
}
]
export async function main() {
for (const u of userData) {
await prisma.user.create({ data: u })
}
}
main()
次に、package.json
ファイルにprisma.seed
設定を追加します。
{
"name": "my-app",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev --turbopack",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"prisma": {
"seed": "tsx prisma/seed.ts"
},
"dependencies": {
"@prisma/client": "^6.2.1",
"@prisma/extension-accelerate": "^1.2.1",
"next": "15.1.4",
"react": "^19.0.0",
"react-dom": "^19.0.0"
},
"devDependencies": {
"@eslint/eslintrc": "^3",
"@types/node": "^20",
"@types/react": "^19",
"@types/react-dom": "^19",
"eslint": "^9",
"eslint-config-next": "15.1.4",
"postcss": "^8",
"prisma": "^6.2.1",
"tailwindcss": "^3.4.1",
"tsx": "^4.19.2",
"typescript": "^5"
}
}
開発サーバーを起動する前に、Next.js v15.2.0を使用している場合は、既知の問題があるため、Turbopackを使用しないでください。package.json
を更新して、devスクリプトからTurbopackを削除します
"script":{
"dev": "next dev --turbopack",
"dev": "next dev",
}
最後に、prisma db seed
を実行して、seed.ts
ファイルで定義した初期データでデータベースをシードします。
npx prisma db seed
これで、初期データを含むデータベースができました!prisma studio
を実行して、データベース内のデータを確認できます。
npx prisma studio
2.5 Prisma Clientのセットアップ
初期データを含むデータベースができたので、Prisma Clientをセットアップしてデータベースに接続できます。
プロジェクトのルートディレクトリに、新しいlib
ディレクトリを作成し、その中にprisma.ts
ファイルを追加します。
mkdir -p src/lib && touch src/lib/prisma.ts
次に、次のコードをlib/prisma.ts
ファイルに追加します
import { PrismaClient } from '../src/app/generated/prisma'
import { withAccelerate } from '@prisma/extension-accelerate'
const globalForPrisma = global as unknown as {
prisma: PrismaClient
}
const prisma = globalForPrisma.prisma || new PrismaClient().$extends(withAccelerate())
if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma
export default prisma
このファイルはPrisma Clientを作成し、グローバルオブジェクトにアタッチして、アプリケーションでクライアントのインスタンスが1つだけ作成されるようにします。これは、開発モードでPrisma ORMをNext.jsで使用する場合に発生する可能性のあるホットリロードの問題を解決するのに役立ちます。
Prisma Postgresを使用していない場合は、次の行を置き換えてください。
const prisma = globalForPrisma.prisma || new PrismaClient().$extends(withAccelerate())
次のように。
const prisma = globalForPrisma.prisma || new PrismaClient()
そして、@prisma/extension-accelerate
のインポートを削除します。
次のセクションでは、このクライアントを使用して最初のクエリを実行します。
3. Prisma ORMでデータベースをクエリする
初期化されたPrisma Client、データベースへの接続、および初期データができたので、Prisma ORMを使用してデータのクエリを開始できます。
この例では、アプリケーションの「ホーム」ページにすべてのユーザーを表示します。
app/page.tsx
ファイルを開き、既存のコードを次のように置き換えます
export default async function Home() {
return (
<div className="min-h-screen bg-gray-50 flex flex-col items-center justify-center -mt-16">
<h1 className="text-4xl font-bold mb-8 font-[family-name:var(--font-geist-sans)] text-[#333333]">
Superblog
</h1>
<ol className="list-decimal list-inside font-[family-name:var(--font-geist-sans)]">
<li className="mb-2">Alice</li>
<li>Bob</li>
</ol>
</div>
);
}
これにより、タイトルとユーザーリストを備えた基本的なページが得られます。ただし、このリストは静的であり、変更されません。ページを更新して、データベースからユーザーをフェッチし、動的にしましょう。
import prisma from '@/lib/prisma'
export default async function Home() {
const users = await prisma.user.findMany();
return (
<div className="min-h-screen bg-gray-50 flex flex-col items-center justify-center -mt-16">
<h1 className="text-4xl font-bold mb-8 font-[family-name:var(--font-geist-sans)] text-[#333333]">
Superblog
</h1>
<ol className="list-decimal list-inside font-[family-name:var(--font-geist-sans)]">
{users.map((user) => (
<li key={user.id} className="mb-2">
{user.name}
</li>
))}
</ol>
</div>
);
}
これで、クライアントをインポートし、User
モデルにすべてのユーザーをクエリし、それらをリストに表示します。
これで、ホームページが動的になり、データベースからユーザーが表示されるようになります。
3.1 データの更新(オプション)
データが更新されたときに何が起こるかを確認したい場合は、次のことができます。
- 任意のSQLブラウザを介して
User
テーブルを更新する seed.ts
ファイルを変更して、ユーザーを追加するprisma.user.findMany
の呼び出しを変更して、ユーザーを並べ替えたり、ユーザーをフィルタリングしたりなど。
ページをリロードするだけで、変更が表示されます。
4. 新しい投稿リストページの追加
ホームページは動作していますが、すべての投稿を表示する新しいページを追加する必要があります。
まず、app
ディレクトリに新しいposts
ディレクトリを作成し、その中に新しいpage.tsx
ファイルを作成します。
mkdir -p src/app/posts && touch src/app/posts/page.tsx
次に、次のコードをapp/posts/page.tsx
ファイルに追加します
import prisma from "@/lib/prisma";
export default async function Posts() {
return (
<div className="min-h-screen bg-gray-50 flex flex-col items-center justify-center -mt-16">
<h1 className="text-4xl font-bold mb-8 font-[family-name:var(--font-geist-sans)] text-[#333333]">
Posts
</h1>
<ul className="font-[family-name:var(--font-geist-sans)] max-w-2xl space-y-4">
<li>My first post</li>
</ul>
</div>
);
}
これでlocalhost:3000/posts
がロードされますが、コンテンツは静的です。ホームページと同様に、動的に更新しましょう
import prisma from "@/lib/prisma";
export default async function Posts() {
const posts = await prisma.post.findMany({
include: {
author: true,
},
});
return (
<div className="min-h-screen bg-gray-50 flex flex-col items-center justify-center -mt-16">
<h1 className="text-4xl font-bold mb-8 font-[family-name:var(--font-geist-sans)] text-[#333333]">
Posts
</h1>
<ul className="font-[family-name:var(--font-geist-sans)] max-w-2xl space-y-4">
<li>My first post</li>
{posts.map((post) => (
<li key={post.id}>
<span className="font-semibold">{post.title}</span>
<span className="text-sm text-gray-600 ml-2">
by {post.author.name}
</span>
</li>
))}
</ul>
</div>
);
}
これはホームページと同様に動作しますが、ユーザーを表示する代わりに投稿を表示します。また、Prisma Clientクエリでinclude
を使用して各投稿の作成者をフェッチし、作成者名を表示していることも確認できます。
この「リストビュー」は、Webアプリケーションで最も一般的なパターンの1つです。アプリケーションにもう2つのページを追加します。これらも通常必要になります。「詳細ビュー」と「作成ビュー」です。
5. 新しい投稿詳細ページの追加
投稿リストページを補完するために、投稿詳細ページを追加します。
posts
ディレクトリに、新しい[id]
ディレクトリを作成し、その中に新しいpage.tsx
ファイルを作成します。
mkdir -p src/app/posts/[id] && touch src/app/posts/[id]/page.tsx
このページには、単一の投稿のタイトル、コンテンツ、および作成者が表示されます。他のページと同様に、次のコードをapp/posts/new/page.tsx
ファイルに追加します
import prisma from "@/lib/prisma";
export default async function Post({ params }: { params: Promise<{ id: string }> }) {
return (
<div className="min-h-screen bg-gray-50 flex flex-col items-center justify-center -mt-16">
<article className="max-w-2xl space-y-4 font-[family-name:var(--font-geist-sans)]">
<h1 className="text-4xl font-bold mb-8 text-[#333333]">My first post</h1>
<p className="text-gray-600 text-center">by Anonymous</p>
<div className="prose prose-gray mt-8">
No content available.
</div>
</article>
</div>
);
}
以前と同様に、このページは静的です。ページに渡されたparams
に基づいて動的に更新しましょう
import prisma from "@/lib/prisma";
import { notFound } from "next/navigation";
export default async function Post({ params }: { params: Promise<{ id: string }> }) {
const { id } = await params;
const post = await prisma.post.findUnique({
where: { id: parseInt(id) },
include: {
author: true,
},
});
if (!post) {
notFound();
}
return (
<div className="min-h-screen bg-gray-50 flex flex-col items-center justify-center -mt-16">
<article className="max-w-2xl space-y-4 font-[family-name:var(--font-geist-sans)]">
<h1 className="text-4xl font-bold mb-8 text-[#333333]">My first post</h1>
<p className="text-gray-600 text-center">by Anonymous</p>
<div className="prose prose-gray mt-8">
No content available.
</div>
<h1 className="text-4xl font-bold mb-8 text-[#333333]">{post.title}</h1>
<p className="text-gray-600 text-center">by {post.author.name}</p>
<div className="prose prose-gray mt-8">
{post.content || "No content available."}
</div>
</article>
</div>
);
}
ここでは多くの変更があるので、分解してみましょう
- Prisma Clientを使用して、
params
オブジェクトから取得するid
で投稿をフェッチしています。 - 投稿が存在しない場合(削除されたか、間違ったIDを入力した可能性があります)、
notFound()
を呼び出して404ページを表示します。 - 次に、投稿のタイトル、コンテンツ、および作成者を表示します。投稿にコンテンツがない場合は、プレースホルダーメッセージを表示します。
最も美しいページではありませんが、良いスタートです。localhost:3000/posts/1
およびlocalhost:3000/posts/2
に移動して試してみてください。localhost:3000/posts/999
に移動して404ページをテストすることもできます。
6. 新しい投稿作成ページの追加
アプリケーションを締めくくるために、投稿の「作成」ページを追加します。これにより、独自の投稿を書いてデータベースに保存できます。
他のページと同様に、最初に静的なページから始めて、動的に更新します。
mkdir -p src/app/posts/new && touch src/app/posts/new/page.tsx
次に、次のコードをapp/posts/new/page.tsx
ファイルに追加します
import Form from "next/form";
export default function NewPost() {
async function createPost(formData: FormData) {
"use server";
const title = formData.get("title") as string;
const content = formData.get("content") as string;
}
return (
<div className="max-w-2xl mx-auto p-4">
<h1 className="text-2xl font-bold mb-6">Create New Post</h1>
<Form action={createPost} className="space-y-6">
<div>
<label htmlFor="title" className="block text-lg mb-2">
Title
</label>
<input
type="text"
id="title"
name="title"
placeholder="Enter your post title"
className="w-full px-4 py-2 border rounded-lg"
/>
</div>
<div>
<label htmlFor="content" className="block text-lg mb-2">
Content
</label>
<textarea
id="content"
name="content"
placeholder="Write your post content here..."
rows={6}
className="w-full px-4 py-2 border rounded-lg"
/>
</div>
<button
type="submit"
className="w-full bg-blue-500 text-white py-3 rounded-lg hover:bg-blue-600"
>
Create Post
</button>
</Form>
</div>
);
}
このフォームは良さそうに見えますが、まだ何もしていません。createPost
関数を更新して投稿をデータベースに保存しましょう
import Form from "next/form";
import prisma from "@/lib/prisma";
import { revalidatePath } from "next/cache";
import { redirect } from "next/navigation";
export default function NewPost() {
async function createPost(formData: FormData) {
"use server";
const title = formData.get("title") as string;
const content = formData.get("content") as string;
await prisma.post.create({
data: {
title,
content,
authorId: 1,
},
});
revalidatePath("/posts");
redirect("/posts");
}
return (
<div className="max-w-2xl mx-auto p-4">
<h1 className="text-2xl font-bold mb-6">Create New Post</h1>
<Form action={createPost} className="space-y-6">
<div>
<label htmlFor="title" className="block text-lg mb-2">
Title
</label>
<input
type="text"
id="title"
name="title"
placeholder="Enter your post title"
className="w-full px-4 py-2 border rounded-lg"
/>
</div>
<div>
<label htmlFor="content" className="block text-lg mb-2">
Content
</label>
<textarea
id="content"
name="content"
placeholder="Write your post content here..."
rows={6}
className="w-full px-4 py-2 border rounded-lg"
/>
</div>
<button
type="submit"
className="w-full bg-blue-500 text-white py-3 rounded-lg hover:bg-blue-600"
>
Create Post
</button>
</Form>
</div>
);
}
このページには機能的なフォームができました!フォームを送信すると、データベースに新しい投稿が作成され、投稿リストページにリダイレクトされます。
また、revalidatePath
呼び出しを追加して、投稿リストページを再検証し、新しい投稿で更新されるようにしました。そうすることで、誰もがすぐに新しい投稿を読むことができます。
localhost:3000/posts/new
に移動してフォームを送信してみてください。
7. Vercelへのアプリケーションのデプロイ(オプション)
アプリケーションをVercelにデプロイする最も速い方法は、Vercel CLIを使用することです。
まず、Vercel CLIをインストールします
npm install -g vercel
次に、vercel login
を実行してVercelアカウントにログインします。
vercel login
デプロイする前に、Prisma Clientが確実に生成されるようにVercelに指示する必要があります。これを行うには、package.json
ファイルにpostinstall
スクリプトを追加します。
{
"name": "my-app",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev --turbopack",
"build": "next build",
"postinstall": "prisma generate",
"start": "next start",
"lint": "next lint"
},
"prisma": {
"seed": "tsx prisma/seed.ts"
},
"dependencies": {
"@prisma/client": "^6.2.1",
"@prisma/extension-accelerate": "^1.2.1",
"next": "15.1.4",
"react": "^19.0.0",
"react-dom": "^19.0.0"
},
"devDependencies": {
"@eslint/eslintrc": "^3",
"@types/node": "^20",
"@types/react": "^19",
"@types/react-dom": "^19",
"eslint": "^9",
"eslint-config-next": "15.1.4",
"postcss": "^8",
"prisma": "^6.2.1",
"tailwindcss": "^3.4.1",
"tsx": "^4.19.2",
"typescript": "^5"
}
}
この変更後、vercel
を実行してアプリケーションをVercelにデプロイできます。
vercel
デプロイが完了したら、Vercelが提供するURLでアプリケーションにアクセスできます。おめでとうございます。Next.jsアプリケーションをPrisma ORMでデプロイしました!
8. 次のステップ
Prisma ORMを使用した動作するNext.jsアプリケーションができたので、アプリケーションを拡張および改善する方法をいくつか紹介します。
- ルートを保護するために認証を追加する
- 投稿を編集および削除する機能を追加する
- 投稿にコメントを追加する
- ビジュアルデータベース管理にはPrisma Studioを使用する
詳細情報
Prismaとの繋がりを保つ
以下と繋がってPrismaの旅を続けましょう。 アクティブなコミュニティ。常に情報を入手し、参加し、他の開発者と協力しましょう
- Xでフォローしてください お知らせ、ライブイベント、役立つヒントをお届けします。
- Discordに参加してください 質問をしたり、コミュニティと話したり、会話を通じてアクティブなサポートを受けたりできます。
- YouTubeで購読してください チュートリアル、デモ、ストリームをご覧ください。
- GitHubで参加してください リポジトリにスターを付けたり、問題を報告したり、問題に貢献したりできます。