React+FastAPI+dockerでWeb開発のお勉強 (dockerコマンド不使用縛りプレイ)

# はじめに

仕事の進みが悪すぎて現実逃避したくなったので、気になっていたReactをさわってみることにしました。

４月ということで、新入社員や、学生の方でも作業しやすいようにGUI操作多めで記述していこうと思います。

バックエンドはPython＋FastAPIでサクッと作って組み合わせていきます。


# 開発環境

以下の環境を準備してください。

インストール手順は説明しませんが、インストーラーのリンクは張っておきます。

- Windows 10 Pro

  Docker Desktop for Windows を使うので、Pro環境を想定。

  簡易的なことしかしないので、dockerが動作すればMac、Linuxでも良いです。

- docker
 
   Docker Desktop for Windows で説明をします。

   CUIに慣れていない人向けにGUIでの操作を記載しますが、詳しい人はCUIでの操作を推奨。

   [インストーラー](https://hub.docker.com/editions/community/docker-ce-desktop-windows)

   Get Dokcerをクリックしてダウンロードし、インストーラーを実行する。


- Visual Studio Code

  [インストーラー](https://code.visualstudio.com/download)

  Windowsをクリックしてダウンロードし、インストーラーを実行する。

  Python,JavaScript,dockerfileの編集を行うので、以下の拡張機能を入れてください。


  - 拡張機能

Visual Studioを起動して、左側の拡張機能のアイコンをクリックし、入力欄で以下を検索してインストールしてください。

| 名前 | 説明 |
|:-----------|:-------------|
| Japanese Language Pack for Visual Studio Code | 日本語環境 |
| Python | Python言語用 |
| Docker | Docker用 |
| Remote - SSH | リモート作業用 |
| Remote - Containers | リモート作業用 |
| Remote - SSH: Explorer | リモート作業用 |
| Remote Development | リモート作業用 |
     



# docker環境

### １．任意のフォルダを作成

エクスプローラーを右クリックして、[新規作成] - [フォルダー] をクリック。任意の名前を設定します。

例:sample

### ２．Visual Studio Codeで開く

1で作成したフォルダを開き、右クリックで、[Codeで開く] をクリックし、Visual Studio Codeで開きます。

### ３．dockerfileの作成

右クリックで[新しいファイル]をクリックします。

![01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264524/569408a7-7c35-cc43-30a3-871aabfe8bc9.png)


ファイル名にdockerfileを指定し、ファイルの内容は以下をコピペします。


```dockerfile

FROM node:lts-alpine

RUN apk update && apk add --virtual=module curl git python3 python3-dev py3-pip

RUN npm install -g create-react-app
RUN npm install axios
RUN pip3 install uvicorn fastapi requests

WORKDIR /usr/src/app
EXPOSE 8000 3000


```

![02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264524/fc65a91c-6344-7b7b-1618-581dc6d12ed2.png)



### ４．dockerfileをビルド

dockerfileを右クリックして、[Build Image...]をクリックします。

![03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264524/af93553c-d71f-6667-fd4c-9bda81bb0fa8.png)


イメージの名前を入力してEnterキー。(デフォルトのままでOK)

例：sample:latest


ダウンロードに少し時間がかかります。

![04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264524/bf30655c-e47a-3c7d-66fd-086a220d4e4b.png)

### ５．dockerのコンテナの作成

左のバーの"くじら"のアイコン(赤枠)をクリックします。

作成した名前のイメージがツリーで表示される(緑枠)ので右クリックして[Run Interactive]をクリックします。

コンテナが作成されるとツリーに表示されます。(青枠)

![05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264524/3a6d4dbb-e3b5-94e9-181e-a1ee5bf38bbd.png)


### ６．コンテナへの接続(ターミナル)

コンテナを右クリックして[Attach Shell]をクリック。

黄枠の部分にターミナルが表示されます。

![06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264524/8ca5f2cf-e24b-43e4-57de-494edf83bfc8.png)

### ７．コンテナへの接続(Visual Studio Code)

コンテナを右クリックして[Attach Visual Studio Code]をクリック。

別ウィンドウで、コンテナ内のフォルダでVisual Studio Codeが起動します。



[フォルダーを開く]ボタンをクリック。

開くパスを入力する欄が表示されるので、/usr/src/app を入力して[OK]ボタンをクリックする。


![07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264524/5df10ccf-23cf-422c-8ea8-b615f8a02a28.png)
画面左に表示されるフォルダツリーでファイル操作ができます。
エクスプローラーからファイルをドロップしたり、右クリックで[ダウンロード]を実行することでコンテナ内のファイルをホストのＰＣに保存することができます。


# フロントエンド(React)

6でコンテナに接続したターミナルで、以下を実行してReactのプロジェクトを作成します。

```

create-react-app my-app

```

ダウンロードに時間がかかります。

作成されたmy-appフォルダに移動して、起動します。

```

cd my-app
yarn start

```

Chromeなどのブラウザを起動して、http://localhost:3000/ を表示するとデフォルトのページが表示されます。

![08.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264524/6825db2e-1754-a55e-8708-d5faf438e02c.jpeg)



# バックエンド(Python+FastAPI)


7で表示したウィンドウを使いPython+FastAPIでバックエンドのREST APIを作成します。

React側から呼べるように、CORSを考慮して実装しておきます。

/usr/src/app の下にbg-appフォルダを作成し、その下にmain.pyを作成します。

![09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264524/889fd713-eed4-386a-01d5-5c9c686fe000.png)


```python:main.py

from fastapi import FastAPI
from pydantic import BaseModel
from starlette.middleware.cors import CORSMiddleware

# uvicorn main:app --reload --host 0.0.0.0
app = FastAPI()

app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"]
)

class TestParam(BaseModel):
    param1 : str
    param2 : str

# curl http://localhost:8000/
@app.get("/")
def get_root():
    return {"message": "fastapi sample"}

# curl -X POST -H "Content-Type: application/json" -d '{"param1":"test1", "param2":"text2"}' http://localhost:8000/
@app.post("/")
def post_root(testParam : TestParam):
    print(testParam)
    return testParam


```


bg-appを右クリックして、[統合ターミナルで開く]をクリック。

表示されたターミナルで、以下を実行して起動する。

```

uvicorn main:app --reload --host 0.0.0.0

```

ブラウザを起動して、http://localhost:8000/ を表示するとjsonの応答が確認できます。


![09.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264524/2be75757-be1d-4cd4-c635-8c231613d61d.jpeg)


ソースのコメントに記載したcurlコマンドでも動作確認できます。

```

curl http://localhost:8000/

curl -X POST -H "Content-Type: application/json" -d '{"param1":"test1", "param2":"text2"}' http://localhost:8000/

```



# React + FastAPI

Reactのjsで、FastAPIに接続するコードを用意します。

my-app/srcフォルダ内にsample.jsを用意します。

```javascript:sample.js

import React from "react";
import axios from "axios";
 
class Sample extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      message1 : '',
      message2 : ''
      };
  }
 
  handleClick = () => {
    axios
     .post("http://localhost:8000/" , {
              "param1": "hoge",
              "param2": "fuga"})
      .then(res => {
          this.setState({
                message1 : res.data.param1 , 
                message2 : res.data.param2
                });
            })
      .catch(err =>{
        console.log(err);
      } );
  };

  render() {
    return (
      <dev>
        <button onClick={this.handleClick}>POST</button>
        <p>メッセージ１={this.state.message1}</p>
        <p>メッセージ２={this.state.message2}</p>
      </dev>
    );
  }
}
 
export default Sample;

```

ボタンをクリックすると http://localhost:8000/ にPOSTでパラメータを渡して応答を画面に表示するSampleというコンポーネントを実装します。

my-app/src/App.jsにSampleコンポーネントを表示する処理を追加します。


```javascript:App.js

import logo from './logo.svg';
import './App.css';
import Sample from "./sample";  // 追加することで<Sample />が利用できる


function App() {
  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>
          Edit <code>src/App.js</code> and save to reload.
        </p>
        <a
          className="App-link"
          href="https://reactjs.org"
          target="_blank"
          rel="noopener noreferrer"
        >
          Learn React
        </a>

        <Sample />

      </header>
    </div>
  );
}

export default App;


```


ブラウザを起動して、http://localhost:3000/ を表示するとPOSTボタンが追加されていることが確認できます。

![10.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264524/7a8a12ac-2ddc-03a2-b3c0-583b924feb83.jpeg)


POSTボタンをクリックするとFastAPIにPOSTリクエストを送り、応答を画面に表示します。

![11.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264524/b506396e-178b-0de6-654b-0b29acb0d961.jpeg)


# おわりに

dockerの操作はターミナルでのCUI操作が多めになって、初心者向けじゃないと思っていたので、Visual Stusio Codeの拡張機能を利用したGUI操作で説明を記載してみました。

本来はターミナルでdockerコマンドを利用するべきですので、慣れてきたらdockerコマンドを覚えてください。

Reactは、サンプル実行したくらいなのであまり理解が深まっていません。

次回は、[こちら](https://qiita.com/magiclib/items/46aa3e9f4120a6cb04db)で解説したマインスイーパを作ることで理解を深める記事の予定です。

### 追記

マインスイーパを作る記事をまとめました。

[Hello Worldの次はマインスイーパを作ろう(React+FastAPI)](https://qiita.com/magiclib/items/789a76fc98c420161dd7)