Go+Gin × React(Typescript)でシンプルなチャットAPIを作る
はじめに
今回は、GoとReact(Typescript)を使って、フロントエンドからバックエンドAPIを呼び出すシンプルなチャットアプリの基盤を構築します。この記事では、API通信までの一連の流れを紹介します。
これは、将来的にVertex AIなどと組み合わせたチャット形式のAIエージェントを構築していくプロジェクトの最初のステップです。まずはフロントエンドで入力されたメッセージを取得し、サーバーサイドでAPIを叩いてレスポンスを返すという、チャットエージェントにおける基本機能を構築します。
開発環境
OS: macOS Sequoia 15.5
Go: 1.24.2
Gin: 1.10.1
node: 23.11.0
npm: 10.9.2
実装する流れ
- Go + Ginで
/chat
POST APIを構築 - React + TypeScriptのフロントエンドを
create-react-app
で生成 - Reactからfetch POSTでGo APIを呼び出し
- Goが得たメッセージを文字列加工して応答
- Reactでの応答を受け取り表示
backend: Go + Gin
1. Ginプロジェクトを初期化
go mod init badkend
go get github.com/gin-gonic/gin
2. 各フォルダ・ファイルの作成
backendフォルダのディレクトリ構造は以下の通りです。
backend/
├── main.go
├── go.mod
├── go.sum
├── controllers/
│ └── chat_controller.go
├── routes/
│ └── routes.go
└── models/
└── chat.go
今後の拡張性を考慮して、上記のように用途部でフォルダを分けています。
/chat
エンドポイント実装
3. まずはmain.go
のみで記述すると以下のようになります。
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.POST("/chat", func(c *gin.Context) {
var req struct {
Message string `json:"message"`
}
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid input"})
return
}
reply := "受け取ったメッセージ: " + req.Message
c.JSON(http.StatusOK, gin.H{"reply": reply})
})
r.Run(":8080")
}
今後のために、上記のコードを機能ごとに分割します。
package main
import (
"github/k-kanke/backend/routes"
)
func main() {
r := routes.SetupRoutes()
r.Run(":8080")
}
package controllers
import (
"github/k-kanke/backend/models"
"net/http"
"github.com/gin-gonic/gin"
)
func ChatHandler(c *gin.Context) {
var req models.ChatRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid input"})
return
}
resp := models.ChatResponse{
Reply: "受け取ったメッセージ: " + req.Message,
}
c.JSON(http.StatusOK, resp)
}
package routes
import (
"github/k-kanke/backend/controllers"
"github.com/gin-contrib/cors"
"github.com/gin-gonic/gin"
)
func SetupRoutes() *gin.Engine {
r := gin.Default()
r.Use(cors.New(cors.Config{
AllowOrigins: []string{"http://localhost:3000"},
AllowMethods: []string{"GET", "POST"},
AllowHeaders: []string{"Content-Type"},
AllowCredentials: true,
}))
// `/ping` エンドポイントは、サーバーが正常に起動しているかを確認するための
// 一般的なヘルスチェック用のエンドポイントであり、本チャットアプリケーションの
// 主要な機能とは直接関係ありません。
r.GET("/ping", func(c *gin.Context) {
c.JSON(200, gin.H{"message": "pong"})
})
r.POST("/chat", controllers.ChatHandler)
return r
}
上記のroutes.go
ファイルではCORS設定をしています。React(ポート:3000)からGo(ポート:8080)への異なるドメイン間のリクエストを許可するために必要です。
package models
type ChatRequest struct {
Message string `json:"message"`
}
type ChatResponse struct {
Reply string `json:"reply"`
}
frontend: React + TypeScript
- プロジェクト生成
npx create-react-app frontend --template typescript
- Chatコンポーネントの作成
src/components/Chat.tsx
import React, { useState } from "react";
const Chat: React.FC = () => {
const [message, setMessage] = useState('');
const [reply, setReply] = useState('');
const handleSend = async () => {
if (!message.trim()) return;
try {
const res = await fetch('http://localhost:8080/chat', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ message }),
});
const data = await res.json();
setReply(data.reply);
} catch (err) {
console.error('送信エラー: ', err);
setReply('エラーが発生しました');
}
};
return (
<div>
<h1>Zenith</h1>fi
<input
type="text"
value={message}
onChange={(e) => setMessage(e.target.value)}
placeholder="メッセージを入力"
style={{ padding: '0.5rem', width: '300px' }}
/>
<button onClick={handleSend} style={{ marginLeft: '1rem' }}>
送信
</button>
<div style={{ marginTop: '1rem' }}>
<strong>応答:</strong> {reply}
</div>
</div>
);
};
export default Chat;
-
App.tsx
Chat
コンポーネントはApp.tsx
で以下のように呼び出されています。
import './App.css';
import Chat from './components/Chat';
function App() {
return (
<div className="App">
<Chat />
</div>
);
}
export default App;
動作確認
Goサーバー起動
go run main.go
Reactサーバー起動
npm start
ブラウザでlocalhost:3000
にアクセスすると以下のような画面になります。
テストとして"Hello World!!"というメッセージを入力し、送信すると以下のような挙動が見られました。
正常に動いていることが確認できました。
おわりに
今回は以下のフローを実装しました。
[1] ユーザーがブラウザでメッセージを入力して「送信」ボタンを押す
[2] ReactがfetchでJSONデータをGoサーバーにPOST(/chat)
[3] Gin (Go) がPOSTリクエストを受け取り、メッセージをJSONから取得
[4] Ginが加工した応答をJSONで返す
[5] Reactがレスポンスを受け取り、画面に表示
API通信の基礎が確立できたことで、今後はこの上に様々な機能を追加していく予定です。
Discussion