シンプルなJavaScriptやモダンなReactなどを使っていると「データをフェッチする」という言葉を聞いたことがあるかと思います。
結論、外部からデータを取得するという意味になるのですが意外と初学者の方はフェッチを習わずに実務に入っていたりします。
最近ではフェッチ自体にライブラリを使うことが当たり前になったので、どちらかと言うと「ライブラリの使い方を覚える」と言う感じかもしれません。
JavaScriptにはfetch()という関数が用意されていて、こちらの使い方を知っておくとライブラリを使うときの理解が進むかと思います。
fetch()をそのまま使っている現場は少なくなっていますが、仕組みを理解するために一度触ってみましょう。
また動画もあるので必要に応じて活用してください。
JavaScriptにおけるfetchの基本的な使い方
冒頭にフェッチとは外部からデータを取得する行為だと説明しました。
データと言っても色んな種類があって、それぞれで細かく文法が変わりますので何点か例を出しながら解説していきます。
まずはテキストデータを扱うパターンです。
テキストデータです
fetch("test.txt")
.then((data) => data.text())
.then((res) => console.log(res));
// 「テキストデータです」と出力される
fetch関数は引数に取得するデータのパスやURLを指定します。
さらにthenメソッドで繋げてtext()を実行するとテキストデータを文字列に変換します。
thenメソッドで繋げているのは「前の処理を待つ」ことが目的です。
上記のコードの場合だとtest.txtが読み込めていないのにtext()を実行しても意味がないからです。
分かりやすくするためにfetchとは別にコンソールログを追加してみます。
fetch("test.txt")
.then((data) => data.text())
.then((res) => console.log(res));
// ここを追加
console.log("done");
// 「done」が最初に出力される
学習教材やスクールで「プログラムは上から下に向かって順番に実行される」と習ったかと思います。
しかし上記の場合は、下に書いたはずのコンソールログが先に実行されます。
fetch関数にthenメソッドを繋げているため時間がかかっており、その間にconsole.log(“done”)が実行されたわけです。
このように処理を開始するタイミングをコントロールする考え方を「非同期処理」と言って、非同期処理を簡単に理解するための記事を別で公開しているので参考までにどうぞ。
またfetch関数のような非同期処理はJavaScriptの場合、別の書き方で表現するのが最近の傾向としてあるので紹介しておきます。
async function fetchData() {
const data = await fetch("data.json");
const res = await data.json();
console.log(res);
}
fetchData();
こちら先ほどのthenメソッドの部分を書き換えている形です。
「async」がついた関数は自動的に「await」を使うルールになっていて、awaitというのが先ほどで言うところのthenの役割になっています。
書き方こそ違いますが結果は同じになるので両方の書き方に慣れておくことをお勧めします。
// ①thenを使った書き方
fetch("test.txt")
.then((data) => data.text())
.then((res) => console.log(res));
// 「テキストデータです」と出力される
// ②async,awaitを使った書き方
async function fetchData() {
const data = await fetch("data.json");
const res = await data.json();
console.log(res);
}
fetchData();
// 「テキストデータです」と出力される
JavaScriptにおけるfetchでJSONデータを取得する方法
続いてJSONデータをfetchで使う方法です。
{
"id": "0001",
"name": "AAA",
"age": 20
}
fetch("data.json")
.then((data) => data.json())
.then((res) => console.log(res));
// {"id": "0001", "name": "AAA", "age": 20}と出力される
JSONデータの場合はjson()を使うことでデータの中身を読み込んでオブジェクトに変換します。
こちらasync,awaitを使った書き方は以下のようになります。
async function fetchData() {
const data = await fetch("data.json");
console.log(data);
const res = await data.json();
console.log(res);
}
fetchData();
ちなみに「.then」で繋げる書き方をPromiseチェーンと、「async」での書き方をasync/awaitと呼ぶことが多いです。
書き方の違いが大きく出るのがエラー処理を含めたコードを書く時です。
例題としてAPIからJSONデータを取得する処理をエラー処理を含めて、それぞれの書き方で書いてみたいと思います。
まずはPromiseチェーンで書いてみます。
fetch("https://jsonplaceholder.typicode.com/posts")
.then((res) => {
if (!res.ok) {
throw new Error("fetchに失敗しました");
}
return res.json()
})
.then((data) => console.log(data))
.catch((error) => console.error("エラーです:", error));
fetchメソッドで指定したURLに間違いがあった時に備えてエラー処理を追加しました。
Promiseチェーンでのエラー処理は最後に「.catch( )」の中で書くのがルールです。
上記の場合だとfetchメソッドに失敗した場合は「res.ok」がfalseになる想定をしてthenメソッドの中に条件分岐を書きました。
条件分岐に入った時は「”fetchに失敗しました”」というエラー文がcatchメソッドに投げられます。
投げられたエラー文はcatchメソッドの引数で受け取ることで、catchメソッドの中で使用することになります。
結果としてコンソールに「fetchに失敗しました」が表示されるわけです。
続いて同じものをasync/awaitで書き直してみます。
async function fetchFunc() {
try {
const res = await fetch("https://jsonplaceholder.typicode.com/posts");
if (!res.ok) {
throw new Error("fetchに失敗しました");
}
const data = await res.json();
console.log(data);
} catch (error) {
console.error("エラーです:", error);
}
}
fetchFunc();
async/awaitの書き方だとtry catchでエラー処理をします。
tryの中で非同期処理をまとめて書いておき、エラーがあった時の処理をcatchの中に書くルールです。
初学者の方には条件分岐のようなイメージが分かりやすいと思います。
2つの書き方はどちらも正解ですが、async/awaitの方が「throw new Error(“fetchに失敗しました”);」を書く場所に迷いにくいです。
Promiseチェーンで書く方法だと「.then」がどんどん増えていくことで「どのthenメソッドの中にエラー文を書けば良いんだ?」となる可能性が高いです。
熟練されたエンジニアであれば問題ないですが初学者の方には注意でしょう。
2つの書き方を練習しながら実際の開発ではasyc/awaitをメインに書く方が良いかもしれませんね。
またAPI関連でよく出てくるJSONデータというものが初めての方には詳細を解説した記事があるので参考にしてください。
JavaScriptにおけるfetchで画像データを扱う方法
fetchでは画像データを読み込むこともできます。
fetch("../images/image.png")
.then((data) => data.blob())
.then((res) => console.log(res));
画像データの場合はblob()で読み込むのですが、blob()の返り値はオブジェクトになっています。
実際の画面に読み込んだ画像を表示するところまでやってみたいと思います。
まずはHTMLに画像を表示するのに必要なimgタグを新しく作成します。
fetch("../images/image.png")
.then((data) => data.blob())
.then((res) => {
// ここから修正
const image = new Image();
console.log(image);
});
imgタグを作ることができましたね、続いてsrc属性にパスを作成したいと思います。
fetch("../images/image.png")
.then((data) => data.blob())
.then((res) => {
const image = new Image();
// ここから修正
image.src = URL.createObjectURL(res);
console.log(image);
});
src属性を追加することができました、パスのURLは何だか分からない文字列になっていますが先ほど実行したblob()の結果になります。
あとはHTMLに作成したimgタグを挿入するだけです。
fetch("../images/image.png")
.then((data) => data.blob())
.then((res) => {
const image = new Image();
image.src = URL.createObjectURL(res);
// ここを追加
document.body.appendChild(image);
});
ちなみにこちらもasync,awaitを使った書き方も紹介しておきます。
async function fetchImg() {
const data = await fetch("../images/image.png");
const res = await data.blob();
const image = new Image();
image.src = URL.createObjectURL(res);
document.body.appendChild(image);
}
fetchImg();
どちらの書き方もよく使用しますので両方書けるように練習しておきましょう。
fetchメソッドでAPIデータを取得、更新する方法
fetchメソッドの一番多い使用目的は外部のAPIからデータを取得する時です。
例えば以下のAPIからデータを取得するとします。
https://jsonplaceholder.typicode.com/
fetch('https://jsonplaceholder.typicode.com/todos/')
.then((res) => {
console.log(res);
return res.json();
})
.then((data) => console.log(data));
fetchメソッドの引数に指定されたURLを文字列として入れます。
続いて返ってきた値をthenの部分で取得できるわけですがコンソールで見てみると以下のようになるはずです。
こちらはレスポンスオブジェクトと言って今回のような外部サービスから情報を取得する場合、ネット環境やユーザーの操作によって通信が途中で失敗するケースがあります。
そのためデータ取得の前に「通信がうまくいったか」をレスポンスオブジェクトというもので返します。
レスポンスオブジェクトのokプロパティがtrueの時だけデータが取得されて、falseであれば失敗していますのでデータは返ってこないことをコンソール上で確認できるようになっています。
通信がOKだった場合にはさらにthenメソッドを繋げてレスポンスオブジェクトに対してjsonメソッドを実行することでデータの取得内容がわかります。
jsonメソッドは今回の使用したAPIが「データはJSON形式」になっているため使いましたが、必ずしもjsonメソッドを使うというものではありません。
jsonメソッドの結果は以下のよう何なります。
今やっていたのがデータの取得だったので、データの更新をやってみます。
fetchメソッドはデータの更新いわゆる投稿という作業にも使用することができますが、引数の指定が少し違う書き方をします。
fetch('https://jsonplaceholder.typicode.com/posts', {
method: 'POST',
body: JSON.stringify({
title: 'foo',
body: 'bar',
userId: 1,
}),
headers: {
'Content-type': 'application/json; charset=UTF-8',
},
})
.then((res) => res.json())
.then((data) => console.log(data));
URLを入れたりthenメソッドの部分は同じですが、fetchメソッドの第二引数にオブジェクト形式のものを書き足しているのがわかります。
外部のDBやAPIと通信する時にはデータ本体だけでなく、先ほど話した「成功?失敗?」などいろんな情報を一緒に送ります。
これをHTTP通信と呼んでいて、主には「ヘッダー」「ボディ」「メソッド」が代表的なキーワードとしてあります。
「ヘッダー」とは上記のコードでheadersとなっている部分で、主にはデータ形式などを書く場所です。
最低限の内容は書かなくても自動で付与されるので先ほどのデータ取得時には必要なかったのですが、今回のようなデータを書き換える、更新については書くことになります。
データ本体は「ボディ」と言って上記のコードでbodyとなっている部分です。
加えて取得、更新、削除などを「メソッド」というキーワードで上記のコードではmethodと書いている部分です。
データ取得の際にはメソッドがデフォルトでgetという値ですでに登録されているので先ほどの時には書きませんでした。
今回は更新ということでpostという値にする必要があるのでmethod: “post”と書いています
上記のコードを実行すると以下のようなオブジェクト形式のものがコンソールに表示されているはずです。
今回はコンソールに更新内容を出力していますが、実際のアプリやサイトでは画面に表示したりします。
fetchを使って外部と通信をすると専門用語が登場するので初心者の方は苦手意識を覚えてしまいがちですが、簡単なものから練習していけば理解できるので頑張ってみてください。
また今回参考にした本は以下になりますので良ければどうぞ。