FastAPIとReact, Herokuを使用した小規模アプリのデプロイとホスティング

最小限の機能を持つバックエンドとフロントエンドのデプロイをしてみました。

# フロントエンド
## 初期化
```bash
npm create vite@latest <your-frontend-folder-name> --template react-ts
cd <your-frontend-folder-name>
npm install
```

### キーとなるファイル
- index.html: エントリーポイント
- src/main.jsx: Reactのエントリーファイル
- src/App.jsx: Reactの根幹となるコンポーネント

## src/App.tsxの改修
以下のコードで、フロントエンドのフォルダにあるsrc/App.tsxを更新する。

```tsx
import React from 'react';

function App() {
  return (
    <div>
      <h1>Hello from React + Vite!</h1>
    </div>
  );
}

export default App;
```

## フロントエンドサーバーの動作確認
以下のコードを起動してブラウザでサーバーが立ち上がるか確認する。

```bash
npm run dev
```

アプリを指定されたURL(e.g., http://localhost:5173)で閲覧する。

# バックエンド
## セットアップ
1. FastAPIとUvicornのインストール
- バックエンドのために新しいフォルダを作る (e.g., backend)
- 依存しているライブラリをインストールする

```bash
pip install fastapi uvicorn
```

2. バックエンドのフォルダを作成

```bash
cd ..
mkdir <your-backend-folder-name>
```

3. バックエンドのフォルダにmain.pyを作成

```python
from fastapi import FastAPI

app = FastAPI()

@app.get("/")
def read_root():
    return {"message": "Hello from FastAPI!"}
```

3. バックエンドサーバー動作確認

```bash
uvicorn main:app --reload --host 0.0.0.0 --port 8000
```

http://127.0.0.1:8000 にアクセスして確かめる。

# フロントエンドとバックエンドの繋ぎ込み
1. CORS (Cross Origin Resource Sharing)の適用
バックエンドのフォルダにあるmain.pyを以下のように更新する。

```python
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware

app = FastAPI()

app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],  # Adjust this for production
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

@app.get("/")
def read_root():
    return {"message": "Hello from FastAPI!"}
```

2. React側でのデータ取得
フロントエンド側のフォルダにある`src/App.tsx`を以下のように編集する。

```tsx
import { useEffect, useState } from 'react';

function App() {
  const [message, setMessage] = useState("");

  useEffect(() => {
    fetch("http://127.0.0.1:8000/")
      .then((response) => response.json())
      .then((data) => setMessage(data.message));
  }, []);

  return (
    <div>
      <h1>{message}</h1>
    </div>
  );
}

export default App;
```

# Herokuにデプロイ
## フロントエンド

1. フロントエンドのフォルダに移動し、新しいHerokuアプリを作成し、Node.jsとNGINXビルドパックを追加する

```bash
heroku create <your-frontend-app-name>
heroku buildpacks:add heroku/nodejs -a <your-frontend-app-name>
heroku buildpacks:add heroku-community/nginx -a <your-frontend-app-name>
```

2. フロントエンドのフォルダのルートに`config`フォルダを追加して、`nginx.conf.erb`というファイルを追加する

```bash
mkdir config
touch config/nginx.conf.erb
```

3. `nginx.conf.erb`に以下の内容を追加する

```text
worker_processes 1;
events { worker_connections 1024; }
http {
    include mime.types;
    default_type application/octet-stream;
    sendfile on;
    keepalive_timeout 65;

    server {
        listen <%= ENV["PORT"] %>;
        root /app/dist;

        location / {
            try_files $uri /index.html;
        }
    }
}
```

4. `package.json`を以下のように更新

```json
{
  "name": "my-react-app",
  "version": "1.0.0",
  "private": true,
  "description": "A simple React.js application using Vite",
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "serve": "vite preview",
    "start": "serve -s dist"
  },
  "dependencies": {
    "vite": "^4.4.5",
    "@vitejs/plugin-react": "^4.0.0",
    "react": "^18.2.0",
    "react-dom": "^18.2.0"
  },
  "devDependencies": {
    "vite": "^4.0.0",
    "@vitejs/plugin-react": "^4.0.0"
  },
  "engines": {
    "node": ">=14.x",
    "npm": ">=6.x"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}
```

5. viteをインストールする

viteがpackage.jsonのdevDependenciesではなくdependenciesに入ることがポイントで、これにより本番環境でもviteが使えるようになる。

```bash
npm install vite @vitejs/plugin-react --save
```

6. `serve`パッケージをインストールする
Herokuはビルドしたファイルを`dist`フォルダからサーバーに使う必要があるため、`serve`パッケージをインストールする。

```bash
npm install --save serve
```

7. フロントエンドのフォルダのルートにある`vite.config.js`を以下のように編集する

```javascript
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';

export default defineConfig({
  plugins: [react()],
  build: {
    outDir: 'dist' // Output folder for production builds
  }
});
```

8. フロントエンドの`public/index.html`を以下のように更新する

```xml
<!doctype html>
<html lang="en">
  <body>
    <div id="root"></div>
    <script type="module" src="/src/main.tsx"></script>
  </body>
</html>
```

9. ルートに`Procfile`を置いて中身を以下のように編集する

```text
web: npm start
```

10. フロントエンドのフォルダのルートに`static.json`を追加して、以下のように編集する

```json
{
    "root": "dist/"
}
```

11. フロントエンド側のフォルダでアプリをビルドする。

```bash
npm run build
```

12. Herokuにデプロイ

```bash
git init
heroku git:remote -a <your-frontend-app-name>
git add .
git commit -m "Deploy frontend"
git push heroku main
```

## バックエンド
1. `Procfile`をバックエンドのフォルダのルートに追加する

```text
web: uvicorn main:app --host=0.0.0.0 --port=${PORT}
```

2. 依存先ライブラリ一覧(`requiremens.txt`)を作成

```bash
pip freeze > requirements.txt
```

私の環境では`requirements.txt`は以下でした。

```text
fastapi==0.115.12
uvicorn==0.34.0
```

3. Herokuにデプロイ

```bash
heroku create <your-backend-app-name>
git init
git add .
git commit -m "Deploy backend"
git push heroku main
```

# デプロイ後にすること

フロントエンドのsrc/App.tsxにはバックエンドのURLが載っているが、それはローカルホスト用。Herokuにデプロイした後は、実際にバックエンドのアプリを開いて実際のURLを確認し、App.tsxを修正する必要がある。変更後はフロントエンドのアプリを再度デプロイすること。