APIサーバーの内部動作：リクエストからレスポンスまでの処理の流れ

## はじめに
APIについて調査したところ、クライアントサイドの解説は充実している一方、サーバーサイドの処理に関する記述は少ないと感じました。APIの設計方法やHTTPメソッド、JSONの構造については情報が豊富ですが、サーバー内部の処理について深く理解するには時間がかかりました。
以前、VSCodeでz/OSを操作する環境を構築しました。VSCode（クライアント）からAPIを発行し、z/OSMF（z/OSの機能をREST APIとして公開するミドルウェア）（APIサーバーのようなものだと思っていただいて良いです。）を経由してz/OSを操作するのですが、z/OSMFがAPIリクエストをどのように処理しているのか、内部でどのような定義が必要なのかを理解するのに時間を要しました。
そこで、z/OSMFのようなサーバーがリクエストをどのように処理するのか、サーバー内部に必要な定義について調査し、本記事にまとめました。z/OSMF固有の解説ではなく、一般的なサーバーサイドの処理フローを中心に解説しています。
本記事では、以下の内容について説明します。
- クライアントからのAPIリクエストに対するサーバーの処理フロー
- サーバー内部でのリクエスト処理：ルーティング、バリデーション、ビジネスロジックなど
- レスポンスの生成と返却
- リクエスト処理のシーケンス図

本記事が、APIサーバーの処理フローを理解する一助となれば幸いです。

## APIサーバーとは
APIとは「Application Programming Interface」の略で、ソフトウェアシステムが外部のプログラムと連携するための窓口です。APIを通じて、他システムの機能やデータを利用できます。例えば、天気予報アプリは気象情報提供サービスのAPIを利用して最新の天気情報を取得し、地図アプリは地図データ提供サービスのAPIを利用して地図を表示します。
APIサーバーとは、このAPIを提供するサーバーのことです。クライアントからのリクエストを受け取り、指定された処理を実行し、その結果をレスポンスとして返却します。
WebサーバーとAPIサーバーでは役割と返却するデータが異なります。Webサーバーは主にHTML、CSS、JavaScriptなどのWebページを構成するファイルをクライアントに返却し、ユーザーがブラウザで閲覧できるようにします。一方、APIサーバーは、クライアントがプログラムで処理しやすい形式のデータ（JSONやXMLなど）を返却します。APIサーバーはユーザーインターフェースを持たず、他のプログラムとの連携に特化しています。

例えば、天気予報アプリが最新の天気情報を表示する流れを考えてみます。
1. ユーザーが天気予報アプリを開く
2. アプリは、Webサーバーにリクエストを送信し、アプリの画面を表示するためのHTML、CSS、JavaScriptファイルなどを受け取る
3. アプリは、気象情報提供サービスのAPIサーバーにリクエストを送信。このリクエストには、位置情報などが含まれる
4. APIサーバーは、リクエストされた位置情報に対応する天気情報をデータベースから取得
5. APIサーバーは、取得した天気情報をJSON形式に変換し、レスポンスとしてアプリに返却
6. アプリは、受け取ったJSONデータを解析し、天気情報を画面に表示

この例では、天気予報アプリはWebサーバーからWebページを受け取り、さらにAPIサーバーから天気情報を受け取っています。地図アプリも同様に、Webサーバーから地図を表示するためのWebページを受け取り、APIサーバーから地図データを受け取って表示しています。
### REST API
REST APIは、Representational State Transfer APIの略で、現在最も広く利用されているAPIの設計スタイルです。HTTPメソッド（GET, POST, PUT, DELETEなど）を用いてリソースを操作し、ステータスコードで処理結果を表現します。シンプルで柔軟性が高いため、WebサービスのAPIとして広く採用されています。

