想法记于初学者使用FastAPI + React + Docker进行开发时

首先

我是一个从开始拜读起经过四个月的人,对FastAPI、React和Docker都进行了调查和研究。这一次,我想总结一下我在使用FastAPI + React + Docker进行开发时考虑的一些事情。

为什么选择FastAPI?

FastAPI是一個專注於非同步處理的Python API建構網絡框架。由於Python提供了一些專注於機器學習的庫,例如sklearn和Pytorch,因此如果希望在後端中整合機器學習,我認為FastAPI是一個有效的選擇(儘管本次並不盛裝)。

为什么要使用Docker?

如果后端和前端的服务器被分离,那么使用Docker是很有效的。因为它可以同时启动不同的容器并进行中继,非常方便。如果没有中继,部署时就需要两个服务器,所以在这一点上也很重要。虽然也可以使用nginx等反向代理来实现,但这次我们将使用Docker。

1. 目录结构

我觉得最好基本上统一而不是每个项目都进行更改。举一个例子,我创建了一个类似模板的东西,每次都可以重复使用,并且只使用所需的部分。

# ---Docker関連--- #
├── docker
│   ├── backend
│   │   ├── Dockerfile
│   │   └── requirements.txt
│   └── frontend
│       └── Dockerfile
├── docker-compose.yml
│
│
# ---React関連--- #
├── package-lock.json
├── package.json
├── public
│   └── index.html
├── src
│   ├── components # 画面ごとに必要なコンポーネントを入れる
│   │   └── component-1
│   │   │   ├── child-component # 子コンポーネントを同じディレクトリに
│   │   │   │   ├── child-component.module.scss
│   │   │   │   └── index.jsx
│   │   │   ├── component-1.module.scss
│   │   │   └── index.jsx
│   ├── pages
│   │   └── page.jsx # 画面遷移ごとのコンポーネントを入れる
│   ├── index.js     # ルーティングとレンダリングを定義する
│   └── response.js  # axiosでAPIとのやりとりをする
│
│
# ---FastAPI関連--- #
├── run.py # fastapiメイン
├── apis
│   └── page.py # apiのパスごとにソースを分ける
└── scripts
    ├── crud # データベースへのクエリー関数
    │   └── user.py
    ├── models # テーブルスキーマを定義するモデル
    │   └── User.py
    └── other.py # その他の機能

这不一定是正确答案,但从个人而言,我觉得这是一个比较方便的环境。虽然还有一些缺失的部分,但你可以在GitHub上保存这个模板,如果方便的话可以试着使用一下。(还包含了适用于Node.js的express。)

 

关于FastAPI的相关内容

run.py的内容如下。

ポイントとなるのは、CORSを使うことです。
Reactがport:3000、FastAPIがport:3001を使うとすると、FastAPIはport:3000からのアクセスを許可する必要があります。ちなみにですがリバースプロキシを使うと許可を出さずともアクセスできるようになります。

こちらはプログラムベースでサーバーを起動しています。コマンドラインでuvicornを実行しても構いませんが、テストの度にコマンドを入れるのは個人的に面倒だと感じています。

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
import uvicorn

