DockerでFastAPI+Next.js+MySQLの環境を構築してみる(Windows)

アプリケーションの定義ファイル(YAML)作成

type nul > docker-compose.yml

下記の内容で編集。

services:
  backend:
    build: ./infra/fastapi
    command: sh -c "python3 -m debugpy --listen 0.0.0.0:5678 -m uvicorn main:app --reload --proxy-headers --host='0.0.0.0' --port=8000"
    ports:
      - "8000:8000"
      - "5678:5678"
    volumes:
      - ./backend:/app

  frontend:
    build: ./infra/nextjs
    tty: true
    volumes:
      - ./frontend:/app
    environment:
      - WATCHPACK_POLLING=true
    command: sh -c "npm run dev"
    ports:
      - "3000:3000"

  db:
    build: ./infra/mysql
    volumes:
      - db-store:/var/lib/mysql
    ports:
      - "3306:3306"

volumes:
  db-store:

backendコンテナ(FastAPI)作成

Dockerファイル作成

ディレクトリ作成。

mkdir -p infra/fastapi

Dockerファイル作成。

type nul > infra/fastapi/Dockerfile

下記の内容で編集。

# ベースイメージを指定
FROM python:3.9

# 作業ディレクトリを作成
WORKDIR /app

# 必要なパッケージをインストール
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# アプリケーションのソースコードをコピー
COPY . .

# アプリケーションを起動
CMD ["uvicorn", "main:app", "--reload", "--host", "0.0.0.0", "--port", "8000"]

requirements.txt作成

type nul > infra/fastapi/requirements.txt

uvicorn
fastapi
mysqlclient
sqlalchemy
debugpy

frontendコンテナ(Next.js)作成

Dockerファイル作成

ディレクトリ作成。

mkdir infra/nextjs

ファイル作成。

type nul > infra/nextjs/Dockerfile

下記の内容で編集。

FROM node:24.15.0

WORKDIR /app

DBコンテナ(MySQL)作成

Dockerファイル作成

ディレクトリ作成。

mkdir infra/mysql

ファイル作成。

type nul > infra/mysql/Dockerfile

下記の内容で編集。

FROM mysql/mysql-server:8.0

ENV MYSQL_DATABASE=odaneko \
  MYSQL_USER=odaneko \
  MYSQL_PASSWORD=odaneko \
  MYSQL_ROOT_PASSWORD=odaneko \
  TZ=Asia/Tokyo

COPY ./my.cnf /etc/my.cnf
RUN chmod 644 /etc/my.cnf

confファイル作成

type nul > infra/mysql/my.cnf

下記の内容で編集。

[mysqld]
# default
skip-host-cache
skip-name-resolve
datadir = /var/lib/mysql
socket = /var/lib/mysql/mysql.sock
secure-file-priv = /var/lib/mysql-files
user = mysql

pid-file = /var/run/mysqld/mysqld.pid

# character set / collation
character_set_server = utf8mb4
collation_server = utf8mb4_ja_0900_as_cs_ks

# timezone
default-time-zone = SYSTEM
log_timestamps = SYSTEM

# Error Log
log-error = mysql-error.log

# Slow Query Log
slow_query_log = 1
slow_query_log_file = mysql-slow.log
long_query_time = 1.0
log_queries_not_using_indexes = 0

# General Log
general_log = 1
general_log_file = mysql-general.log

[mysql]
default-character-set = utf8mb4

[client]
default-character-set = utf8mb4

API作成

backendディレクトリ作成

mkdir backend

環境変数作成

type nul > backend/.env

DB_HOST=db:3306
DB_NAME=odaneko
DB_USER=odaneko
DB_PASSWORD=odaneko

db.py作成

MySQL接続用のコードを作成。

type nul > backend/db.py

import os

from sqlalchemy import create_engine
from sqlalchemy.orm import declarative_base, sessionmaker, scoped_session

host = "db:3306"
db_name = "odaneko"
user = "odaneko"
password = "odaneko"

DATABASE = 'mysql://%s:%s@%s/%s?charset=utf8' % (
    user,
    password,
    host,
    db_name,
)

ENGINE = create_engine(
    DATABASE,
    echo=True
)

session = scoped_session(
    sessionmaker(
        autocommit=False,
        autoflush=False,
        bind=ENGINE
    )
)

Base = declarative_base()
Base.query = session.query_property()

main.pyを作成

APIの処理が記載されたプログラムを作成。

type nul > backend/main.py

下記の内容で編集。
DB接続確認を行うAPIです。

from fastapi import FastAPI
from db import session
from sqlalchemy import text

app = FastAPI()

@app.get("/db-test")
def mysql_test():
    query = text("SELECT 1;")
    result = session.execute(query).scalar()

    return {
        "result": result
    }

フロントページ作成

アプリ作成

docker compose run --rm frontend sh -c 'npx create-next-app . --typescript'

環境変数作成

type nul > frontend/.env

BACKEND_API_URL=http://backend:8000

ページ作成

mkdir frontend/app/db-test

type nul > frontend/app/db-test/page.tsx

"use client";

import { useEffect, useState } from "react";

export default function Home() {
  const [result, setResult] = useState<number | null>(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState("");

  useEffect(() => {
    const executeTest = async () => {
      try {
        const response = await fetch("/api/db-test");

        if (!response.ok) {
          throw new Error("API request failed");
        }

        const data = await response.json();

        setResult(data.result);
      } catch (error) {
        setError("APIの呼び出しに失敗しました");
      } finally {
        setLoading(false);
      }
    };

    executeTest();
  }, []);

  return (
    <main>
      <h1>Python API 接続テスト</h1>

      {loading && <p>読み込み中...</p>}

      {result !== null && (
        <p>
          DB検索結果:{result}
        </p>
      )}

      {error && (
        <p>
          エラー:{error}
        </p>
      )}
    </main>
  );
}

API作成

mkdir frontend/app/api/db-test

type nul > frontend/app/api/db-test/route.ts

import { NextResponse } from "next/server";

export async function GET() {
  const response = await fetch(
    `${process.env.BACKEND_API_URL}/db-test`
  );

  const data = await response.json();

  return NextResponse.json(data);
}

コンテナビルド・起動

docker compose up -d --build

Dockerデスクトップでも起動を確認。

環境が構築できたか確認

下記のURLを開いて、テストページが表示されるか確認。
http://localhost:3000/db-test

以上になります。
お読み頂き、ありがとうございました。