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

Next.jsでPrisma ORMを使用する方法

20分

はじめに

このガイドでは、フルスタック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モデルです。

prisma/schema.prisma
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])
}

これは、ユーザーと投稿を含むシンプルなブログを表しています。各PostUserを著者として持ち、各Userは多数のPostを持つことができます。

Prismaスキーマとモデルができたので、Prisma Postgresデータベースに接続しましょう。

2.2 データベース接続文字列の保存

Prismaスキーマができたので、スキーマを適用するデータベースが必要になります。

まだデータベースがありません

まだデータベースがない場合は、以下から新しいデータベースを作成できます。。ステップごとの手順は、入門ガイドにあります。

Prisma Postgresプロジェクトを作成すると、DATABASE_URLが取得されます。それを.envファイルに保存します。

.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という新しいファイルを作成します。

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設定を追加します。

package.json
{
"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を削除します

package.json
"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ファイルに追加します

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ファイルを開き、既存のコードを次のように置き換えます

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>
);
}

これにより、タイトルとユーザーリストを備えた基本的なページが得られます。ただし、このリストは静的であり、変更されません。ページを更新して、データベースからユーザーをフェッチし、動的にしましょう。

app/page.tsx
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ファイルに追加します

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がロードされますが、コンテンツは静的です。ホームページと同様に、動的に更新しましょう

app/posts/page.tsx
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ファイルに追加します

app/posts/[id]/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に基づいて動的に更新しましょう

app/posts/[id]/page.tsx
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ファイルに追加します

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関数を更新して投稿をデータベースに保存しましょう

app/posts/new/page.tsx
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スクリプトを追加します。

package.json
{
"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の旅を続けましょう。 アクティブなコミュニティ。常に情報を入手し、参加し、他の開発者と協力しましょう

皆様のご参加を心から歓迎し、コミュニティの一員としてお迎えできることを楽しみにしています!