環境
- macOS:13.6.7
- Docker:27.3.1
- VScode
開発環境構築
VScodeを開いて、ターミナルを起動
自分の好きなディレクトリにて開発をしましょう
$ cd /Users/{username}/Desktop/
$ mkdir API_test
{username}は自分のPCのユーザー名を入力
例: /Users/{username}/Desktop/API_test
が /Users/alice/Desktop/API_test
になる
今回は軽量のDockerイメージのalpineを使ってみたいと思いますので、イメージをローカル環境に持ってきます
$ docker pull python:3.8-alpine
API構築
そもそもAPIとは?GPTに聞いてみた
異なるソフトウェアアプリケーションが相互にデータや機能をやりとりできるようにするための「インターフェース(接点)」です
連携の際に使えるデータの取り出し口のようなもの
また、APIを実装していく上でいくつかの用語が出てきますのでざっくり理解
-
エンドポイント
APIを利用するためのURLパスで、特定のデータや機能を提供するための接続先です。
例えば、/userエンドポイントはユーザー情報を扱うための接続先になります。 -
メソッド(HTTPメソッド)
APIがどのようなアクションを実行するかを指定するもので、代表的なものにGET(データ取得)、POST(データ送信)、PUT(データ更新)、DELETE(データ削除)があります。 -
パラメータ
エンドポイントへのリクエストに付随して送る情報で、必要なデータや条件を指定します。
ステップ1 コピペ実装
では実際にソースコードを書いてみます。
とりあえずコピペしてみて、動くものを見てみましょう!
ディレクトリ構成
API_test/
├── src
└── app.py
└── user.json
├── Dockerfile
├── requirements.txt
└── docker-compose.yml
app.py
ファイル
# app.py
from flask import Flask, jsonify
app = Flask(__name__)
@app.route('/')
def home():
return jsonify(message="Hello!")
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000)
Dockerfile
ファイル
# Dockerfile
# ベースイメージにAlpineのPythonを指定
FROM python:3.8-alpine
# 作業ディレクトリを設定
WORKDIR /app
# 必要なパッケージをインストール
RUN apk add --no-cache gcc musl-dev curl
# 依存関係をインストール
COPY requirements.txt .
RUN pip install -r requirements.txt
# Flaskアプリのソースコードをコピー
COPY . .
# Flaskアプリの実行
CMD ["python", "./src/app.py"]
docker-compose.yml
ファイル
# docker-compose.yml
version: '3'
services:
web:
container_name: api_test
build: .
ports:
- "5000:5000"
volumes:
- /Users/{username}/Desktop/API_test:/apps
実行
/Users/{username}/Desktop/API_test
配下にて以下を実行
$ docker compose up --build
$ curl localhost:5000 -v
以下のように出力されるはず
* Trying [::1]:5000...
* Connected to localhost (::1) port 5000
> GET / HTTP/1.1
> Host: localhost:5000
> User-Agent: curl/8.4.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Server: Werkzeug/3.0.6 Python/3.8.20
< Date: Fri, 08 Nov 2024 17:18:52 GMT
< Content-Type: application/json
< Content-Length: 49
< Connection: close
<
{"message":"Hello!"}
* Closing connection
app.py
にて指定した通り、{"message":"Hello!"}
とJSONが返ってきます
今回curl
を-v
オプションで詳細まで表示しています
確認してみるとレスポンスが200でGETが返ってきていることがわかります
ステップ2 GETメソッドのパスを追加してみる
パスを新しく追加してみます
/heartbeat
のエンドポイントにリクエストされた場合は「元気です!」とテキストでレスポンスするようにしてみましょう
...
# 新しいエンドポイント /heartbeat を追加
@app.route('/heartbeat')
def heartbeat():
return "元気です!"
...
ソースコードを更新したので、コンテナも更新する必要があります
$ docker compose up --build
$ curl localhost:5000/heartbeat
このようにGETメソッドは追加することができます
ステップ3 クエリを指定してGETメソッドしてみよう
/hello
エンドポイントで、クエリパラメータからusernameを受け取り、「[username]さん、こんにちは!」と返すように修正してみましょう
from flask import Flask, jsonify,request
...
# /helloエンドポイントでusernameを受け取り、挨拶を返す
@app.route('/hello')
def hello():
username = request.args.get('username', 'ゲスト')
return f"{username}さん、こんにちは!"
...
$ docker compose up --build
$ curl localhost:5000/hello?username=raha
どうですか?出力されましたか?
ステップ3 POST&PUTを試す
ユーザーとその年齢を登録するようにしてみましょう
少し難しくなります...
まず、ユーザー情報を管理するjsonファイルを用意しましょう
$ touch ./src/user.json
それではソースコードを変更していきます
# app.py
import json
from flask import Flask, request, jsonify
app = Flask(__name__)
# ユーザー情報を保存するファイルパス
USER_FILE = 'user.json'
# user.jsonファイルを読み込んで辞書として返す
def load_users():
try:
with open(USER_FILE, 'r') as file:
return json.load(file)
except (FileNotFoundError, json.JSONDecodeError):
return {}
# usersをuser.jsonファイルに保存する
def save_users(users):
with open(USER_FILE, 'w') as file:
json.dump(users, file, indent=4)
# POST /user エンドポイント:新しいユーザーを追加
@app.route('/user', methods=['POST'])
def add_user():
users = load_users()
data = request.get_json()
username = data.get('username')
age = data.get('age')
if not username or not age:
return "usernameとageを指定してください", 400
# ユーザーが既に存在するかチェック
if username in users:
return f"{username}は既に存在します", 400
# 新しいユーザーを追加してファイルに保存
users[username] = age
save_users(users)
return jsonify(message=f"{username}が追加されました", user={username: age}), 201
# PUT /user/<username> エンドポイント:ユーザーの年齢を更新
@app.route('/user/<username>', methods=['PUT'])
def update_user(username):
users = load_users()
if username not in users:
return f"{username}は存在しません", 404
data = request.get_json()
age = data.get('age')
if not age:
return "ageを指定してください", 400
# ユーザーの年齢を更新してファイルに保存
users[username] = age
save_users(users)
return jsonify(message=f"{username}の年齢が更新されました", user={username: age}), 200
# GET /user/<username> エンドポイント:ユーザー情報を取得
@app.route('/user/<username>', methods=['GET'])
def get_user(username):
users = load_users()
if username not in users:
return f"{username}は存在しません", 404
# ユーザー情報を返す
return jsonify(username=username, age=users[username])
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000)
ではリクエストしてみましょう
POSTはGETと違うcurlの叩き方をします
$ docker compose up --build
$ curl -X POST -H "Content-Type: application/json" -d '{"username": "raha", "age": 15}' http://localhost:5000/user
では、情報が登録されたかGETリクエストしてみましょう
次に、年齢が嘘なので、更新をしてみます
$ curl -X PUT -H "Content-Type: application/json" -d '{"age": 20}' http://localhost:5000/user/raha
こちらもGETリクエストで確認してみましょう
ステップ4 ユーザー削除してみる
...
# DELETE /user エンドポイント:クエリパラメータで指定されたユーザー情報を削除
@app.route('/user', methods=['DELETE'])
def delete_user():
username = request.args.get('username')
if not username:
return "usernameをクエリパラメータで指定してください", 400
users = load_users()
if username not in users:
return f"{username}は存在しません", 404
# ユーザー情報を削除してファイルに保存
del users[username]
save_users(users)
return jsonify(message=f"{username}が削除されました"), 200
...
削除の際のリクエストは以下です
$ curl -X DELETE "http://localhost:5000/user?username=taro"
では、ユーザー情報を取得してみましょう
適当な存在しないユーザー情報を取得しようとするとどうなるでしょう
おわり
APIサーバーの開発ハンズオンは以上です