WebAPIを作ると、基本部分は大体同じような実装をすることが多いのでその雛型となるような簡単なアプリを作ってみようと思います。
内容
凶から大吉までをランダムに返すだけです。めっちゃシンプルです。
ソースコードはここに置いてあります。
https://github.com/hiro949/omikujiApp/tree/0.1.0
バックエンド
Flaskで書いています。
プロダクトコード
from flask import Flask, jsonify
from flask_cors import CORS
import random
app = Flask(__name__)
CORS(app) # allow cross-origin requests
fortunes = [
"大吉!今日は最高の一日になるわよ!",
"中吉…まぁまぁね。油断しないでよ!",
"小吉…ちょっとだけいいことあるかも?",
"凶…アンタ、今日は大人しくしてなさい!"
]
@app.route('/api/fortune')
def get_fortune():
result = random.choice(fortunes)
return jsonify({'fortune': result})
if __name__ == '__main__':
app.run(host="0.0.0.0", port=5000, debug=True)
テストコード
内容はランダムなので、どれかが返ってくればPASSとしています。
ルートディレクトリから
python -m pytest
で実行できます。
import pytest
from backend.app import app
def test_get_fortune():
# Flaskのテストクライアントを使う
client = app.test_client()
response = client.get('/api/fortune')
# ステータスコードが200であること
assert response.status_code == 200
# JSONレスポンスに 'fortune' キーがあること
data = response.get_json()
assert 'fortune' in data
# fortuneの内容が期待されるリストの中にあること
expected_fortunes = [
"大吉!今日は最高の一日になるわよ!",
"中吉…まぁまぁね。油断しないでよ!",
"小吉…ちょっとだけいいことあるかも?",
"凶…アンタ、今日は大人しくしてなさい!"
]
assert data['fortune'] in expected_fortunes
フロントエンド
プロダクトコード
npmwをinstallして、
npm create vite@latest frontend
cd frontend
npm install
で画面表示に必要なファイルは自動で作ってくれます。
あとはその中のApp.jsxとApp.cssを以下のように修正すればOKです。
import React, { useState } from 'react';
import './App.css';
function App() {
const [fortune, setFortune] = useState('');
const fetchFortune = async () => {
const response = await fetch('/api/fortune');
const data = await response.json();
setFortune(data.fortune);
};
return (
<div className="container">
<h1>おみくじ React版</h1>
<button onClick={fetchFortune} className="button">
占ってみる?…別にアンタのためじゃないけど!
</button>
<p className="result">{fortune}</p>
</div>
);
}
export default App;
.container {
text-align: center;
padding: 50px;
background-color: #fff0f5;
font-family: Arial, sans-serif;
}
.button {
background-color: #ff69b4;
color: white;
border: none;
padding: 10px 20px;
font-size: 16px;
border-radius: 8px;
cursor: pointer;
}
.result {
margin-top: 20px;
font-size: 20px;
color: #800080;
}
テストコード
frontend/ディレクトリから
npm test
で実行できます。
作成の際は
npm install --save-dev jest @testing-library/react @testing-library/jest-dom babel-jest jest-environment-jsdom @babel/preset-env @babel/preset-react
npm audit fix
である程度必要なコードは自動で揃えてくれます。
自分で書く必要があるのは
frontend/setupTest.jsvite.configs.jsbabel.configs.jsfrontend/__test___/App.test.jsx-
frontend/package.json(自動生成されたファイルを修正)
です。以下のように書きます。
import '@testing-library/jest-dom';
export default {
testEnvironment: 'jsdom',
setupFilesAfterEnv: ['<rootDir>/setupTests.js'],
transform: {
'^.+\\.jsx?$': 'babel-jest',
},
extensionsToTreatAsEsm: ['.jsx'],
moduleFileExtensions: ['js', 'jsx', 'json', 'node'],
moduleNameMapper: {
'\\.css$': '<rootDir>/__mocks__/styleMock.js',
},
};
{
"name": "frontend",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"lint": "eslint .",
"preview": "vite preview",
"test": "jest"
},
:
:
追加箇所:"test":"jest"
export default {
presets: ['@babel/preset-env', '@babel/preset-react'],
};
jestでApp.cssは読み込めないので代わりのmockを作ります。
module.exports = {};
import React from 'react';
import { render, screen, fireEvent } from '@testing-library/react';
import App from '../src/App.jsx';
test('renders title and button', () => {
render(<App />);
expect(screen.getByText(/おみくじ React版/i)).toBeInTheDocument();
expect(screen.getByText(/占ってみる?/i)).toBeInTheDocument();
});
test('fetchFortune updates fortune text', async () => {
global.fetch = jest.fn(() =>
Promise.resolve({
json: () => Promise.resolve({ fortune: '大吉!今日は最高の一日になるわよ!' }),
})
);
render(<App />);
fireEvent.click(screen.getByText(/占ってみる?/i));
const result = await screen.findByText(/大吉!/i);
expect(result).toBeInTheDocument();
});
(バックエンドはmockしています)
docker composeでデプロイ
frontendとbackendの通し尿のproxyを設定します。
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
// https://vite.dev/config/
export default defineConfig({
plugins: [react()],
server: {
host: true,
proxy: {
'/api': {
target: 'http://backend:5000',
changeOrigin: true,
rewrite: path => path.replace(/^\/api/, '/api')
}
}
}
})
次にfrontend,backendのDockerfileをそれぞれ作ります。
FROM python:3.10-slim
WORKDIR /app
COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["python", "app.py"]
FROM node:22-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 5173
CMD ["npm", "run", "dev"]
を用意してルートの
services:
backend:
build: ./backend
ports:
- "5000:5000"
volumes:
- ./backend:/app
environment:
- FLASK_ENV=development
frontend:
build: ./frontend
ports:
- "5173:5173"
volumes:
- ./frontend:/app
depends_on:
- backend
に対して
docker-compose up -d --build
でコンテナを起動します。
で以下のようなページにアクセスできるはずです。

