0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Codex SDKを使って爆速🚀でWEBチャットアプリ💬を作ってみた

Last updated at Posted at 2025-10-18

screenshot.png

はじめに

普段から業務を中心に、AI コーディングアシスタントを積極的に活用しています。以前は Claude Code を使っていましたが、最近ではほぼ Codex 一択になりました。そして先日、OpenAI から Codex の一般提供が正式に発表され、大きな話題となっています。

この機会に、Codex SDK の実力を試してみたいと考え、実際に Web チャットアプリを作ってみることにしました。

AI とチャットできる Web アプリを作るとなると、通常は WebSocket の実装やストリーミング処理、状態管理など、考えることがたくさんあって大変です。しかし Codex SDK を使えば、そんな悩みを一気に解決できます。

結果として、わずか数時間 で本格的な AI チャットアプリが完成しました。その手順と魅力を、実際のコードを交えながら詳しくご紹介します!

リポジトリ

完全なソースコードは GitHub で公開しています:

https://github.com/nogataka/codex-sdk-ts-sample

完成したアプリの特徴

まずは完成品の主な機能をご覧ください:

  • マルチターン会話対応 - 文脈を理解した自然な会話ができる
  • スレッド管理機能 - 複数のチャット履歴を保持・切り替え可能
  • リアルタイム応答 - ストリーミングでサクサク動作
  • 美しいUI - グラデーションを活かしたモダンなデザイン
  • レスポンシブ対応 - PC でもスマホでも快適に使える

これらすべてが、TypeScript + React + Express というシンプルな構成で実現できました。

なぜ Codex SDK を選んだのか?

Codex SDK の最大の魅力は「簡単さ」です。通常、AI チャットアプリを作るには以下のような実装が必要になります:

  • OpenAI API との通信処理
  • ストリーミングレスポンスの処理
  • 会話履歴の管理
  • エラーハンドリング
  • レート制限対策

しかし Codex SDK を使えば、これらが たった数行のコード で実現できます。

アーキテクチャ解説

このアプリは以下の3層構造になっています:

各層が疎結合になっているため、拡張も容易です。

実装のポイント

1. バックエンド: わずか100行以下で完結

バックエンドのコアロジックは驚くほどシンプルです。backend/src/index.ts のメイン処理を見てみましょう:

// Codex SDK クライアントの初期化
const codex = new Codex();

// API エンドポイントの実装
app.post('/api/run', async (req, res) => {
  const { prompt, threadId } = req.body;

  // スレッドの作成または再開
  const thread = threadId
    ? await codex.threads.resume(threadId)
    : await codex.threads.create({ workdir });

  // Codex を実行してレスポンスを取得
  const turn = await thread.runStreamed(prompt);

  // 結果を返す
  res.json({
    threadId: thread.id,
    finalResponse: turn.finalResponse,
    items: turn.items
  });
});

たったこれだけで、以下の機能がすべて動きます:

  • AI との通信
  • ストリーミング処理
  • 会話履歴の管理
  • エラーハンドリング

Codex SDK が裏側で複雑な処理を全部やってくれるので、開発者はビジネスロジックに集中できます。

2. スレッド管理: 継続的な会話を実現

Codex SDK の「スレッド」機能が素晴らしいです。各スレッドは独立した会話コンテキストを持ち、以下のように使い分けられます:

// 新しい会話を始める
const newThread = await codex.threads.create(options);

// 既存の会話を続ける
const existingThread = await codex.threads.resume(threadId);

フロントエンドでは、このスレッドIDをキーにして会話履歴を管理しています:

// スレッドごとに会話履歴を保存
const [turnsByThreadId, setTurnsByThreadId] = useState<
  Record<string, Array<{ prompt: string; response: string }>>
>({});

// スレッド切り替え
const handleSelectThread = (id: string) => {
  setThreadId(id);
  setTurns(turnsByThreadId[id] || []);
};

これにより、複数のチャットを並行して管理でき、いつでも過去の会話に戻れます。

3. フロントエンド: React Hooks でシンプルに

frontend/src/App.tsx では、React の useState フックだけで状態管理を実装しています。Redux や Zustand のような複雑なライブラリは不要です:

const [prompt, setPrompt] = useState('');       // 入力中のメッセージ
const [threadId, setThreadId] = useState('');   // アクティブなスレッドID
const [turns, setTurns] = useState([]);         // 現在の会話履歴
const [loading, setLoading] = useState(false);  // ローディング状態
const [error, setError] = useState('');         // エラーメッセージ

フォーム送信時の処理もシンプルです:

