svelteKit & FastAPI の連携（Docker上)

今回はローカルのDocker環境でsvelteKitとfastAPIを使用し、
Webアプリをデプロイするまでの手順を投稿します。
この方法以外にもやり方は、いろいろあると思いますが、
検証済みの方法なので参考にして下さい。

前回の投稿でFE(フロントエンド）のsvelteKitをDocker環境で構築しました。

今回はBE（バックエンド）との連携編です。
Docker環境でFastAPIのアプリケーションが動作していることが前提とします。
世の中にFastAPIの入門記事が溢れているので、BEの中身の学習は、他の記事を参考にしてください。
svelteKit側を注目して見ていきます。
※私がお世話になった記事はこちらになります。（迷ったら公式をまず見よう）

https://zenn.dev/sh0nk/books/537bb028709ab9/viewer/b92ab0

# 目標
<ol>
    <li>FastAPI部分の軽く説明</li>
    <li>FEのDockerの設定</li>
    <li>FEからBEへfetchして値を取得する</li>
</ol>

# FastAPI部分の軽く説明
### Dockerfileのポイント
<ul>
    <li>パッケージ管理ツールとしてpoetryを使用しています。</li>
    <li>poetryからuvicornを起動しています。</li>
</ul>

```Dockerfile
#python3.9のイメージをダウンロード
FROM python:3.9-buster
ENV PYTHONUNBUFFERED=1

WORKDIR /src

#pipを使ってpoetryをインストール
RUN pip install poetry

#poetryでライブラリをインストール（pyproject.tomlが既にある場合）
RUN poetry config virtualenvs.in-project true
RUN if [ -f pyproject.toml ]; then poetry install --no-root; fi

#uvicornのサーバーを立ち上げる
ENTRYPOINT ["poetry", "run", "uvicorn", "api.main:app", "--host", "0.0.0.0", "--reload"] 
```

### docker-compose.ymlのポイント
<ul>
    <li>networks</li>
    FEとBEは別々のdocker-compose.ymlによって作成されるため、
    デフォルトでは、別のnetworkとなります。
    そのため、BEでnetwork(be_external)を作成し、FE側で作成したnetworkを使用することとなります。
    
</ul>

```docker-compose.yml
version: '3'
services:
  demo-app:
    build: .
    volumes:
      - .dockervenv:/src/.venv
      - .:/src
    networks:
      - external
    ports:
      - 8000:8000 #ホストマシンのポート8000をdocker内のポート8000に接続する
  db:
    image: mysql:8.0
    platform: linux/x86_64
    environment:
      MYSQL_ALLOW_EMPTY_PASSWORD: 'yes' #rootアカウントをパスワードなしで作成
      MYSQL_DATABASE: 'demo' #初期データベースとしてdemoを設定
      TZ: 'Asia/Tokyo' #タイムゾーンを日本時間に設定
    volumes:
      - mysql_data:/var/lib/mysql
    command: --default-authentication-plugin=mysql_native_password # MYSQL8.0ではデフォルトが"caching_sha2_password"でドライバが非対応のため、変更
    networks:
      - external
    ports:
      - 33306:3306 #ホストマシンのポート33306をdocker内のポート3306に接続する
networks:
  # internal: #DBを外部から接続できなくしたい場合は、networksをinternalに変更する
  #   driver: bridge
  #   internal: false
  external:
    driver: bridge
    internal: false
    name: be_external
volumes:
  mysql_data:
```
↓ポイントとなる設定の詳細説明↓
<details><summary>driver:bridge</summary>
同じ Docker デーモン ホスト上で実行されているコンテナーに適用される。
同じホスト上の他のコンテナーと通信する必要があるコンテナーでアプリケーションを実行する場合によく使用される。
公式↓

https://docs.docker.com/network/drivers/bridge/

</details>
<details><summary>internal: false</summary>
内部ネットワーク上のコンテナーは相互に通信できますが、、他のネットワークとは通信できません。internal: trueを設定することで外部から分離されたnetworkを作成可能です。
ここでは明示的にfalseとしています。
公式↓

https://docs.docker.com/reference/cli/docker/network/create/

</details>
<details><summary>name: be_external</summary>
Linux ブリッジを作成するときに使用するブリッジ名を"be_external"と指定しました。
FEではこの名前を使って、ここで作成したnetworkを使用することとなります。
公式↓

https://docs.docker.com/reference/cli/docker/network/create/

</details>


### FastAPIその他のポイント
今回は、FEからBEへfetchして取得した値を画面に表示するというところまで行います。
BEで注目していただきたいのは、main.pyでoriginsとadd_middlewareの設定をしていることです。
※Getリクエストをするだけなら、この設定は必要ないかも

```main.py
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from api.routers import users,task

import uvicorn

app = FastAPI(swagger_ui_parameters={"docExpansion":"none"})

origins = [
    "http://localhost:8000",
    "http://127.0.0.1:5173",
    "http://my-app:5173"
]

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

app.include_router(users.router, tags=["users"])
app.include_router(task.router, tags=["task"])

@app.get("/")
async def hello():
    return {"message": "hello world!"}

if __name__ == "__main__":
    uvicorn.run(app, host="0.0.0.0", port=8001)
```

# FEのDockerの設定
いよいよ本題のFE側の設定に入ります。
※BE側でわからないことがあれば、他の記事を参考にして下さい。いつか見やすく更新します。
前回の作成したFEのDockerの設定を追加変更していきます。

先ほど、BE側で作成したnetworkを使用するように変更します。
```diff_yaml
version: '3.8'

services:
  my-app:
    image: svelte-app
    build:
      context: .
      dockerfile: ./Dockerfile
    environment:
      - NODE_ENV=development
      - PORT=5173
+    networks:
+      - external
    ports:
      - 5173:5173
    volumes:
      - ./src:/app/src
      - ./static:/app/static
      - ./vite.config.js:/app/vite.config.js
      - ./tsconfig.json:/app/tsconfig.json
      - ./svelte.config.js:/app/svelte.config.js
+networks:
+  external:
+    name: be_external
+    external: true
```
Dockerの設定でいじるこれだけです！
# FEからBEへfetchして値を取得する
それでは、SvelteKitのソースを修正していきましょう。
src直下に+page.server.tsを作成し、fetchして値を取得する処理を追加します。
```+page.server.ts
import { error } from "@sveltejs/kit";

interface Message {
    message: string;
}

export async function load({fetch}){
    const res = await fetch('http://demo-app:8000');
    if(res.ok){
        return await res.json() as Message;
    } else {
        throw error(404)
    }
}
```
Dockerコンテナ同士のやりとりは、URLをコンテナ名にすることで可能です。
load({fetch})として、SvelteKitが持つサーバーサイドのfetchを使用しています。
（今回はあんまり効果を発揮しません）
※interfaceは外だしにしたほうがいいかもね

次にサーバーサイドで取得した値を+page.svelteで画面に表示します。

```svelte
<script lang="ts">
    export let data;
    let message = data.message;
</script>
<h1>Welcome to SvelteKit</h1>
<p>Visit <a href="https://kit.svelte.dev">kit.svelte.dev</a> to read the documentation</p>
<p>message:{message}</p>
```
export let data;でサーバーサイドの値を受け取っています。
 ### 成功イメージ
 ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3484672/068e38f7-2c6e-5504-6482-083a1613892a.png)
BEから取得した、"hello world!"をFEで画面に表示することができました。

# 終わりに
今回も非常に簡単いですが、SvelteKitとFastAPIをDocker上でどうするようにしました。
詳細の解説は気が向いたら順次していきたいと思います。

よかったら、いいねと保存して下さい。

次回は、BEから取得したデータ一覧をSvelteでSkeletonのデータテーブルを使用して、画面に表示しようと思います。