HTTP通信

ローカルサーバーでアプリケーションが実行できるようになったので、次はGitHubのAPIを呼び出す処理を実装していきます。 GitHubのAPIを呼び出すためにはHTTP通信をする必要があります。 ウェブブラウザ上でJavaScriptからHTTP通信するために、Fetch APIという機能を使います。

Fetch API

Fetch APIはHTTP通信を行いリソースを取得するためのAPIです。 Fetch APIを使うことで、ページ全体を再読み込みすることなく指定したURLからデータを取得できます。 Fetch APIは同じくHTTP通信を扱うXMLHttpRequestと似たAPIですが、より強力で柔軟な操作が可能です。

GitHubが提供している、ユーザー情報を取得するためのWebAPIを呼び出すコードは次のようになります。 リクエストを送信するためには、fetchメソッドを利用します。 fetchメソッドにURLを与えることで、HTTPリクエストが作成され、サーバーとのHTTP通信を開始します。

const userId = "任意のGitHubアカウントID";
fetch(`https://api.github.com/users/${userId}`);

レスポンスの受け取り

GitHubのAPIに対してHTTPリクエストを送信しましたが、まだレスポンスを受け取る処理を書いていません。 次はサーバーから返却されたレスポンスのログをコンソールに出力する処理を実装します。

fetchメソッドはPromiseを返します。このPromiseインスタンスはリクエストのレスポンスを表すResponseオブジェクトでresolveされます。 送信したリクエストにレスポンスが返却されると、thenコールバックが呼び出されます。

次のように、Responseオブジェクトのstatusプロパティからは、HTTPレスポンスのステータスコードが取得できます。 また、ResponseオブジェクトのjsonメソッドもPromiseを返します。これは、HTTPレスポンスボディをJSONとしてパースしたオブジェクトでresolveされます。 ここでは、書籍用に用意したjs-primer-exampleというGitHubアカウントのユーザー情報を取得しています。

const userId = "js-primer-example";
fetch(`https://api.github.com/users/${userId}`)
    .then(response => {
        console.log(response.status); // => 200
        response.json().then(userInfo => {
            // JSONパースされたオブジェクトが渡される
            console.log(userInfo); // => {...}
        });
    });

エラーハンドリング

HTTP通信にはエラーがつきものです。 そのためFetch APIを使った通信においても、エラーをハンドリングする必要があります。 サーバーとの通信に際してネットワークエラーが発生した場合は、ネットワークエラーを表すNetworkErrorオブジェクトでrejectされたPromiseが返されます。 すなわち、thenメソッドの第2引数かcatchメソッドのコールバック関数が呼び出されます。

const userId = "js-primer-example";
fetch(`https://api.github.com/users/${userId}`)
    .then(response => {
        console.log(response.status);
        response.json().then(userInfo => {
            console.log(userInfo);
        });
    }).catch(error => {
        console.error("ネットワークエラー", error);
    });

一方で、リクエストが成功したかどうかはResponseオブジェクトのokプロパティで認識できます。 okプロパティは、HTTPステータスコードが200番台であればtrueを返し、それ以外の400や500番台などならfalseを返します。 次のように、okプロパティがfalseとなるサーバーエラーをハンドリングできます。

const userId = "js-primer-example";
fetch(`https://api.github.com/users/${userId}`)
    .then(response => {
        console.log(response.status); 
        // エラーレスポンスが返されたことを検知する
        if (!response.ok) {
            console.error("サーバーエラー", response);
        } else {
            response.json().then(userInfo => {
                console.log(userInfo);
            });
        }
    }).catch(error => {
        console.error("ネットワークエラー", error);
    });

ここまでの内容をまとめ、GitHubからユーザー情報を取得する関数をfetchUserInfoという名前で定義します。

function fetchUserInfo(userId) {
    fetch(`https://api.github.com/users/${userId}`)
        .then(response => {
            console.log(response.status);
            // エラーレスポンスが返されたことを検知する
            if (!response.ok) {
                console.error("サーバーエラー", response);
            } else {
                response.json().then(userInfo => {
                    console.log(userInfo);
                });
            }
        }).catch(error => {
            console.error("ネットワークエラー", error);
        });
}

index.jsでは関数を定義しているだけで、呼び出しは行っていません。

ページを読み込むたびにGitHubのAPIを呼び出すと、呼び出し回数の制限を超えるおそれがあります。 呼び出し回数の制限を超えると、APIからのレスポンスがステータスコード403のサーバーエラーになってしまいます。

そのため、手動でfetchUserInfo関数を呼び出すため、HTMLドキュメント側にボタンを追加します。 ボタンのclickイベントでfetchUserInfo関数を呼び出し、取得したいユーザーIDを引数として与えています。 例としてjs-primer-exampleという書籍用に用意したGitHubアカウントを指定しています。

<html lang="ja">
  <head>
    <meta charset="utf-8" />
    <title>Ajax Example</title>
  </head>
  <body>
    <h2>GitHub User Info</h2>

    <button onclick="fetchUserInfo('js-primer-example');">Get user info</button>
    <script src="index.js"></script>
  </body>
</html>

準備ができたら、ローカルサーバーを立ち上げてindex.htmlにアクセスしましょう。 ボタンを押すとHTTP通信が行われ、コンソールにステータスコードとレスポンスのログが出力されます。

Fetchで取得したデータの表示

また、開発者ツールのネットワークパネルを開くと、GitHubのサーバーに対してHTTP通信が行われていることを確認できます。

開発者ツールでHTTP通信の記録を確認する

[コラム] XMLHttpRequest

XMLHttpRequestXHR)はFetch APIと同じくHTTP通信を行うためのAPIです。 Fetch APIが標準化される以前は、ブラウザとサーバーの間で通信を行うにはXHRを使うのが一般的でした。 このセクションで扱ったFetch APIによるfetchUserInfo関数は、XHRを使うと次のように書けます。

function fetchUserInfo(userId) {
    // リクエストを作成する
    const request = new XMLHttpRequest();
    request.open("GET", `https://api.github.com/users/${userId}`);
    request.addEventListener("load", () => {
        // リクエストが成功したかを判定する
        // Fetch APIのresponse.okと同等の意味
        if (request.status >= 200 && request.status < 300) {
            // レスポンス文字列をJSONオブジェクトにパースする
            const userInfo = JSON.parse(request.responseText);
            console.log(userInfo);
        } else {
            console.error("サーバーエラー", request.statusText);
        }
    });
    request.addEventListener("error", () => {
        console.error("ネットワークエラー");
    });
    // リクエストを送信する
    request.send();
}

Fetch APIはXHRを置き換えるために作られたもので、多くのユースケースではXHRを使う必要はなくなっています。 ただし、古いブラウザではFetch APIが実装されていないため、ブラウザの互換性を保つためにXHRを使う場面もまだあります。 XHRの詳しい使い方については、XHRの利用についてのドキュメントを参照してください。

このセクションのチェックリスト

  • Fetch APIを使ってHTTPリクエストを送った
  • GitHubのAPIから取得したユーザー情報のJSONオブジェクトをコンソールに出力した
  • Fetch APIの呼び出しに対するエラーハンドリングをおこなった
  • fetchUserInfo関数を宣言し、ボタンのクリックイベントで呼び出した