const handleSubmit = async (e: React.FormEvent) => {
  e.preventDefault();
  setLoading(true);
  setError('');

  try {
    // バックエンドに送信
    const response = await fetch('/api/run', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ prompt, threadId }),
    });

    const data = await response.json();

    // 会話履歴を更新
    const newTurn = { prompt, response: data.finalResponse };
    setTurns([...turns, newTurn]);
    setThreadId(data.threadId);
    setPrompt('');
  } catch (err) {
    setError('エラーが発生しました');
  } finally {
    setLoading(false);
  }
};

非同期処理、エラーハンドリング、状態更新がすべて含まれていますが、見通しが良く理解しやすいコードになっています。

4. バリデーション: Zod で型安全に

バックエンドでは Zod を使ってリクエストを検証しています:

const runRequestSchema = z.object({
  prompt: z.string().min(1, 'Prompt cannot be empty'),
  threadId: z.string().optional(),
});

app.post('/api/run', async (req, res) => {
  try {
    // バリデーション
    const { prompt, threadId } = runRequestSchema.parse(req.body);
    // ...処理
  } catch (error) {
    if (error instanceof z.ZodError) {
      return res.status(400).json({ error: error.errors });
    }
    // ...
  }
});

これにより、実行時の型安全性が保証され、不正なリクエストを事前に弾けます。

クイックセットアップ

実際に試してみたい方のために、セットアップ手順をご紹介します。

前提条件

  • Node.js 18以上
  • Codex CLI のインストール
    npm i -g @openai/codex
    # または
    brew install codex
    
  • Azure OpenAI の資格情報 (または OpenAI API キー)

セットアップ手順

リポジトリのクローンから起動まで、わずか 4つのコマンド で完了します:

# 1. リポジトリをクローン
git clone https://github.com/nogataka/codex-sdk-ts-sample.git
cd codex-sdk-ts-sample

# 2. 依存関係をインストール
./setup.sh

# 3. 環境変数を設定
export AZURE_OPENAI_ENDPOINT="https://example.openai.azure.com"
export AZURE_OPENAI_KEY="your-key"
export AZURE_OPENAI_DEPLOYMENT="codex-cli"

# 4. 開発サーバーを起動
./start.sh

start.sh を実行すると、バックエンド(ポート4000)とフロントエンド(ポート5173)が同時に起動します。ブラウザで http://localhost:5173 を開けば、すぐに動作確認できます。

開発体験が最高だった理由

1. セットアップが超簡単

上記の手順の通り、セットアップは非常にシンプルです。スクリプトが用意されているため、数コマンドで完了します。

2. TypeScript で全部書ける

フロントエンドもバックエンドも TypeScript なので、以下のメリットがあります:

  • 型の恩恵を最大限に受けられる - コンパイル時にエラーを検出
  • コードの見通しが良い - 型定義がドキュメントの役割を果たす
  • リファクタリングが安全 - IDE のサポートで大胆に変更できる

特に、Codex SDK が TypeScript で書かれているため、IDE の補完が効いて開発効率が爆上がりしました。

3. Hot Reload で爆速開発

Vite の HMR (Hot Module Replacement) により、コード変更が 即座に ブラウザに反映されます:

// vite.config.ts
export default defineConfig({
  plugins: [react()],
  server: {
    proxy: {
      '/api': 'http://localhost:4000', // バックエンドへプロキシ
    },
  },
});

