【完全テンプレ】Next.js + OpenAI Realtime APIで作るリアルタイム音声チャット（2025年最新）

# この記事でわかること
✅ Next.js × OpenAI Realtime APIで音声チャットを最短構築
✅ OpenAIのEphemeral Keyを使った最新構成
✅ WebRTCを使ったリアルタイム音声ストリーミングの実装

# こんな人にオススメ
- ChatGPTを音声で使えるWebアプリを作りたい
- OpenAI Realtime APIの最新ベストプラクティスを知りたい
- Next.js x OpenAI x WebRTCの実践コードが欲しい

# GitHub
https://github.com/dev-cat-v/openai-reaitime-api

## 環境構築
### 1 Next.jsプロジェクトを作成します。
```shell
npx create-next-app --example with-docker {your project name}
```

### 2 GitHub でリポジトリを作成し、ローカルの内容をPushします。（任意）
```shell
cd {your project name}
git remote add origin https://github.com/{your git account id}/{your git repository url}
git branch -M main
git push -u origin main
```

### 3 Tailwind-CSS をインストールします。（任意）
- Tailwind CSSをインストールする
```shell
npm install tailwindcss @tailwindcss/postcss postcss
```

- PostCSS設定にTailwindを追加する
```postcss.config.mjs
export default {
  plugins: {
    "@tailwindcss/postcss": {},
  }
}
```

- Tailwind CSSをインポートする
```globals.css
@import "tailwindcss";
```

### 4 .env.local にAPIキー設定
```.env.local
OPENAI_API_KEY=sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
```

## リソースの配置
### /pages/api/openai-realtime/init.ts
OpenAIにエフェメラルキーをリクエストするAPI。

```typescript
import type { NextApiRequest, NextApiResponse } from 'next';

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
  if (req.method !== 'GET') {
    return res.status(405).json({ error: 'Method Not Allowed' });
  }

  const apiKey = process.env.OPENAI_API_KEY;
  if (!apiKey) {
    return res.status(500).json({ error: 'API Key not found' });
  }

  const response = await fetch("https://api.openai.com/v1/realtime/sessions", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${apiKey}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      model: "gpt-4o-realtime-preview-2024-12-17",
      voice: "verse",
    }),
  });

  if (!response.ok) {
    const errorText = await response.text();
    console.error('Failed to create session:', errorText);
    return res.status(response.status).json({ error: 'Failed to create session', details: errorText });
  }

  const data = await response.json();
  res.status(200).json({ ephemeralKey: data.client_secret.value });
}
```

### /app/pages/openai-realtime.tsx
音声チャットのUIとWebRTC接続処理。
```typescript
import { useState, useRef } from 'react';

export default function Chat() {
  const [isChatting, setIsChatting] = useState(false);
  const peerConnection = useRef<RTCPeerConnection | null>(null);
  const dataChannelRef = useRef<RTCDataChannel | null>(null);

  const startChat = async () => {
    const res = await fetch('/api/openai-realtime/init');
    const { ephemeralKey } = await res.json();

    const pc = new RTCPeerConnection();

    const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
    stream.getTracks().forEach(track => pc.addTrack(track, stream));

    const audioEl = document.createElement('audio');
    audioEl.autoplay = true;
    pc.ontrack = (e) => audioEl.srcObject = e.streams[0];

    const dc = pc.createDataChannel('oai-events');
    dataChannelRef.current = dc;

    dc.onmessage = () => {};

    const offer = await pc.createOffer();
    await pc.setLocalDescription(offer);

    const sdpResponse = await fetch(`https://api.openai.com/v1/realtime?model=gpt-4o-realtime-preview-2024-12-17`, {
      method: 'POST',
      headers: {
        Authorization: `Bearer ${ephemeralKey}`,
        'Content-Type': 'application/sdp',
      },
      body: offer.sdp,
    });

    const sdpText = await sdpResponse.text();
    const answer: RTCSessionDescriptionInit = {
      type: 'answer',
      sdp: sdpText,
    };
    await pc.setRemoteDescription(answer);

    peerConnection.current = pc;
    setIsChatting(true);
  };

  const stopChat = () => {
    peerConnection.current?.close();
    dataChannelRef.current?.close();
    setIsChatting(false);
  };

  return (
    <div className="p-4">
      <h1 className="text-xl font-bold">リアルタイム音声チャット</h1>

      <div className="mt-4">
        {isChatting ? (
          <button onClick={stopChat} className="px-4 py-2 bg-red-500 text-white rounded">
            チャット終了
          </button>
        ) : (
          <button onClick={startChat} className="px-4 py-2 bg-blue-500 text-white rounded">
            チャット開始
          </button>
        )}
      </div>
    </div>
  );
}
```

## 動作確認
### 開発サーバー起動
```bash
npm run dev
```

### ブラウザでアクセス
```
http://localhost:3000/openai-realtime
```

# 完成！！　お疲れ様です
✅ Next.jsで超シンプルに音声チャット
✅ OpenAI Realtime APIの正式対応構成
✅ Ephemeral Keyによる安全設計
✅ WebRTCでリアルタイム処理

# さらにこんなカスタマイズもオススメ！
- 会話履歴を保存して「チャットログ機能」追加
- スマホ対応デザイン（録音中アイコンとか）
- サーバー側で音声保存してAI音声分析

# そのままデプロイしたい方は！
こちらの記事で、Cloud Runを使って秒速デプロイする方法をご紹介しています！
https://qiita.com/dev-cat/items/0de2ca8168684fc21a69

### 🔥 最先端のリアルタイムAI体験を、Next.jsでサクッと実現しよう！
### ⭐️ この記事が役に立ったら「いいね」＆「ブクマ」よろしくお願いします！