nextjs-development
Next.js App Router開発ガイド。Server Components、ルーティング、データフェッチング、キャッシング、デプロイなど、Next.js開発のベストプラクティス。
When & Why to Use This Skill
This comprehensive Next.js development skill serves as a definitive guide for mastering the App Router architecture. It provides expert-level insights into React Server Components (RSC), efficient data fetching strategies, and advanced caching mechanisms. Designed to enhance web performance and developer productivity, it offers actionable best practices, implementation patterns with Prisma, and deployment optimization for building scalable, high-performance modern web applications.
Use Cases
- Architecting scalable web applications using Next.js App Router and Server Components to significantly reduce client-side bundle sizes.
- Implementing high-performance data fetching and mutation patterns using the fetch API, Prisma ORM, and Server Actions.
- Configuring complex multi-level caching and on-demand revalidation strategies to optimize Time to First Byte (TTFB) and SEO rankings.
- Designing robust and type-safe API Route Handlers for CRUD operations and third-party service integrations.
- Troubleshooting and refactoring common development pitfalls and anti-patterns in Server/Client component composition.
| name | nextjs-development |
|---|---|
| description | Next.js App Router開発ガイド。Server Components、ルーティング、データフェッチング、キャッシング、デプロイなど、Next.js開発のベストプラクティス。 |
Next.js Development Skill
📋 目次
基礎編(このファイル)
詳細ガイド(完全版)
- Server Components完全ガイド - 23,000文字
- データフェッチング戦略完全ガイド - 28,000文字
- キャッシング&リバリデーション完全ガイド - 26,000文字
概要
このSkillは、Next.js App Router開発をカバーします:
- App Router - ファイルベースルーティング
- Server Components - サーバーサイドレンダリング
- データフェッチング - fetch, Prisma, ORMs
- キャッシング - 自動キャッシュ、revalidate
- API Routes - RESTful API
- デプロイ - Vercel, 自己ホスティング
📚 公式ドキュメント・参考リソース
このガイドで学べること: App Routerパターン、Server Components設計、キャッシング戦略 公式で確認すべきこと: 最新API、Next.js 15の新機能、デプロイオプション、マイグレーションガイド
主要な公式ドキュメント
Next.js Documentation - Next.js公式ドキュメント
- App Router Guide - App Routerの完全ガイド
- Data Fetching - データフェッチング詳細
- Caching - キャッシング機構の詳細
- Server Actions - Server Actions完全ガイド
- API Reference - 全APIリファレンス
Next.js Learn - 公式チュートリアル
- インタラクティブな学習コース
- ステップバイステップのプロジェクト構築
関連リソース
- Vercel Documentation - デプロイプラットフォーム
- Next.js Examples - 100+の公式サンプル
- Awesome Next.js - ライブラリ・プラグイン一覧
- Next.js Conf - 年次カンファレンス動画
📚 詳細ガイド
プロダクションレベルの実装を学ぶには、以下の完全ガイドを参照してください:
1. Server Components完全ガイド
23,000文字 | 完全なTypeScript実装例 | 実測値データ付き
- Server Components vs Client Componentsの完全解説
- 使い分け戦略とパターン(10以上)
- 実測パフォーマンス比較(バンドルサイズ -81.7%、データフェッチ -82.7%)
- よくある4つの間違いと解決策
- 実践的なブログアプリ・ECサイトの完全実装
2. データフェッチング戦略完全ガイド
28,000文字 | Prisma統合 | Server Actions実装
- fetch APIの完全活用(5パターン)
- Prisma/ORMとの統合(CRUD、トランザクション、集計クエリ)
- 並列・直列フェッチングの最適化
- エラーハンドリングとリトライ機構
- 実測値に基づく改善効果(並列実行 -66.7%、キャッシング -98.2%)
3. キャッシング&リバリデーション完全ガイド
26,000文字 | 4つのキャッシュ階層 | Webhook連携
- キャッシュの4つの階層(Request Memoization、Data Cache、Full Route Cache、Router Cache)
- 時間ベース、オンデマンド、タグベースリバリデーション
- キャッシュ戦略パターン(5パターン)
- CMSとのWebhook連携実装
- 実測データ(キャッシュヒット -98.2%、TTFB -97.9%)
合計: 77,000文字 | 30以上の完全実装例 | 実プロジェクトの測定データ
🎓 学習パス
初心者向け
- このファイルで基礎を理解
- Server Components完全ガイドで実装方法を習得
- 簡単なブログアプリを作成
中級者向け
- データフェッチング戦略完全ガイドでPrisma統合を学習
- キャッシング&リバリデーション完全ガイドで最適化手法を習得
- ECサイトやダッシュボードを作成
上級者向け
- 全ての詳細ガイドを参照しながら、大規模アプリケーションを設計
- パフォーマンス測定とチューニング
- CMSとのWebhook連携やカスタムキャッシュ戦略を実装
いつ使うか
🎯 必須のタイミング
- 新規Next.jsプロジェクト作成時
- ページ・レイアウト追加時
- API Route追加時
- データフェッチング実装時
App Router基礎
ファイルベースルーティング
app/
├── page.tsx # / (ルート)
├── about/page.tsx # /about
├── blog/
│ ├── page.tsx # /blog
│ └── [slug]/page.tsx # /blog/hello-world
├── dashboard/
│ ├── layout.tsx # /dashboard のレイアウト
│ ├── page.tsx # /dashboard
│ └── settings/page.tsx # /dashboard/settings
└── api/
└── users/route.ts # /api/users
ページの作成
// app/page.tsx(ルートページ)
export default function Home() {
return (
<main>
<h1>Welcome</h1>
</main>
)
}
レイアウトの作成
// app/layout.tsx(ルートレイアウト)
export const metadata = {
title: 'My App',
description: 'App description',
}
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="ja">
<body>
<nav>ナビゲーション</nav>
{children}
<footer>フッター</footer>
</body>
</html>
)
}
// app/dashboard/layout.tsx(ネストレイアウト)
export default function DashboardLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<div className="flex">
<aside>サイドバー</aside>
<main>{children}</main>
</div>
)
}
動的ルート
// app/blog/[slug]/page.tsx
interface PageProps {
params: { slug: string }
searchParams: { [key: string]: string | string[] | undefined }
}
export default function BlogPost({ params }: PageProps) {
return <h1>Post: {params.slug}</h1>
}
// 静的生成用
export async function generateStaticParams() {
const posts = await getPosts()
return posts.map((post) => ({
slug: post.slug,
}))
}
Server Components vs Client Components
Server Components(デフォルト)
// app/posts/page.tsx
// ✅ Server Component(デフォルト)
async function getPosts() {
const res = await fetch('https://api.example.com/posts', {
next: { revalidate: 3600 } // 1時間キャッシュ
})
return res.json()
}
export default async function PostsPage() {
const posts = await getPosts() // 直接await可能
return (
<ul>
{posts.map(post => (
<li key={post.id}>{post.title}</li>
))}
</ul>
)
}
メリット:
- サーバーで実行(クライアントバンドル削減)
- 直接DBアクセス可能
- 環境変数を安全に使用可能
Client Components
// components/Counter.tsx
'use client' // ← 必須
import { useState } from 'react'
export function Counter() {
const [count, setCount] = useState(0)
return (
<button onClick={() => setCount(count + 1)}>
Count: {count}
</button>
)
}
使用するタイミング:
- useState, useEffect等のHooksを使う
- イベントハンドラー(onClick等)
- ブラウザAPI(localStorage等)
混在パターン
// app/page.tsx(Server Component)
import { Counter } from '@/components/Counter' // Client Component
async function getInitialCount() {
// サーバーでデータ取得
return 42
}
export default async function Home() {
const initialCount = await getInitialCount()
return (
<div>
<h1>Server Component</h1>
<Counter initialValue={initialCount} />
</div>
)
}
データフェッチング
fetch API
// キャッシュあり(デフォルト)
async function getData() {
const res = await fetch('https://api.example.com/data')
return res.json()
}
// キャッシュなし
async function getData() {
const res = await fetch('https://api.example.com/data', {
cache: 'no-store'
})
return res.json()
}
// 時間ベースリバリデーション
async function getData() {
const res = await fetch('https://api.example.com/data', {
next: { revalidate: 3600 } // 1時間
})
return res.json()
}
Prisma使用例
// 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
// app/users/page.tsx
import { prisma } from '@/lib/prisma'
export default async function UsersPage() {
const users = await prisma.user.findMany()
return (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
)
}
キャッシング戦略
リバリデーション
時間ベース(Time-based)
// 60秒ごとに再検証
fetch('https://api.example.com/data', {
next: { revalidate: 60 }
})
オンデマンド(On-demand)
// app/api/revalidate/route.ts
import { revalidatePath } from 'next/cache'
import { NextRequest } from 'next/server'
export async function POST(request: NextRequest) {
const path = request.nextUrl.searchParams.get('path')
if (path) {
revalidatePath(path)
return Response.json({ revalidated: true, now: Date.now() })
}
return Response.json({ revalidated: false })
}
// 使用例
// POST /api/revalidate?path=/posts
実践例
Example 1: ブログアプリ
// app/blog/page.tsx
import Link from 'next/link'
async function getPosts() {
const res = await fetch('https://jsonplaceholder.typicode.com/posts', {
next: { revalidate: 3600 }
})
return res.json()
}
export default async function BlogPage() {
const posts = await getPosts()
return (
<div>
<h1>Blog</h1>
<ul>
{posts.map((post: any) => (
<li key={post.id}>
<Link href={`/blog/${post.id}`}>
{post.title}
</Link>
</li>
))}
</ul>
</div>
)
}
// app/blog/[id]/page.tsx
async function getPost(id: string) {
const res = await fetch(`https://jsonplaceholder.typicode.com/posts/${id}`, {
next: { revalidate: 3600 }
})
return res.json()
}
export default async function PostPage({ params }: { params: { id: string } }) {
const post = await getPost(params.id)
return (
<article>
<h1>{post.title}</h1>
<p>{post.body}</p>
</article>
)
}
Example 2: API Route(CRUD)
// app/api/users/route.ts
import { NextRequest, NextResponse } from 'next/server'
import { prisma } from '@/lib/prisma'
// GET /api/users
export async function GET() {
const users = await prisma.user.findMany()
return NextResponse.json(users)
}
// POST /api/users
export async function POST(request: NextRequest) {
const body = await request.json()
const user = await prisma.user.create({
data: {
name: body.name,
email: body.email,
},
})
return NextResponse.json(user, { status: 201 })
}
// app/api/users/[id]/route.ts
// PUT /api/users/:id
export async function PUT(
request: NextRequest,
{ params }: { params: { id: string } }
) {
const body = await request.json()
const user = await prisma.user.update({
where: { id: params.id },
data: body,
})
return NextResponse.json(user)
}
// DELETE /api/users/:id
export async function DELETE(
request: NextRequest,
{ params }: { params: { id: string } }
) {
await prisma.user.delete({
where: { id: params.id },
})
return new NextResponse(null, { status: 204 })
}
Example 3: フォーム送信(Server Actions)
// app/create-post/page.tsx
import { redirect } from 'next/navigation'
import { prisma } from '@/lib/prisma'
async function createPost(formData: FormData) {
'use server' // Server Action
const title = formData.get('title') as string
const content = formData.get('content') as string
await prisma.post.create({
data: { title, content },
})
redirect('/posts')
}
export default function CreatePostPage() {
return (
<form action={createPost}>
<input name="title" placeholder="Title" required />
<textarea name="content" placeholder="Content" required />
<button type="submit">Create</button>
</form>
)
}
アンチパターン
❌ 1. Client ComponentでのDB直接アクセス
'use client'
// ❌ 悪い例
import { prisma } from '@/lib/prisma'
export function UserList() {
const users = await prisma.user.findMany() // エラー!
}
// ✅ 良い例(Server Component)
import { prisma } from '@/lib/prisma'
export default async function UserList() {
const users = await prisma.user.findMany()
return <ul>{/* ... */}</ul>
}
❌ 2. 不要な'use client'
// ❌ 悪い例
'use client' // 不要(インタラクティブでない)
export function UserCard({ user }: { user: User }) {
return <div>{user.name}</div>
}
// ✅ 良い例(Server Component)
export function UserCard({ user }: { user: User }) {
return <div>{user.name}</div>
}
Agent連携
📖 Agentへの指示例
新規ページ作成
/about ページを作成してください。
会社概要、ミッション、チーム紹介を含めてください。
API Route作成
/api/posts のCRUD APIを作成してください。
Prismaを使用して、GET, POST, PUT, DELETEをサポートしてください。
Server Actions実装
ユーザー作成フォームをServer Actionsで実装してください。
バリデーションも含めてください。
まとめ
Next.jsのベストプラクティス
- Server Components優先 - デフォルトで使用
- 適切なキャッシング - revalidateを活用
- 型安全性 - TypeScript + Prisma
- Server Actions - フォーム送信に活用
Last updated: 2025-12-26