なんの記事?
以下の記事の中編にあたります。
手順
残りの手順として
- フロントエンド
- 結合
- デプロイ
が存在します。が、その前に docker-compose
でバックエンドとフロントエンドの環境を整えます。
構成
今回は以前にbackendのリポジトリを作成してしまったので、submoduleでバックエンドを引っ張ってきます。
.
├── README.md
├── docker-compose.prod.yml
├── docker-compose.yml
├── backend
│ ├── Dockerfile
│ ├── README.md
│ ├── app
│ ├── backend
│ ├── pytest.ini
│ ├── requirements.dev.txt
│ ├── requirements.txt
│ └── tests
└── frontend
├── Docekrfile.prod
├── Dockerfile.dev
├── README.md
├── node_modules
├── package.json
├── public
├── src
└── yarn.lock
リポジトリは以下になります。
開発環境用のdocker-compose.yml
最終的に以下のようになりました。
services:
backend:
tty: true
build:
context: ./backend
dockerfile: Dockerfile
volumes:
- ./backend/:/backend/
command: uvicorn app.main:app --reload --host 0.0.0.0 --port 8000 --log-level trace
ports:
- 8000:8000
frontend:
build:
context: ./frontend
dockerfile: Dockerfile.dev
command: yarn start
volumes:
- ./frontend:/usr/src/app
ports:
- "3000:3000"
docker-compose up --build
を行いlocalhost:8000
, localhost:3000
を見に行くことで、それぞれ起動していることが確認できます。
また、volumes
でマウントを行っているので、docker-compose up --build -d
で立ち上げたあと、開発しながら localhost:3000
を見に行くことで、開発を進めることが可能です。
開発を終えるときは docker-compose down
をしておきましょう。
本番環境用のdocker-compose.prod.yml
本番環境用ではnginxを用いてフロントが表示されるように変更します。
services:
backend:
tty: true
build:
context: ./backend
dockerfile: Dockerfile
volumes:
- ./backend/:/backend/
command: uvicorn app.main:app --reload --host 0.0.0.0 --port 8000 --log-level trace
ports:
- 8000:8000
frontend:
build:
context: ./frontend
dockerfile: Dockerfile.prod
volumes:
- ./frontend:/usr/src/app
ports:
- "80:80"
本番用のフロントは以下のようになります。
FROM node:latest AS builder
WORKDIR /usr/src/app
COPY . .
RUN npm install
RUN npm run build
FROM nginx:latest
RUN rm -r -f /usr/share/nginx/html
COPY --from=builder /usr/src/app/build /usr/share/nginx/html
nginx経由の表示については、こちらを参考にしました。
docker-compose -f docker-compose.prod.yml up -d --build
を行うことで、 localhost
でのフロント表示が確認できます。
フロントエンド実装
フロントエンド実装に移ります。前回実装したAPIから、結果を取得する関数fetchPapers
、またpaper
をレンダリングするPaper
コンポーネントと、Paper
一覧を表示するPapers
を実装します。
import axios from 'axios';
import DEFAULT_API_LOCALHOST from '../urls';
export const fetchPapers =() => {
return axios.get(DEFAULT_API_LOCALHOST)
.then(res => {
return res.data
})
.catch((e) => console.error(e))
}
DEFAULT_API_LOCALHOST
は前回実装したAPIのエンドポイント、すなわちhttp://localhost:8000/api/papers/
になります。
const DEFAULT_API_LOCALHOST = 'http://localhost:8000/api/papers/'
export default DEFAULT_API_LOCALHOST;
仮置で、fetchPapers
関数を用いて、APIの結果が帰ってくるかどうかをconsole上で見てみます。
import React, { Fragment, useEffect } from 'react';
// apis
import Paper from '../components/Paper';
import { fetchPapers } from '../apis/fetchPapers';
export const Papers = () => {
useEffect(() => {
fetchPapers()
.then((data) =>
console.log(data)
)
}, [])
return (
<Fragment>
Paper一覧
</Fragment>
)
}
export default Papers;
トップページでPapers
をレンダし、ブラウザ上でログを見てみます。
NetWorkErrorで怒られてしまっています。networkのログを見てみると
バックエンド側のCORS設定に問題があるようです。
を参考に、バックエンドを修正します。
import uvicorn
from fastapi import FastAPI
from starlette.middleware.cors import CORSMiddleware
from backend.arxiv_api import ArxivApiClass
from backend.extractor import Extractor
app = FastAPI()
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"]
)
arxiv_api = ArxivApiClass()
extractor = Extractor()
# ...
submodule側で更新を行い、mainブランチにマージしたので、その反映を行います。
(base) ➜ arxiv-checker git:(feature/createPaperComponent) ✗ git submodule foreach git pull origin main
Entering 'backend'
remote: Enumerating objects: 12, done.
remote: Counting objects: 100% (12/12), done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 8 (delta 4), reused 7 (delta 4), pack-reused 0
Unpacking objects: 100% (8/8), done.
From github.com:izuna385/arxiv-checker-backend
* branch main -> FETCH_HEAD
c37561e..6f5d886 main -> origin/main
Updating c37561e..6f5d886
Fast-forward
README.md | 4 ++--
app/main.py | 10 ++++++++++
2 files changed, 12 insertions(+), 2 deletions(-)
(base) ➜ arxiv-checker git:(feature/createPaperComponent) ✗
docker-compost up --build
で再度開発環境をビルドした後、localhost:3000
番を見に行きます。
バックエンドから論文一覧を取得できていることが分かります。
useState, useEffectを用いたレンダリング
バックエンドからの結果を実際にレンダリングする実装を次に行います。
import React, { Fragment, useEffect, useState } from 'react';
// apis
import Paper from '../components/Paper';
import { fetchPapers } from '../apis/fetchPapers';
export const Papers = () => {
const[allPapersData, setAllPapersData] = useState([]);
useEffect(() => {
fetchPapers()
.then(data => setAllPapersData(data.papers))
}, [])
return (
<Fragment>
{console.log(allPapersData)}
<ul>
{allPapersData.map((data) => (
<Paper paperData={data}/>
))}
</ul>
</Fragment>
)
}
export default Papers;
allPapersData
を空の初期値で定義し、そこにバックエンドからの結果を格納しています。
ブラウザ上では、バックエンドの返却結果のリストの各要素が、Paper
コンポーネントとして表示されます。
ここまでで、バックエンドとフロントエンドの結合までを完了しました。
動作を確認する場合は、リポジトリからcloneし、docker-compose up --build
で確認可能です。
また、UIの調整についてはここでは省略しますが、今回はreact-bootstrapを用いました。
次回
リポジトリ