app = FastAPI()
app.add_middleware(
    CORSMiddleware,
    allow_origins=["http://localhost:3000"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

@app.get("/")
async def index():
    return {"message": 'Hello World!'}


if __name__=="__main__":
    uvicorn.run("run:app",port=3001, reload=True)

3. 关于React相关的内容

package.json的开头如下所示。

port:3001で起動するように、proxyを設定しています。
インストールしているパッケージですが、私がよく使うものを入れてます

emotion:jsx内でスタイリングができるライブラリです。

mui:Material UI、コンポーネントベースでスタイリングされたテンプレートを使用することができます。

axios:APIとのデータのやりとりを行うためのライブラリです。

node-sass:静的sassファイルからスタイリングを実装する場合に使います。

react-router-dom:Reactでルーティングの定義をするために使います。(単体では定義ができません。)

我将为您解释关于这些事项中的axios通信和react-router-dom的部分。

{
  "name": "react_test",
  "version": "0.1.0",
  "private": true,
  "proxy": "http://localhost:3001",
  "dependencies": {
    "@emotion/react": "^11.11.1",
    "@emotion/styled": "^11.11.0",
    "@mui/icons-material": "^5.14.15",
    "@mui/joy": "^5.0.0-beta.12",
    "@mui/material": "^5.14.15",
    "@mui/x-charts": "^6.0.0-alpha.17",
    "@testing-library/jest-dom": "^5.17.0",
    "@testing-library/react": "^13.4.0",
    "@testing-library/user-event": "^13.5.0",
    "axios": "^1.5.1",
    "node-sass": "^7.0.3",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "react-router-dom": "^6.15.0",
    "react-scripts": "5.0.1",
    "web-vitals": "^2.1.4"
  },

使用axios与API进行交互

在src/response.js文件中,具体如下。

    • ローカルで作業をする場合とdockerで動かす場合とでドメインを分けています。

 

    • 関数の引数にエンドポイントを入れるだけでデータを取得できるように関数を用意しています。例えば、getResponse(“/hello”)といれると、Fastapiで@app.get(“/hello”)と定義されているAPIからデータを受け取ることができます。

postResponse()は、第二引数にdataをとるようになっていますが、データをFormData()インスタンスを入れることで送信することができます。

import axios from "axios";

const Domain = "http://127.0.0.1:3001";

// docker用
//const Domain = "http://0.0.0.0:3001";

// get methodでのレスポンスを返す関数
const getResponse = async (endpoint) => {
    try {
        const res = await axios.get(Domain + endpoint);
        return res.data;
    } catch (e) {
        throw e;
    }
}
// post methodでデータを送信し,レスポンスを返す関数
const postResponse = async (endpoint, data) => {
    try {
        const res = await axios.post(Domain + endpoint, data);
        return res.data;
    } catch (e) {
        throw e;
    }
}

使用React定义路由和渲染

在src/index.js文件中,如下所示。

    • Reactではルーティングの定義はサポートされていないので、react-router-domを使って定義しています。

src/pagesにルートごとのコンポーネントを定義し、に組み込みます。
ルートはを使って定義します。以下の例だと、/にアクセスした時にPageコンポーネントが表示されるということです。

import React from 'react';
import ReactDOM from 'react-dom/client';
import { BrowserRouter, Routes, Route } from 'react-router-dom';

import Page from './pages/page';

function App() {
    return (
        <Routes>
            <Route exact path="/" element={ <Page /> }/>
        </Routes>
    );
}

const root = ReactDOM.createRoot(document.getElementById('root'));
    root.render(
        <BrowserRouter>
            <App />
        </BrowserRouter>
);

4. 关于Docker相关内容

前端

docker/frontend/Dockerfile的内容如下:

nodejsベースのイメージを使っています。
ソースファイルをそのままの形でdockerへコピーしています。

FROM node:latest
WORKDIR /frontend

COPY package.json .
RUN npm install

COPY /src /frontend/src/
COPY /public /frontend/public/

CMD ["npm", "start"]

后端

docker/backend/Dockerfile和docker/backend/requirements.txt文件的内容如下所示。

    • pythonバージョンはローカル環境と同じものを使うことを推奨します。

requirements.txtを用意し、pip installできるようにしておきます。
インストール前にpipバージョンのアップグレードをしておくことを推奨します。
最新のパッケージバージョンを取得したい場合は、–no-cache-dirがあると確実です。

FROM python:3.10-alpine
WORKDIR /backend

COPY /docker/backend/requirements.txt .
RUN pip install --no-cache-dir --upgrade -r /backend/requirements.txt

COPY run.py /backend/
COPY /scripts /backend/scripts

CMD ["uvicorn", "run:app", "--reload", "--host", "0.0.0.0", "--port", "3001"]
fastapi
uvicorn
python-multipart

docker-compose.yml 的一种翻译选项:

    • 2つのコンテナを稼働します。イメージ名は-backendと-frontendとします。

ports:を設定して、それぞれのポート番号を設定しておきます。

version: '3'

services:
  backend:
    build:
      context: .
      dockerfile: docker/backend/Dockerfile
    ports:
      - 3001:3001
  
  frontend:
    build:
      context: .
      dockerfile: docker/frontend/Dockerfile
    ports:
      - 3000:3000

他的想法 (tā de

    • nginxでリバースプロキシをやりたい!

 

    • tsベースで使ったほうが良い気がした。

 

    細かいスタイリングはemotionよりTailwind CSSがいいかなと思う。

最後的

这次我只是随意写了些个人的观念。
毕竟这只是一个初学者的想法,希望有经验的人能够参考一下。如果有专业人士有改善意见的话,我会期待您的评论。

广告
将在 10 秒后关闭
bannerAds