TypeScriptでAxiosのレスポンスデータにバリデーションをかける方法!API通信の安全な型チェック
生徒
「TypeScriptでインターネットからデータをダウンロードするとき、そのデータが本当に正しい形をしているか調べる方法はありますか?」
先生
「アクシオス(Axios)という道具で通信したあとに、バリデーションという仕組みを使って、中身を自動で検査する方法がありますよ。」
生徒
「予想と違うおかしなデータが届いたときに、プログラムが壊れないように先回りでチェックするのですね!具体的にはどのように行うのですか?」
先生
「それでは、初心者の方でも絶対にマスターできる基本のやり方と、入力チェックの具体的な書き方を順番に見ていきましょう!」
1. API通信とレスポンスデータとは?
インターネットの向こう側にあるコンピューターと、手元にあるパソコンやスマートフォンとの間で、情報のやり取りをすることをAPI通信(エーピーアイつうしん)と呼びます。この通信を行うための便利な部品として、多くの開発現場で使われているのがAxios(アクシオス)というライブラリです。ライブラリとは、複雑なプログラムの詰め合わせのことで、これを使うと数行の命令で簡単にインターネット通信が実現できます。
そして、こちらのコンピューターから「データをください」とお願いしたことに対して、向こう側のサーバーから戻ってきた返事のデータのことをレスポンスデータと言います。例えば、お天気アプリなら明日の気温や天気の文字、SNSアプリなら投稿された文章や写真の場所が、レスポンスデータとして手元の画面に送られてきます。このデータは、インターネットの線を伝って、目に見えない電気信号として届くため、受け取る側でしっかりと確認をすることが重要です。
2. バリデーションが必要な理由と型安全
パソコンのプログラムを書いたことがない方は、「インターネットから届くデータなら、最初から正しい形になっているはずではないか」と思うかもしれません。しかし、インターネットの世界では、通信の途中でエラーが起きたり、相手のシステムが急に新しくなってデータの仕組みが変わってしまったりすることがあります。もし、数字が届くはずの場所に、全く関係のない文字が紛れ込んでいたら、パソコンのプログラムはパニックを起こして強制終了してしまいます。スマートフォンでアプリが突然消えてしまう現象は、このような想定外のデータが原因であることも多いのです。
ここで登場するのがバリデーション(入力チェック・妥当性確認)という作業です。届いたデータが、私たちが決めた約束通りの正しい形式になっているかを、プログラムの門番のチェックポイントのように一台一台検査します。TypeScriptというプログラミング言語は、データの種類を厳しく見分ける型安全(かたあんぜん)という特徴を持っています。この特徴を活かして、怪しいデータは中に入れないように入り口で徹底的にブロックすることが、アプリを絶対に壊さないための最大の秘訣です。
3. 最もシンプルな手動でのデータ型チェック
まずは、特別な道具を新しく追加せずに、TypeScriptが標準で持っている機能を使ってデータの中身を調べる方法を学びましょう。これを手動でのバリデーションと呼びます。届いたデータが、文字なのか、それとも数字なのかを、一つひとつ順番に確認していく最も基本的な方法です。パソコンに慣れていない方でも、条件分岐の仕組みを使えば簡単に理解することができます。
以下のプログラムは、インターネットからユーザーの名前を受け取ったと仮定して、その名前が本当に「文字」のデータになっているかを検査するコードです。
function checkUserData(receivedData: any) {
if (typeof receivedData.name === "string") {
console.log("名前のデータは正しい文字でした");
} else {
console.log("エラーです。名前が文字ではありません");
}
}
let testData1 = { name: "山田太郎" };
checkUserData(testData1);
このコードを実行すると、画面には以下のように出力されます。データが正しいことを確認して安全に処理が進みます。
名前のデータは正しい文字でした
ここで使われているtypeofという命令は、データの種類を調べるための特別な合言葉です。もし相手から届いたデータが数字の123だった場合、この門番がブロックしてエラーのメッセージを表示してくれます。これが、最も単純で確実なチェックの第一歩です。
4. Axiosで取得した未確認のデータを扱う方法
実際にAxiosを使ってインターネットからデータをダウンロードするとき、最初の状態ではそのデータに何が入っているか分かりません。TypeScriptでは、何が入っているか分からない未知のデータのことをunknown(アンノウン)型、または何でも許してしまうany(エニィ)型として扱います。プログラミングの初心者の方は、何でも入るany型を使いがちですが、これを使うと門番が居眠りをしてしまい、おかしなデータが素通りしてしまいます。
そのため、通信の直後は一度データを「正体不明の箱」に入れて、厳重に包みを解いていく必要があります。次のサンプルプログラムでは、Axiosの通信によって商品データを持ってきた場面を再現しています。IDという番号が、ちゃんと数字の型になっているかをチェックしています。
function verifyProductResponse(response: any) {
let rawData = response.data;
if (typeof rawData.id === "number") {
console.log("商品の番号が数字であることを確認しました");
} else {
console.log("データの形がおかしいので処理を中断します");
}
}
let mockResponse = { data: { id: 505 } };
verifyProductResponse(mockResponse);
プログラムを実行すると、数字であるかどうかが正しく判定されて、次の結果が表示されます。
商品の番号が数字であることを確認しました
このように、インターネットから来た生の情報(ローデータ)をそのまま信用せず、まずは仮の場所で中身を覗き見ることが、安全な通信を行うための基本姿勢です。
5. 複数の項目を一度に検証する詳細な判定手順
実際のインターネット通信では、名前や番号だけでなく、たくさんの情報がセットになって一度にドサッと送られてきます。例えば、ユーザーの登録情報であれば、名前と、年齢と、プレミアム会員かどうかのフラグという、3つの異なる種類のデータが合体しています。これらをすべて同時に、漏れなくチェックするためには、条件を && という記号で繋いで、すべての条件が合格であるかを判定します。
少し難しく見えるかもしれませんが、一つひとつの部品はこれまでに学んだことの組み合わせです。慌てずにゆっくりコードを眺めてみてください。プログラミング未経験の方でも、パズルのように条件が揃っているかを見比べることができます。
function checkFullProfile(data: any) {
if (typeof data.username === "string" && typeof data.age === "number" && typeof data.isAdmin === "boolean") {
console.log("すべての項目が完璧に一致しました。安全です");
} else {
console.log("データに不備があります。危険なデータです");
}
}
let badData = { username: "会員A", age: "二十歳", isAdmin: false };
checkFullProfile(badData);
上記の実験用データでは、年齢の項目が数字の20ではなく、「二十歳」という日本語の文字になってしまっています。そのため、門番に見つかってしまい、次のようなエラーの結果が出力されます。
データに不備があります。危険なデータです
このように、一つの項目でも嘘のデータが混ざっていれば、それを即座に発見して排除することができるのが、連鎖的な判定処理の大きなメリットです。
6. 複雑な構造のデータに対応する関数での部品化
データの中には、箱の中にさらに小さな箱が入っているような、複雑な入れ子構造になっているものもあります。例えば、お店のデータの中に、住所という別の項目があり、その住所の中にさらに郵便番号や県名が隠されているような場合です。これを一度に全部調べようとすると、プログラムが長くなってしまい、どこを読んでいるのか分からなくなってしまいます。パソコンの操作が初めての方でも、役割ごとにプログラムを細かく小分けに分ければ、頭の中がすっきりと整理されます。
住所を専門にチェックする専用の部品(関数)を作り、それを大きなチェック処理の中で呼び出す高度な書き方に挑戦してみましょう。これにより、どれだけ巨大なデータがインターネットから送られてきても、迷子にならずに処理を組み立てることができます。
function isAddressValid(address: any): boolean {
if (!address) return false;
return typeof address.city === "string";
}
function checkShopData(shop: any) {
if (typeof shop.shopName === "string" && isAddressValid(shop.location)) {
console.log("お店の名前も住所も正しい形式です");
} else {
console.log("お店のデータが壊れています");
}
}
let safeShop = { shopName: "駅前カフェ", location: { city: "東京都" } };
checkShopData(safeShop);
このプログラムを実行すると、小分けにされた部品同士がしっかりと連携して動き、以下の結果を導き出します。
お店の名前も住所も正しい形式です
一見すると難解なプログラムも、このように小さな確認作業の積み重ねでできていることが分かると、エラーを未然に防ぐ防御壁の仕組みがとても親しみやすいものに感じられるはずです。