10
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

docker composeで手軽にReact+FastAPI+MySQLお試しローカル環境を作る

Posted at

docker composeで手軽にReact+FastAPI+MySQLお試しローカル環境を作る

動機

  • Reactを使った静的ホスティングなるものをやってみたい。
  • 動的なコンテンツはFastAPIで作って適宜呼び出したい。
  • FastAPIとデータベースはつなげておいて、FastAPIサーバーは将来的にサーバーレスみたいにしたい。

この記事の目的

Reactで生成したページから、FastAPIで作ったAPIを利用して、MySQLに入っているデータをブラウザ上で確認する。

最初のディレクトリ構成

  • docker-compose.yml
  • client
    • Dockerfile
  • server
    • Dockerfile
    • requirements.txt
    • code
      • db.py
      • main.py
      • model.py
  • mysql
    • conf.d
      • my.cnf
    • initdb.d
      • schema.sql
      • testdata.sql
    • log
      • mysql

Docker系ファイル

docker-compose.yml

docker-compose.yml
version: "3"

services:
  db: 
    container_name: back_db
    image: mysql:5.7
    restart: always
    tty: true
    environment:
      MYSQL_DATABASE: sample_db
      MYSQL_USER: user
      MYSQL_PASSWORD: password
      MYSQL_ROOT_PASSWORD: password
    ports: 
      - 3306:3306
    command: --port 3306
    volumes: 
      - ./mysql/initdb.d:/docker-entrypoint-initdb.d
      - ./mysql/conf.d:/etc/myaql/conf.d
      - ./mysql/log/mysql:/var/log/mysql

  server:
    links:
      - db
    build: ./server/
    tty: true
    container_name: back_fastapi
    working_dir: /usr/src/server
    ports:
      - 8080:8080
    volumes:
      - ./server/code/:/usr/src/server

  client:
    build: ./client/
    container_name: front_react
    ports:
      - 3000:3000
    volumes:
      - ./server/:/var/www/server/
      - ./client/:/var/www/client/
    tty: true
    stdin_open: true
    command: sh -c "cd test_app && npm start"
  • ポイント
    • clientのcommandで、コンテナたちを立ち上げるたびにReactのサーバーを起動している。
    • serverの方も本当はcommandで起動できるのかもしれないが、今回はのちに出てくるFastAPIの方のDockerfileで起動の処理を行った。
    • MySQLの環境変数の設定の項目は、おそらく本来は.envか何かで外に出して、gitignoreさせるのがよろしいとは思う。

server/Dockerfile

Dockerfile
FROM python:3.8
WORKDIR /usr/src/server
ADD ./requirements.txt .
RUN pip install --trusted-host pypi.python.org -r requirements.txt
CMD ["uvicorn", "main:app", "--reload", "--host", "0.0.0.0", "--port", "8080"]
  • ポイント
    • 地味にWORKDIRを記しておくことが大切そう。
    • requirements.txtをコンテナ内に追加して、環境構築をする。
    • CMDコマンドはおそらくdocker-composeのcommandと一緒(確証なし)

client/Dockerfile

Dockerfile
FROM node:latest
WORKDIR /var/www/client/
RUN npm install -g create-react-app
RUN npm install --save react-router-dom
RUN npm install --save prop-types
  • ポイント
    • こちらも環境構築のためにDockerfileを構築した

serverの中身

requirements.txt

requirement.txt
mysqlclient
sqlalchemy
uvicorn
fastapi
  • ポイント
    • 上二つはSQLとつなげるため
    • 下二つはFastAPIサーバーを立てるため

code/main.py

main.py
from fastapi import FastAPI
from typing import List  # ネストされたBodyを定義するために必要
from starlette.middleware.cors import CORSMiddleware  # CORSを回避するために必要
from db import session  # DBと接続するためのセッション
from model import UserTable, User  # 今回使うモデルをインポート

app = FastAPI()

# CORSを回避するために設定
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# ----------APIの実装------------

@app.get("/")
def read_root():
    return {"Hello": "World!!!!"}

# テーブルにいる全ユーザ情報を取得 GET
@app.get("/users")
def read_users():
    users = session.query(UserTable).all()
    return users
  • ポイント
    • ルートでFastAPIをたたくと、{"Hello": "World!!!!"}を返す。
    • /usersでたたくと、DBに入っているusersのキークエリを返す。

