ハッカソン個人備忘録⑫：FastAPI × Next.js × MySQL をつなぐ！CORS対応と接続設定まとめ

## はじめに
FastAPI を使って MySQL と連携したバックエンド API を構築する中で、Next.js などのフロントエンドとの通信時に CORS エラーや接続問題に悩まされました。

:::note info
**個人の備忘録程度の走り書きとなっておりますが、温かい目で見守っていただければ幸いです。**
:::

今回はそれらの課題を解決し、Docker 上でも安定稼働する構成を整備するための実装と設定を整理します。

## 書こうと思ったきっかけ
ITスクールでのチーム開発の中で、FastAPI と MySQL を Docker 上に載せて実行しつつ、Next.js フロントエンドから呼び出す構成を組んでいました。その際に CORS エラーや DB 接続不良に悩まされたため、次回以降スムーズに構築できるよう備忘録として記録しています。

## 実装の目的とポイント
- FastAPI + MySQL を組み合わせたAPI構築
- CORS対応 により、Next.js など外部フロントエンドからのアクセス許可
- 接続リソースの安定性（コネクションクローズなど）

## Next.js フロントエンドの修正

Next.js 側の `src/app/page.tsx` を以下のように修正することで、FastAPI の `/` エンドポイントからメッセージを取得し、画面上に表示できるようにしました。

```tsx
"use client";

import { useEffect, useState } from "react";
import Image from "next/image";

export default function Home() {
  const [message, setMessage] = useState("Loading...");

  useEffect(() => {
    fetch("http://localhost:8000/") // FastAPI の URL に合わせて変更
      .then((res) => res.json())
      .then((data) => setMessage(data.message))
      .catch(() => setMessage("Error fetching message from FastAPI."));
  }, []);

  return (
    <div className="grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20 font-[family-name:var(--font-geist-sans)]">
      <main className="flex flex-col gap-[32px] row-start-2 items-center sm:items-start">
        <Image
          className="dark:invert"
          src="/next.svg"
          alt="Next.js logo"
          width={180}
          height={38}
          priority
        />

        {/* 🔽 FastAPI のメッセージ表示部分 🔽 */}
        <div className="text-lg text-center sm:text-left font-bold text-blue-600">
          {message}
        </div>

        <ol className="list-inside list-decimal text-sm/6 text-center sm:text-left font-[family-name:var(--font-geist-mono)]">
          <li className="mb-2 tracking-[-.01em]">
            You are now connected to FastAPI backend!
          </li>
          <li className="tracking-[-.01em]">
            Edit <code className="bg-black/[.05] dark:bg-white/[.06] px-1 py-0.5 rounded font-semibold">src/app/page.tsx</code> to customize.
          </li>
        </ol>

        <div className="flex gap-4 items-center flex-col sm:flex-row">
          <a
            className="rounded-full border border-solid border-transparent transition-colors flex items-center justify-center bg-foreground text-background gap-2 hover:bg-[#383838] dark:hover:bg-[#ccc] font-medium text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 sm:w-auto"
            href="https://vercel.com/new"
            target="_blank"
            rel="noopener noreferrer"
          >
            <Image
              className="dark:invert"
              src="/vercel.svg"
              alt="Vercel logomark"
              width={20}
              height={20}
            />
            Deploy now
          </a>
          <a
            className="rounded-full border border-solid border-black/[.08] dark:border-white/[.145] transition-colors flex items-center justify-center hover:bg-[#f2f2f2] dark:hover:bg-[#1a1a1a] hover:border-transparent font-medium text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 w-full sm:w-auto md:w-[158px]"
            href="https://nextjs.org/docs"
            target="_blank"
            rel="noopener noreferrer"
          >
            Read our docs
          </a>
        </div>
      </main>

      <footer className="row-start-3 flex gap-[24px] flex-wrap items-center justify-center">
        <a
          className="flex items-center gap-2 hover:underline hover:underline-offset-4"
          href="https://nextjs.org/learn"
          target="_blank"
          rel="noopener noreferrer"
        >
          <Image
            aria-hidden
            src="/file.svg"
            alt="File icon"
            width={16}
            height={16}
          />
          Learn
        </a>
        <a
          className="flex items-center gap-2 hover:underline hover:underline-offset-4"
          href="https://vercel.com/templates?framework=next.js"
          target="_blank"
          rel="noopener noreferrer"
        >
          <Image
            aria-hidden
            src="/window.svg"
            alt="Window icon"
            width={16}
            height={16}
          />
          Examples
        </a>
        <a
          className="flex items-center gap-2 hover:underline hover:underline-offset-4"
          href="https://nextjs.org"
          target="_blank"
          rel="noopener noreferrer"
        >
          <Image
            aria-hidden
            src="/globe.svg"
            alt="Globe icon"
            width={16}
            height={16}
          />
          Go to nextjs.org →
        </a>
      </footer>
    </div>
  );
}
```

## FastAPI バックエンドの修正

### 修正前のコード問題点
- CORSの設定がないため、外部フロントからブロックされる
- DB接続が close されておらず、リソースリークの懸念

### 修正済みの `main.py`

```python
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
import mysql.connector
import os

app = FastAPI()

# ✅ CORS設定（Next.jsからアクセス可能にする）
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],  # 必要なら "http://localhost:3000" などに限定
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

@app.get("/")
def read_root():
    return {"message": "Hello from FastAPI + MySQL in Docker!"}

@app.get("/db-status")
def db_status():
    try:
        connection = mysql.connector.connect(
            host="db",  # ← Dockerコンテナ名
            user=os.getenv("MYSQL_USER"),
            password=os.getenv("MYSQL_PASSWORD"),
            database=os.getenv("MYSQL_DATABASE")
        )
        if connection.is_connected():
            connection.close()  # ✅ 接続が確認できたら明示的にクローズ
            return {"db_status": "connected"}
        else:
            return {"db_status": "not connected"}
    except Exception as e:
        return {"db_status": "error", "details": str(e)}
```

## 環境変数 .env

```env
MYSQL_ROOT_PASSWORD=rootpassword
MYSQL_DATABASE=fastapi_db
MYSQL_USER=fastapi_user
MYSQL_PASSWORD=fastapi_pass
```

## 動作確認

### API起動コマンド：
```bash
uvicorn main:app --host 0.0.0.0 --port 8000
```

### エンドポイント：
- `http://localhost:8000/`
- `http://localhost:8000/db-status`

## まとめ
この構成は、学習用でも実践でも役立つ FastAPI + MySQL + Docker の最小連携パターンです。

:::note warn
***CORS や DB の扱いに注意すれば、フロントエンドともシームレスに連携できます。***
:::

今後はこれをベースに docker-compose 化やセキュリティ面の最適化にも取り組んでいきたいと思います！