【React + TypeScript】DBと接続してみた

## 環境
- React 18.3.1
- Typescript 5.5.3
- node.js 20.5.0
- MySQL 8.0.31

## はじめに
TODOアプリを作成する中でデータベースに登録できるようにしたなと思い実装してみました。
ローカルにMySQLの環境は構築していたので今回はローカルのMySQLとの接続を行いました。
初めてフロントエンドとバックエンド、データベースとの接続まで実装したので備忘録的な感じで残しておきます。

## 準備
Node.jsとMySQLを連携させるのに以下のライブラリをインストールしました。
- express : サーバー構築用フレームワーク
- mysql2 : Node.jsでMySQLを扱うためのライブラリ

以下のコマンドでインストールができます。

```
npm install express mysql2
```

## 全体の構造
ざっくりと全体の構造を紹介します。

### MainLayout.tsx (フロントエンド)

ユーザーがアプリケーションのUIを操作します。タスクを登録する際に、handleAddTask関数を利用してバックエンドのエンドポイント（/api/add-task）にリクエストを送信します。
フロントエンドの全体像は省略しています。

### server.js (バックエンドのエントリーポイント)

サーバー全体の設定を行い、クライアントからのリクエストを適切なルート（taskRoutes.js）に振り分けます。

### taskRoutes.js (バックエンドのルーティング)

サーバーに送られてきたリクエストを適切なコントローラー関数（taskController.js内）に渡します。

### taskController.js (バックエンドのコントローラー)

リクエストを処理し、DB操作を行います。このファイルでは、受信したリクエストデータを検証した上で、db.jsを使ってMySQLクエリを実行します。

### db.js (データベース接続)

MySQLとの接続を管理し、taskController.jsからのクエリを実行します。

## データの流れ

### 1.ユーザー操作 (フロントエンド)
MainLayout.tsx内でタスク登録ボタンがクリックされると、handleAddTask関数が呼ばれます。この関数がfetchを使ってバックエンドの/api/add-taskエンドポイントにデータを送信します。
```typescript:MainLayout.tsx
const handleAddTask = async (newTaskContent: string) => {
  const newTask = {
    task_content: newTaskContent,
    category_id: selectedCategory,
  };

  const response = await fetch("http://localhost:3000/api/add-task", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(newTask),
  });

  if (response.ok) {
    alert("タスクが登録されました");
  } else {
    alert("登録に失敗しました");
  }
};
```

### 2.リクエスト受信 (バックエンド)
server.jsがクライアントから送られたリクエストを受信します。このリクエストは/apiプレフィックスに基づいてtaskRoutes.jsに渡されます。

```javascript:server.js
import express from 'express';
import cors from 'cors';

const app = express();

// JSONリクエストの解析
app.use(express.json());

// APIルートの設定
import taskRoutes from './routes/taskRoutes.js';
app.use('/api', taskRoutes);

// サーバーの起動
app.listen(3000, () => {
  console.log("サーバーがポート3000で起動しました");
});
```

### 3.ルート処理 (バックエンド)
/add-taskのリクエストがtaskController.js内のaddTask関数に渡されます。
```javascript:taskRoutes.js
import express from 'express';
import { addTask } from '../controllers/taskController.js';

export const router = express.Router();
router.post('/add-task', addTask); // リクエストをaddTaskに渡す
```

### 4.コントローラー処理 (バックエンド)
addTask関数がリクエストを受け取り、データの検証を行った後、db.jsを使ってMySQLクエリを実行します。

```javascript: taskController.js
import db from '../config/db.js';

export const addTask = (req, res) => {
  const { task_content, category_id } = req.body;

  if (!task_content || !category_id) {
    return res.status(400).json({ error: "タスク内容とカテゴリーIDが必要です" });
  }

  const query = `INSERT INTO planned_tasks (task_content, category_id) VALUES (?, ?)`;
  db.query(query, [task_content, category_id], (err, result) => {
    if (err) {
      console.error("DB登録エラー:", err);
      return res.status(500).json({ error: "DB登録に失敗しました" });
    }
    res.status(201).json({ message: "タスクが登録されました", result });
  });
};
```

### 5.データベース操作 (バックエンド)
taskController.jsから渡されたクエリをMySQLに送信し、結果を返します。

```javascript:db.js
import mysql from 'mysql2';

const db = mysql.createConnection({
  host: 'localhost',
  user: 'root',
  password: 'your_password',
  database: 'task_management',
});

db.connect((err) => {
  if (err) {
    console.error('MySQL接続エラー:', err);
    return;
  }
  console.log('MySQLに接続されました');
});

export default db;
```

## 直面した問題と解決方法
### 問題
クライアント（ブラウザ）からのリクエストがバックエンドサーバーに到達できずに苦戦しました。
エラーログに「 **Failed to load resource: net::ERR_FAILED**」と表示されていたのでいろいろ調べた結果フロントエンドとバックエンドのポート番号が違うので**CORSエラー**が発生していました。
CORSの設定を行い、フロントエンドとバックエンド間の通信を有効にする必要があるようです。

フロントエンド：http://localhost:5173/
バックエンド：http://localhost:3000/


### 解決方法

#### 1. CORSをインストール
バックエンドのNode.jsプロジェクトでCORSを管理するミドルウェアをインストールします。
```
npm install cors
```

package.jsonに以下のように表示されればOK
```javascript
  "dependencies": {
  
    "cors": "^2.8.5",
  },
```

#### 2.メインのサーバーファイルにCORSの設定を記述
```diff_javascript:server.js
import express from 'express';
+ import cors from 'cors';

const app = express();

// JSONリクエストの解析
app.use(express.json());

// CORSの設定
+ app.use(cors({
+ origin: 'http://localhost:5173', // フロントエンドのURL
+ methods: ['GET', 'POST'], // 許可するHTTPメソッド
+ allowedHeaders: ['Content-Type'], // 許可するヘッダー
+ }));

// APIルートの設定
import taskRoutes from './routes/taskRoutes.js';
app.use('/api', taskRoutes);

// サーバーの起動
app.listen(3000, () => {
  console.log("サーバーがポート3000で起動しました");
});
```
この対応でクライアントからのリクエストがバックエンドサーバーに届くようになりました。

## まとめ
無事にデータベースに登録できるところまで実装できたので良かったです。
フロントエンドとバックエンドのデータの流れとデータベースとの接続方法を学べたことが大きな収穫でした。