code/db.py

db.py
# -*- coding: utf-8 -*-
# DBへの接続設定
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker, scoped_session


# 接続したいDBの基本情報を設定
user_name = "user"
password = "password"
host = "db"  # docker-composeで定義したMySQLのサービス名
database_name = "sample_db"

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

# DBとの接続
ENGINE = create_engine(
    DATABASE,
    encoding="utf-8",
    echo=True
)

# Sessionの作成
session = scoped_session(
    # ORM実行時の設定。自動コミットするか、自動反映するか
    sessionmaker(
        autocommit=False,
        autoflush=False,
        bind=ENGINE
    )
)

# modelで使用する
Base = declarative_base()
# DB接続用のセッションクラス、インスタンスが作成されると接続する
Base.query = session.query_property()

code/model.py

model.py
# -*- coding: utf-8 -*-
# モデルの定義
from sqlalchemy import Column, Integer, String
from pydantic import BaseModel
from db import Base
from db import ENGINE


# userテーブルのモデルUserTableを定義
class UserTable(Base):
    __tablename__ = 'user'
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(30), nullable=False)
    age = Column(Integer)


# POSTやPUTのとき受け取るRequest Bodyのモデルを定義
class User(BaseModel):
    id: int
    name: str
    age: int


def main():
    # テーブルが存在しなければ、テーブルを作成
    Base.metadata.create_all(bind=ENGINE)


if __name__ == "__main__":
    main()

mysqlの中身

conf.d/my.cnf

my.cnf
[mysqld]
character-set-server=utf8
skip-character-set-client-handshake=utf8
default-storage-engine=INNODB
explicit-defaults-for-timestamp=1
general-log=1
general-log-file=/var/log/mysql/mysqld.log

[mysqldump]
default-character-set=utf8

[mysql]
default-character-set=utf8

[client]
default-character-set=utf8
  • ポイント
    • 主に文字コードの設定

initdb.d/schema.sql

schema.sql
CREATE TABLE user (
    id INT NOT NULL AUTO_INCREMENT,
    name VARCHAR(30) NOT NULL,
    age INT,
    PRIMARY KEY (id)
);

initdb.d/testdata.sql

testdata.sql
INSERT INTO user (name, age) VALUES ("saburo", 15);
INSERT INTO user (name, age) VALUES ("jiro", 18);
INSERT INTO user (name, age) VALUES ("taro", 20);

起動コマンド

docker compose run --rm client sh -c "create-react-app test_app"
docker compose up -d --remove-orphans

1行目は最初だけ。reactの初期フォルダーを作成するためのコマンド。
二回目以降は2行目だけでOK

最初は
localhost:3000でReactの初期画面
localhost:8080で{"Hello": "World!!!!"}
localhost:8080/usersでtestdata.sqlで設定したuserの中身が見えるはず。

ReactからAPIを呼び出す

モジュールのインストール

docker compose exec client bash         
root@xxxxxxxxxxxx:/var/www/client# cd app_test
root@xxxxxxxxxxxx:/var/www/client/app_test# npm install --save axios

App.jsの更新

client/test_app/App.js
import React from "react";
import axios from "axios";

function App() {
	const [data, setData] = React.useState();
	const url = "http://127.0.0.1:8080";

	const GetData = () => {
		axios.get(url).then((res) => {
			setData(res.data);
		});
	};

  const url_users = "http://127.0.0.1:8080/users";

	const GetData_users = () => {
		axios.get(url_users).then((res) => {
			setData(res.data);
		});
	};

	return (
		<div>
			{/* <div>8080</div>
			{data ? <div>{data.Hello}</div> : <button onClick={GetData}>データを取得 Hello</button>} */}
      <div>8080/users</div>
			{data ? <div>{data[1].name}</div> : <button onClick={GetData_users}>データを取得 Users</button>}
		</div>

	);
}

export default App;
  • ポイント
    • jiroが出てくればOK。目的達成
    • React詳しい方はもっとうまいことできるはず

終了コマンド

docker compose stop

参考記事

https://qiita.com/KWS_0901/items/684ac71e728575b6eab0
https://qiita.com/ikeikeda/items/eeed5abb2230bf031ba5
https://qiita.com/A-Kira/items/d2f9c8cef9346cb32229

10
14
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
10
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?