## サーバー内部でのリクエスト処理
クライアントから送信されたリクエストは、APIサーバー内部で様々な処理を経てレスポンスが生成されます。この章では、サーバー内部で行われる主要な処理について解説していきます。
### ルーティング
ルーティングとは、クライアントからリクエストされたURLに基づいて、適切な処理を行うリソース（モジュール、関数、クラスなど）にリクエストを振り分ける仕組みです。APIサーバーは、定義されたルーティングルールに基づいてリクエストを適切なエンドポイントに転送します。例えば、/usersへのGETリクエストであればユーザー一覧を取得する処理、/productsへのPOSTリクエストであれば新しい商品をデータベースに追加する処理といった具合です。
### バリデーション
バリデーションとは、クライアントから送信されたデータの形式や内容をチェックする処理です。チェック項目としては、データ型、値の範囲、必須項目の有無、文字列長などがあります。
### ビジネスロジック
ビジネスロジックとは、APIの主要な処理部分であり、APIの目的を実現するための具体的な処理内容です。データベースへのアクセス、データの加工、外部サービスとの連携など、APIの核心となる処理が含まれます。
### サーバー内部でのリクエスト処理例
以下に、Pythonの標準ライブラリのみを用いたAPIサーバーの例（GETリクエスト処理のみ）を示します。この例では、シンプルさを重視し、一部の機能（エラー処理など）は省略しています。少し長いですが、じっくり読み進めると理解していただけるのではないかと思います。
```python
from http.server import BaseHTTPRequestHandler, HTTPServer
import json

# データ群 (データベースの代わり)
users = {
    1: {'name': 'Alice', 'email': 'alice@example.com'},
    2: {'name': 'Bob', 'email': 'bob@example.com'},
}

class MyHandler(BaseHTTPRequestHandler):
    def do_GET(self):  # GETリクエストの処理
        # ルーティング設定 (URLのパスに応じて処理を分岐)
        if self.path.startswith('/users/'):  # /users/<user_id>へのリクエストかどうかを確認
            try:
                user_id = int(self.path[7:])  # URLからuser_idを取得。/users/以降の文字列を取得し、整数に変換

                # ビジネスロジック (ユーザーデータの取得)
                user = users.get(user_id)  # usersデータ群からuser_idに対応するユーザーデータを取得

                if user: # ユーザーデータが存在する場合
                    self.send_response(200)  # ステータスコード 200 OK を設定
                    self.send_header('Content-type', 'application/json')  # レスポンスヘッダーにContent-Typeを設定
                    self.end_headers()  # ヘッダーの送信を終了
                    self.wfile.write(json.dumps(user).encode())  # ユーザーデータをJSON形式にエンコードしてレスポンスボディとして送信
                else: # ユーザーデータが存在しない場合
                    self.send_response(404)  # ステータスコード 404 Not Found を設定
                    self.end_headers()  # ヘッダーの送信を終了
                    self.wfile.write(b'{"error": "User not found"}') # エラーメッセージをレスポンスボディとして送信

            except ValueError:  # user_idが整数に変換できない場合
                self.send_response(400)  # ステータスコード 400 Bad Request を設定
                self.end_headers()  # ヘッダーの送信を終了
                self.wfile.write(b'{"error": "Invalid user ID"}') # エラーメッセージをレスポンスボディとして送信


        else: # /users/<user_id>以外のパスへのリクエストの場合
            self.send_response(404)  # ステータスコード 404 Not Found を設定
            self.end_headers()  # ヘッダーの送信を終了



if __name__ == '__main__':  #スクリプトが直接実行された場合に以下を実行
    server_address = ('', 8000)  # ポート8000でAPIサーバーを起動
    httpd = HTTPServer(server_address, MyHandler) # APIサーバーを起動
    print('Starting server on port 8000...') # サーバー起動メッセージを表示
    httpd.serve_forever() # HTTPリクエストを受け付けることができる状態をキープ
```
do_GETメソッド内では、`if self.path.startswith('/users/')`で始まる部分がルーティング設定、`users.get(user_id)`の部分がビジネスロジックに該当します。このコードを実行するには、ターミナルで`python ファイル名.py`と入力します。その後、Webブラウザで`http://localhost:8000/users/1`などにアクセスすることで、APIをテストできます。
## レスポンスの生成と返却
APIサーバーは、クライアントからのリクエストを処理した後、結果をレスポンスとしてクライアントに返却します。レスポンスは、HTTPステータスコード、レスポンスヘッダー、レスポンスボディの3つの要素で構成されます。
### HTTPステータスコード、レスポンスヘッダー、レスポンスボディ
#### HTTPステータスコード
リクエストの処理結果を表す3桁の数字です。 200 OK、400 Bad Request、404 Not Found、500 Internal Server Error などがあります。
#### レスポンスヘッダー
レスポンスヘッダーは、レスポンスボディに関する追加情報を提供します。例えば、レスポンスボディのデータ形式や長さ、サーバーの情報などをクライアントに伝えます。
#### レスポンスボディ
リクエストに対する実際の応答データです。多くの場合、JSON 形式でデータを返却します。
### Pythonでのレスポンス生成
以下は、ユーザーデータのリストをJSON形式で返すAPIの例です。該当部分以外は省略しています。
```python
# ユーザーデータのリスト
users = [
    {'id': 1, 'name': 'Alice'},
    {'id': 2, 'name': 'Bob'},
]

class MyHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        if self.path == '/users':  # /usersへのリクエストの場合
            try:
                # レスポンスボディを作成
                response_body = json.dumps(users).encode('utf-8')

                # レスポンスヘッダーとステータスコードを設定
                self.send_response(200) # ステータスコード 200 OK
                self.send_header('Content-type', 'application/json') # Content-Typeヘッダー
                self.send_header('Content-length', str(len(response_body))) # Content-Lengthヘッダー
                self.end_headers() # ヘッダー送信完了

                # レスポンスボディを送信
                self.wfile.write(response_body)

            except Exception as e: # 例外処理
                self.send_response(500) # ステータスコード 500 Internal Server Error
                self.end_headers()
                self.wfile.write(b'Internal Server Error')
        else: # /users以外のパスへのリクエストの場合
             self.send_response(404) # ステータスコード 404 Not Found
             self.end_headers()
             self.wfile.write(b'Not Found')
```
この例では、`json.dumps()`でPythonのリストをJSON形式の文字列に変換し、`encode('utf-8')`でバイト列に変換しています。 `send_response()`でステータスコードを、`send_header()`でレスポンスヘッダーを、`wfile.write()`でレスポンスボディを送信しています。
送信されるjsonファイルは以下になります。
```json
[
    {"id": 1, "name": "Alice"},
    {"id": 2, "name": "Bob"}
]
```
## リクエスト処理のシーケンス図
APIサーバーのリクエスト処理の流れを視覚的に理解するために、シーケンス図を作成しました。この図は、一般的なAPIサーバーをモデルとしており、クライアント、Webサーバー、APIサーバー、データベース間のやりとりを示しています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4030603/88b8358c-4cab-4d03-ad17-f8fb21ca69b4.png)
図の各要素の解説:
- クライアント: APIを利用するアプリケーションやユーザー
- Webサーバー: クライアントからのリクエストを受け付け、APIサーバーに転送。また、APIサーバーからのレスポンスをクライアントに返却。
- APIサーバー: ビジネスロジックを実行し、データベースとのやり取りを行う
- データベース: データの保存と取得を行う
## まとめ
VSCodeでz/OSを操作する環境構築をきっかけに、APIにおけるサーバーサイド処理の理解が重要だと感じ、調査した内容をまとめました。

