簡単なWebアプリケーションを作ってみよう

では、実際に簡単なWebアプリケーションを作ってみましょう。 ここでは、PythonのFlaskというフレームワークを使って、簡単なWebアプリケーションを作成します。

例えば、ユーザー情報が納められたJSONファイルを用意して、その中からユーザーIDに対応するユーザー名を返すAPIを作成してみます。 ディレクトリ(フォルダー)api-exampleを作成し、この中で作業してみましょう。

$ mkdir api-example
$ cd api-example
$ code --reuse-window . # VSCodeを起動し直す

シンプルなAPIサーバーとテスト

まずはユーザー情報のJSONファイルを用意してみましょう。 手打ちでは少し辛いと思いますので、コピー&ペーストで作成すると良いでしょう。

ファイル名は users.json とします。

users.json
[
    {"id": 1, "name": "Alice"},
    {"id": 2, "name": "Bob"},
    {"id": 3, "name": "Charlie"}
]

つづいて、このJSONファイルを読み込んで情報を返すWebアプリケーションとなります。 ここではFlaskを使ったコードとしてみます。 app.py というファイル名で作成してください。

app.py
from flask import Flask, jsonify, Response
from typing import Dict, List, Any, Union
import json

app: Flask = Flask(__name__)

with open(app.root_path + '/users.json', 'r', encoding='utf-8') as f:
    users: List[Dict[str, Any]] = json.load(f)

@app.route('/users/<int:user_id>', methods=['GET'])
def get_user(user_id: int) -> Union[Response, tuple[Response, int]]:
    user: Union[Dict[str, Any], None] = next((u for u in users if u['id'] == user_id), None)
    if user:
        return jsonify(user)
    else:
        return jsonify({"error": "User not found"}), 404

if __name__ == '__main__':
    # 本番環境では debug=False を使用してください
    app.run(debug=True)

それでは、実際に動かすためにuvで環境を構築してみましょう。 コードに含まれているように、このアプリケーションではFlaskというライブラリを使っているので、uv addを使ってインストールしてからでないと動きません。

$ uv init
$ uv add flask # 依存関係としてflaskを追加
$ uv run app.py

これで別のターミナルをVSCodeで開くと、 http://localhost:5000/ にてアクセスできるようになっています。

$ curl localhost:5000/users/1
{
  "id": 1,
  "name": "Alice"
}
$ curl localhost:5000/users/42
{
  "error": "User not found"
}

普通のWebページとして見てみる

そのままブラウザで http://localhost:5000/users/1 にアクセスしてみると、JSONのまま表示されることがわかります。 その一方で、ブラウザで http://localhost:5000/ にアクセスしても、404 Not found の表示となってしまいます。そこで、トップページを用意して、ちょっとしたフォームにより指定ユーザーIDの情報が出せるようなページを作ってみます。

index.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>ユーザー情報取得</title>
</head>
<body>
    <h1>ユーザー情報取得</h1>
    <form id="userForm">
        <label for="user_id">ユーザーID:</label>
        <input type="number" id="user_id" name="user_id" required>
        <button type="submit">取得</button>
    </form>

    <ul id="result" aria-live="polite"></ul>

    <script>
    const form = document.getElementById('userForm');
    const result = document.getElementById('result');

    function showUser(user) {
        result.innerHTML = '';
        const idLi = document.createElement('li');
        idLi.textContent = `ID: ${user.id}`;
        const nameLi = document.createElement('li');
        nameLi.textContent = `名前: ${user.name}`;
        result.appendChild(idLi);
        result.appendChild(nameLi);
    }

    function showError(msg) {
        result.innerHTML = '';
        const li = document.createElement('li');
        li.textContent = msg;
        result.appendChild(li);
    }

    form.addEventListener('submit', async (e) => {
        e.preventDefault();
        const id = document.getElementById('user_id').value;
        if (!id) return;
        try {
            const res = await fetch(`/users/${encodeURIComponent(id)}`);
            const data = await res.json();
            if (res.ok) {
                showUser(data);
                history.replaceState(null, '', `?user_id=${id}`);
            } else {
                showError(data.error || 'ユーザーが見つかりません');
            }
        } catch (err) {
            console.error(err);
            showError('通信エラーが発生しました');
        }
    });

    // ページ読み込み時にクエリに user_id があれば自動取得
    (function initFromQuery() {
        const params = new URLSearchParams(location.search);
        const q = params.get('user_id');
        if (q) {
            document.getElementById('user_id').value = q;
            form.dispatchEvent(new Event('submit', { bubbles: true, cancelable: true }));
        }
    })();
    </script>
</body>
</html>

そして、app.pyに手を入れて、トップページが出るようにしてみましょう。

app.py
--- /home/runner/work/2025-network-doc/2025-network-doc/source/app/http/src/api/app-simple.py
+++ /home/runner/work/2025-network-doc/2025-network-doc/source/app/http/src/api/app.py
@@ -1,4 +1,4 @@
-from flask import Flask, jsonify, Response
+from flask import Flask, jsonify, send_from_directory, Response
 from typing import Dict, List, Any, Union
 import json
 
@@ -15,6 +15,10 @@
     else:
         return jsonify({"error": "User not found"}), 404
 
+@app.route('/', methods=['GET'])
+def index() -> Response:
+    return send_from_directory(app.root_path, 'index.html')
+
 if __name__ == '__main__':
     # 本番環境では debug=False を使用してください
     app.run(debug=True)

一度uv runしている所を止めて、再度起動してみましょう。 今度は http://localhost:5000/ にアクセスすると、トップページが表示されることがわかります。 ユーザーID(1,2,3)や存在しない番号(42)を入れて送信すると、結果が出力されるのがわかると思います。