フロントエンドの開発中も /api/* へのリクエストは自動的にバックエンドにプロキシされるため、CORS の設定に悩むこともありません。

つまずきポイントと解決方法

実際に開発してみて、いくつかハマったポイントもあったので共有します。

1. Codex CLI のインストール

Codex SDK を使うには、事前に Codex CLI をインストールしておく必要があります:

# npm でグローバルインストール
npm i -g @openai/codex

# または Homebrew で
brew install codex

最初これを忘れていて、codex binary not found エラーが出ました。README に書いてあったので、ちゃんと読みましょう(反省)。

2. 環境変数の設定

Azure OpenAI を使う場合、以下の3つの環境変数が必須です:

export AZURE_OPENAI_ENDPOINT="https://your-resource.openai.azure.com"
export AZURE_OPENAI_KEY="your-api-key"
export AZURE_OPENAI_DEPLOYMENT="your-deployment-name"

または ~/.codex/config.toml に設定することもできます:

[azure]
endpoint = "https://your-resource.openai.azure.com"
key = "your-api-key"
deployment = "your-deployment-name"

環境変数を忘れると認証エラーになるので注意してください。

3. 作業ディレクトリの設定

Codex CLI はデフォルトで現在のディレクトリを作業ディレクトリとして使います。別のプロジェクトを対象にしたい場合は、CODEX_WORKDIR 環境変数で指定できます:

export CODEX_WORKDIR=/path/to/your/project
./start.sh

これにより、Codex が参照するファイルやコンテキストを切り替えられます。

UIデザインのこだわりポイント

せっかくなので、UI にもこだわりました。frontend/src/styles.css では以下の工夫をしています:

1. グラデーション背景

紫系のグラデーションで落ち着いた雰囲気に:

body {
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  min-height: 100vh;
}

2. グラスモーフィズム

半透明の背景にぼかしを入れて、モダンな印象に:

.sidebar {
  background: rgba(255, 255, 255, 0.1);
  backdrop-filter: blur(10px);
  border-right: 1px solid rgba(255, 255, 255, 0.2);
}

3. レスポンシブ対応

メディアクエリでモバイルにも対応:

@media (max-width: 960px) {
  .container {
    flex-direction: column;
  }
}

@media (max-width: 640px) {
  .sidebar {
    width: 100%;
  }
}

スマホで見ても快適に使えます!

パフォーマンスの工夫

1. イベントストリーミング

Codex SDK の runStreamed() メソッドを使うことで、リアルタイムでイベントを処理できます:

const turn = await thread.runStreamed(prompt);

// イベントリスナーを登録
for await (const event of turn.events) {
  if (event.type === 'turn.started') {
    console.log('処理開始');
  } else if (event.type === 'item.completed') {
    console.log('アイテム完了:', event.item);
  } else if (event.type === 'turn.completed') {
    console.log('処理完了:', event.turn);
  }
}

現状のサンプルでは最終結果のみを返していますが、この仕組みを使えば「文字が一文字ずつ表示される」ような UX も実装可能です。

2. 軽量なビルド

Vite のビルド最適化により、本番環境では高速に動作します:

cd frontend
npm run build

ビルド成果物は frontend/dist/ に出力され、静的ホスティングサービスにデプロイできます。

今後の拡張アイデア

このサンプルはあくまで基礎ですが、以下のような機能を追加すれば実用的なアプリになります:

1. ストリーミング表示

現在は最終結果のみ表示していますが、イベントストリームを活用すれば ChatGPT のようなリアルタイム表示が可能です:

// フロントエンド側で Server-Sent Events (SSE) を受信
const eventSource = new EventSource('/api/run-stream');
eventSource.onmessage = (event) => {
  const chunk = JSON.parse(event.data);
  setResponse((prev) => prev + chunk.text);
};

2. チャット履歴の永続化

現在はブラウザのメモリに保存していますが、データベースや LocalStorage に保存すれば、ページをリロードしても履歴が残ります:

// LocalStorage に保存
useEffect(() => {
  localStorage.setItem('turnsByThreadId', JSON.stringify(turnsByThreadId));
}, [turnsByThreadId]);

// LocalStorage から復元
useEffect(() => {
  const saved = localStorage.getItem('turnsByThreadId');
  if (saved) {
    setTurnsByThreadId(JSON.parse(saved));
  }
}, []);

3. 認証機能

複数ユーザーに対応するなら、認証が必要です。Firebase Auth や Auth0 を使えば簡単に実装できます:

// Firebase Auth の例
import { getAuth, signInWithPopup, GoogleAuthProvider } from 'firebase/auth';

const auth = getAuth();
const provider = new GoogleAuthProvider();

const handleLogin = async () => {
  const result = await signInWithPopup(auth, provider);
  console.log('ログイン成功:', result.user);
};

4. マークダウン対応

Codex の応答を Markdown でレンダリングすれば、コードブロックやリストが見やすくなります:

import ReactMarkdown from 'react-markdown';

<ReactMarkdown>{response}</ReactMarkdown>

5. 音声入力

Web Speech API を使えば、音声でプロンプトを入力できます:

const recognition = new webkitSpeechRecognition();
recognition.onresult = (event) => {
  const transcript = event.results[0][0].transcript;
  setPrompt(transcript);
};
recognition.start();

まとめ

Codex SDK を使えば、わずか数時間 で本格的な AI チャットアプリが作れました。

良かった点:

  • セットアップが超簡単 (3コマンドで起動)
  • SDK が複雑な処理を隠蔽してくれる
  • TypeScript で型安全に開発できる
  • スレッド管理が優秀で会話の文脈を維持できる
  • Vite の HMR で開発体験が最高

注意点:

  • Codex CLI のインストールが必要
  • 環境変数の設定を忘れずに
  • Docker では動かない (ローカル環境前提)

こんな人におすすめ:

  • AI チャットアプリを作ってみたい初心者
  • プロトタイプを爆速で作りたいエンジニア
  • TypeScript/React の実践的な学習教材を探している人

ぜひこのサンプルをベースに、自分だけの AI アプリを作ってみてください!

参考リンク


この記事が、Codex SDK を使った Web アプリ開発の参考になれば幸いです。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?