簡潔にまとめると以下のようになります。
- APIのリクエストを受け付けるサーバーには、リクエストを処理するためのルーティング、バリデーション、ビジネスロジックの定義がある
- HTTPリクエストが来ると、まずルーティングに基づいてリクエストに対応する処理（関数やメソッド）が呼び出される
- 次にバリデーションによってリクエストデータの形式や内容がチェックされる
- バリデーションを通過した場合、ビジネスロジックに記載の処理（データベースのデータ取得や更新など）が実行される

z/OSMFのようなサーバー内部の理解に時間を要した経験から、一般的なサーバーサイド処理に焦点を当て、他サーバーの理解にも役立つよう努めました。本記事がAPIにおけるサーバーサイドの処理フロー理解の一助になれば幸いです。
### 参考文献
[Express でのルーティング](https://expressjs.com/ja/guide/routing.html "Express でのルーティング")
[Express ミドルウェアの使用](https://expressjs.com/ja/guide/using-middleware.html "Express ミドルウェアの使用")
[Rails Routing from the Outside In — Ruby on Rails Guides](https://guides.rubyonrails.org/routing.html "Rails Routing from the Outside In — Ruby on Rails Guides")
[Quickstart — Flask Documentation (3.1.x)](https://flask.palletsprojects.com/en/stable/quickstart/#routing "Quickstart — Flask Documentation (3.1.x)")
[「ビジネスロジック」とは何か、どう実装するのか #設計 - Qiita](https://qiita.com/os1ma/items/25725edfe3c2af93d735 "「ビジネスロジック」とは何か、どう実装するのか #設計 - Qiita")
[APIサーバーとは？分かりやすい説明はこちら！](https://apidog.com/jp/blog/api-server/ "APIサーバーとは？分かりやすい説明はこちら！")
[MVC、3 層アーキテクチャから設計を学び始めるための基礎知識 #初心者 - Qiita](https://qiita.com/os1ma/items/7a229585ebdd8b7d86c2 "MVC、3 層アーキテクチャから設計を学び始めるための基礎知識 #初心者 - Qiita")
[node.jsを使って簡単なAPIサーバーを構築する方法(Express + TypeScript + Vite)](https://zenn.dev/yuzunosk55/articles/fecce3c722a1bf "node.jsを使って簡単なAPIサーバーを構築する方法(Express + TypeScript + Vite)")