6.
Next.js でチャットボット - 3
Postman
POSTリクエスト作成
POSTリクエストを作成します。
http://localhost:3000/api/chat

JSONデータ設定
「Body」タブで「raw」「JSON」で設定し、POSTするJSONデータを記述します。

レスポンス確認
【Send】ボタンをクリックすると、APIからJSONデータがレスポンスされました。

Gemini APIの利用
Gemini API処理
チャット履歴
チャット履歴「history」を用意します。データ型は「@google/generative-ai」のContentとします。
app/api/chat/route.ts
import { NextRequest, NextResponse } from 'next/server';
// Contentインポート
import { Content } from "@google/generative-ai";
import { Message } from "@/app/interfaces/Message";
const history: Content = [];
...
APIキー読み込み
.env.local から Gemini APIキーを読み込みます。
app/api/chat/route.ts
export async function POST(req: NextRequest) {
// Gemini APIキーを読み込み
const API_KEY = process.env.GEMINI_API_KEY;
if (!API_KEY) return NextResponse.json({ error: "Not found API KEY" });
const message = await req.json();
if (!message) return NextResponse.json({ error: "Not found message" });
...
}
Gemini API連携
Gemni APIにプロンプトをリクエストし、ボットメッセージをレスポンスします。
app/api/chat/route.ts
import { NextRequest, NextResponse } from "next/server";
import { Content, GoogleGenerativeAI } from "@google/generative-ai";
import { Message } from "@/app/interfaces/Message";
const history: Content[] = [];
export async function POST(req: NextRequest) {
const API_KEY = process.env.GEMINI_API_KEY;
if (!API_KEY) return NextResponse.json({ error: "Not found API KEY" });
// POSTデータ取得
const message:Message = await req.json();
if (!message) return NextResponse.json({ error: "Not found message" });
// Gemni APIにリクエスト
const genAI = new GoogleGenerativeAI(API_KEY);
const model = genAI.getGenerativeModel({ model: "gemini-1.5-flash" });
const chat = model.startChat({ history: history });
try {
const result = await chat.sendMessage(message.content);
if (!result) return NextResponse.json({ error: "Not found message" });
// ボットメッセージ
const botMessage: Message = {
sender: "bot",
content: result.response.text()
}
const data = {
client: message,
// ボットメッセージ
bot: botMessage,
}
return NextResponse.json(data);
} catch (error) {
console.log(error)
return NextResponse.json({ error: "Gemini API error" });
}
}
- try-catchを利用して、Exception処理
Postman
動作確認
Postmanでチャットメッセージを変更して、Gemini APIでレスポンスを確認してみましょう。

ソース
app/api/chat/route.ts
import { NextRequest, NextResponse } from "next/server";
import { Content, GoogleGenerativeAI } from "@google/generative-ai";
import { Message } from "@/app/interfaces/Message";
const history: Content[] = [];
export async function POST(req: NextRequest) {
const API_KEY = process.env.GEMINI_API_KEY;
if (!API_KEY) return NextResponse.json({ error: "Not found API KEY" });
const message:Message = await req.json();
if (!message) return NextResponse.json({ error: "Not found message" });
const genAI = new GoogleGenerativeAI(API_KEY);
const model = genAI.getGenerativeModel({ model: "gemini-1.5-flash" });
const chat = model.startChat({ history: history });
try {
const result = await chat.sendMessage(message.content);
if (!result) return NextResponse.json({ error: "Not found message" });
const botMessage: Message = {
sender: "bot",
content: result.response.text()
}
const data = {
client: message,
bot: botMessage,
}
return NextResponse.json(data);
} catch (error) {
return NextResponse.json({ error: "Gemini API error" });
}
}
App Router
App Routerとは
App Routerは、Next.js 13から導入された新しいルーティングシステムで、従来のファイルベースのルーティングより柔軟で強力な機能となりました。
App Routerの主要機能
ファイルベースのルーティング
Next.jsではファイル構造に基づいて自動的にルーティングされます。 appディレクトリ内のファイルとフォルダがそのままルートになります。
Layoutとページの分離
レイアウト「app/layout.tsx」を定義することで、各ページ間で共有レイアウトを利用できます。
Server ComponentsとClient Components
Next.jsでは、Server ComponentsとClient Componentsの両方をサポートします。SSR(サーバー側でレンダリングされるコンポーネント)とCSR(クライアント側で動作するコンポーネント)を使い分けることができます。
トップページのルーティング
App Routerでは「app」ディレクトリの中に「page.*」ファイルが存在すると自動ルーティングされます。
- 拡張子の部分は「jsx」「tsx」
レイアウトとコンポーネント
Next.jsではデフォルトでは「layout.tsx」がレイアウト、「page.tsx」がページコンポーネントになっています。
トップページ
URI | レイアウト | ページコンポーネント |
---|---|---|
http://localhost:3000/ | app/layout.tsx | app/page.tsx |

フロントエンド作成
ファイル構成
my-app/
├── app/
│ ├── api/
│ │ └── chat/
│ │ └── route.ts
│ └── page.tsx
├── styles/
│ └── globals.css
├── .env.local
├── tailwind.config.js
├── tsconfig.json
└── package.json
レイアウト
レイアウト修正
レイアウトファイル「app/layout.tsx」を開きます。

