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通信を開始します。

fetch(`https://api.github.com/users/${userId}`);

レスポンスの受け取り

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

fetchメソッドはPromiseを返します。これはリクエストのレスポンスを表すResponseオブジェクトでresolveされます。 送信したリクエストにレスポンスが返却されると、thenコールバックが呼び出されます。 次のように、ResponseオブジェクトのstatusプロパティからはHTTPレスポンスのステータスコードが取得できます。 また、jsonメソッドもPromiseを返します。これは HTTPレスポンスをJSONとしてパースしたオブジェクトでresolveされます。

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メソッドのコールバック関数が呼び出されます。

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であるサーバーエラーをハンドリングできます。

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からユーザー情報を取得する関数をgetUserInfoという名前で定義します。

function getUserInfo(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のサーバーエラーになってしまいます。 そこでgetUserInfo関数を呼び出すため、HTMLドキュメント側にボタンを追加します。 ボタンのclickイベントでgetUserInfo関数を呼び出し、固定のユーザーIDを引数として与えています。

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

    <button onclick="getUserInfo('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によるgetUserInfo関数は、XHRを使うと次のようになります。

function getUserInfo(userId) {
    // リクエストを作成する
    const request = new XMLHttpRequest();
    request.open("GET", `https://api.github.com/users/${userId}`);
    request.addEventListener("load", () => {
        // ステータス4XXと5XXをサーバーエラーとする
        if (request.status >= 400 || request.status <= 599) {
            console.error("サーバーエラー", request.statusText);
        } else {
            // レスポンス文字列をJSONオブジェクトにパースする
            const userInfo = JSON.parse(request.responseText);
            console.log(userInfo);
        }
    });
    request.addEventListener("error", () => {
        console.error("ネットワークエラー");
    });
    // リクエストを送信する
    request.send();
}

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

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

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