📑

Next.js ハンズオン-その1: 掲示板機能

2024/08/21に公開

はじめに

この記事はNext.jsで掲示板を作るもっとも簡単な例を解説します。

全体の流れ

  1. npxでNext.jsアプリを作成。
  2. APIを作成し、記事データを提供するエンドポイントを作成。
  3. トップページでAPIを使って記事一覧を表示。
  4. 動的ルーティングを使用して記事の詳細ページを作成。
  5. CSSで全体のスタイリングを整える。

Step0: Next.jsアプリの作成

以下のコマンドでNext.jsアプリを作成しましょう。
TypeScriptとappディレクトリを選んでおくと良いです。(基本的にEnterを押し続けておけばよいです。)

$ npx create-next-app@latest blog-app

アプリのひな型ができたら開発サーバーを起動しましょう。

$ cd blog-app
$ npm run dev

開発サーバーが起動し、http://localhost:3000でアプリが確認できます。

Step1: APIをNext.js内で作成

app/api/posts/route.ts

記事データを提供するAPIエンドポイントを作成します。appディレクトリ内にapiフォルダを作成し、その中にroute.tsファイルを追加します。
appルーティングにおけるapiフォルダの取り扱いはこちら

app/api/posts/route.ts
import { NextResponse } from 'next/server';

const posts = [
  { id: 1, title: 'First Post', body: 'This is the first post.' },
  { id: 2, title: 'Second Post', body: 'This is the second post.' },
  { id: 3, title: 'Third Post', body: 'This is the third post.' },
];

export async function GET() {
  return NextResponse.json(posts);
}

・/api/postsエンドポイントが作成され、GET(つまりhttp://localhost:3000/api/postsへのアクセス)によって記事データの配列が返されるようにしました。
・実際にはこの部分でデータベースや他のAPIからデータを取得することも可能です。

Step 2: トップページの作成

app/page.tsx

APIから記事データを取得して、一覧表示するトップページを作成します。

app/page.tsx
import Link from 'next/link';

async function getPosts() {
  const res = await fetch('http://localhost:3000/api/posts');
  if (!res.ok) {
    throw new Error('Failed to fetch posts');
  }
  return res.json();
}

export default async function HomePage() {
  const posts = await getPosts();

  return (
    <div>
      <h1>Blog Posts</h1>
      <ul>
        {posts.map((post: { id: number; title: string }) => (
          <li key={post.id}>
            <Link href={`/posts/${post.id}`}>
              {post.title}
            </Link>
          </li>
        ))}
      </ul>
    </div>
  );
}

・fetch関数を使って、/api/postsエンドポイントから記事データを取得し、記事のタイトルを表示します。
・Linkコンポーネントで、記事詳細ページへのリンクを作成しています。

map()が分からない方へ

  1. posts配列の各要素(記事データ)に対してmap()を実行し、各記事に対応するリストアイテム<li>を生成します。
  2. それぞれの<li>に記事のタイトルを表示し、リンクとしてクリック可能にします。
  3. リンクをクリックすると、/posts/に続く記事IDのパスに動的に遷移し、該当する記事の詳細ページが表示されます

Step 3: 動的なルーティングによる記事詳細ページの作成

app/posts/[id]/page.tsx

動的ルーティングを使用して記事の詳細ページを作成します。

app/posts/[id]/page.tsx
import { notFound } from 'next/navigation';

async function getPost(id: string) {
  const res = await fetch(`http://localhost:3000/api/posts`);
  if (!res.ok) {
    throw new Error('Failed to fetch posts');
  }
  const posts = await res.json();
  return posts.find((post: { id: number }) => post.id.toString() === id);
}

export default async function PostPage({ params }: { params: { id: string } }) {
  const post = await getPost(params.id);

  if (!post) {
    notFound();
  }

  return (
    <div>
      <h1>{post.title}</h1>
      <p>{post.body}</p>
      <Link href="/">Back to Home</Link>
    </div>
  );
}

・動的なルーティングを使用し、URLパラメータ(params.id)に基づいて、記事をフィルタリングして詳細ページを表示します。
・記事が見つからない場合、notFound()関数で404ページを表示します。

({ params }: { params: { id: string } })が分からない方へ

・関数の引数として、paramsというオブジェクトを受け取っています。これはNext.jsの動的ルーティングで自動的に渡されるもので、URLのパスパラメータ(ここではid)が含まれています。
・{ params }: { params: { id: string } }は、TypeScriptによる型定義です。これにより、paramsがidというキーを持つオブジェクトであり、idは文字列型であることが明示されています。

Step 4: スタイリングの追加

app/globals.css

基本的なスタイルを追加します。globals.cssはappディレクトリのルートに置き、全体のスタイリングに利用します。

app/global.css
/* app/globals.css */
body {
  font-family: Arial, sans-serif;
  padding: 20px;
  margin: 0;
  background-color: #f4f4f4;
}

h1 {
  color: #333;
}

ul {
  list-style-type: none;
  padding: 0;
}

li {
  margin: 10px 0;
}

a {
  color: #0070f3;
  text-decoration: none;
}

a:hover {
  text-decoration: underline;
}

Discussion