children
Next.jsのレイアウトファイルは、{children} の部分に各ページコンポーネントが表示されるように設計されています。
app/layout.tsx
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body className="container">{children}</body>
</html>
);
}
Metadata修正
「metadata」 の「title」「description」を修正します。
app/layout.tsx
// Metadata修正
export const metadata: Metadata = {
title: "Gemini Chatbot",
description: "Gemini APIを利用したチャットボットアプリです。",
};
CSS修正
「app/layout.tsx」のデフォルトは、同階層の「globals.css」を読み込んでいます。
app/layout.tsx
import type { Metadata } from "next";
// globals.css 読み込み
import "./globals.css";
「app/globals.css」を開きます。

余分なCSSを削除し、TailwindCSSだけにします。
@tailwind base;
@tailwind components;
@tailwind utilities;
動作確認
ページタイトルや説明文が変更されました。このように各ページの共通部分(HTMLの基本構造やナビゲーションなど)はレイアウトで管理します。

トップページ修正
今回のページはCSRに対応したClient Componentで作成します。
ページコンポーネントを開く
トップページのコンポーネント「app/page.tsx」を開きます。

use client
先頭にuse clientを追加してClient Componentにします。
app/page.tsx
// 追加:Client Componentとする
'use client';
import { useState } from "react";
ページコンポーネント修正
JSXを修正します。
app/page.tsx
'use client';
export default function Home() {
return (
<main className="flex flex-col justify-center">
<h1 className="text-2xl p-5">Gemini Chatbot</h1>
</main>
);
}
動作確認
トップページが変更されました。

メッセージ入力処理
フォーム作成
メッセージの入力フォームを作成します。
app/page.tsx
'use client';
export default function Home() {
return (
<main className="flex flex-col justify-center">
<div className="bg-white shadow-md p-4 z-10">
<h1 className="text-2xl p-5">Gemini Chatbot</h1>
<div className="flex">
<input
type="text"
className="w-full p-2 border border-gray-300 rounded-l mr-0"
placeholder="Type your message..."
/>
<button className="w-1/6 bg-blue-500 text-white p-2 rounded-r">
Send
</button>
</div>
</div>
</main>
);
}
フォーム確認
入力フォームが表示されるか確認します。

イベント処理
テキストボックスに入力したデータを設定するようにします。
データ定義
入力データの定義します。
app/page.tsx
export default function Home() {
// 入力データ定義
const [inputMessage, setInputMessage] = useState<string>('');
...
}
イベントハンドラー作成
changeMessageHandler() を追加します。
app/page.tsx
export default function Home() {
const [inputMessage, setInputMessage] = useState<string>('');
const changeMessageHandler = (e: React.ChangeEvent<HTMLInputElement>) => {
};
....
}
テキストボックスの値を inputMessage() に設定します。
app/page.tsx
export default function Home() {
const [inputMessage, setInputMessage] = useState<string>('');
const changeMessageHandler = (e: React.ChangeEvent<HTMLInputElement>) => {
// デバック用
console.log(e.target.value);
// テキストボックスのデータを inputMessage に設定
setInputMessage(e.target.value);
};
....
}
イベント追加
inputタグにonChangeイベントを追加し、値には「inputMessage」を設定します。
app/page.tsx
export default function Home() {
...
return (
<main className="flex flex-col justify-center">
<div className="bg-white shadow-md p-4 z-10">
<h1 className="text-2xl p-5">Gemini Chatbot</h1>
<div className="flex">
<input
onChange={changeMessageHandler}
value={inputMessage}
type="text"
className="w-full p-2 border border-gray-300 rounded-l mr-0"
placeholder="Type your message..."
/>
<button className="w-1/6 bg-blue-500 text-white p-2 rounded-r">
Send
</button>
</div>
</div>
</main>
);
}
動作確認
テキストボックスに入力するたびに、メッセージがコンソール表示されるか確認します。

クリックイベント
ボタンをクリックしたら、メッセージをAPIに送信するようにします。
イベントハンドラー追加
ボタンクリックのイベントハンドラーを作成します。
app/page.tsx
export default function Home() {
...
const sendHandler = async (e: React.MouseEvent<HTMLButtonElement>) => {
if (inputMessage.trim() === '') return;
setInputMessage('');
};
...
}
イベント追加
buttonタグにonClickイベントを追加します。
app/page.tsx
export default function Home() {
...
return (
<main className="flex flex-col justify-center">
<div className="bg-white shadow-md p-4 z-10">
<h1 className="text-2xl p-5">Gemini Chatbot</h1>
<div className="flex">
<input
onChange={changeMessageHandler}
value={inputMessage}
type="text"
className="w-full p-2 border border-gray-300 rounded-l mr-0"
placeholder="Type your message..."
/>
<button className="w-1/6 bg-blue-500 text-white p-2 rounded-r"
onClick={sendHandler}>
Send
</button>
</div>
</div>
</main>
);
}
動作確認
【Send】ボタンをクリックして、テキストボックスがブランクになるか確認します。