TypeScriptでAPIレスポンスをバリデーションする手法!安全な型チェックとZodによるデータ検証の基本
生徒
「TypeScriptを使ってインターネットからデータを読み込むとき、そのデータが本当に正しい形をしているか確かめる方法ってありますか」
先生
「データを安全に受け取るために、APIレスポンスのバリデーションという仕組みを使います。これを行うことで、予期しないエラーを未然に防ぐことができます」
生徒
「具体的にはどのように確かめていけばいいのですか」
先生
「それでは、パソコンに触ったことがない方でも分かるように、基本的なデータ検証の仕組みと便利なライブラリの使い方を見ていきましょう」
1. APIレスポンスとバリデーションとは何か
インターネット上のサービスでは、別の場所にあるコンピューターからデータを手に入れる機会がたくさんあります。この別のコンピューターから返ってくるお返事のことをAPIレスポンスと呼びます。例えば、お天気アプリが今日の天気を教えてもらうときや、通販サイトが商品の名前の一覧を受け取るときなどがこれに当たります。
しかし、受け取るデータがいつも予想通りの形をしているとは限りません。中身が空っぽだったり、文字が入るはずの場所に数字が入っていたりすることがあります。もし間違ったデータがそのままプログラムに入り込むと、画面が真っ白になったり動きが止まったりします。そこで、データが正しいかどうかを入り口で厳しくチェックする作業が必要になります。この検査のことをバリデーション、またはデータ検証と呼びます。TypeScriptを使う大きな目的は安全性を高めることですが、この検証を行うことでさらに強力な壁を作ることができます。
2. なぜTypeScriptの型定義だけでは足りないのか
プログラムを作成するとき、データの種類や形をあらかじめ決めておく設計図のようなものを型定義と呼びます。これを用意しておくと、文字を入れる箱に間違えて数字を入れようとしたときに、パソコンが事前に注意してくれます。これによって、間違いの少ない綺麗な仕組みを作ることができます。
しかし、ここに大きな落とし穴があります。TypeScriptの型定義が効果を発揮するのは、あくまでプログラムを組み立てている最中だけです。実際にプログラムが動き出した後、インターネットの向こう側から送られてくる本物のデータに対しては、その設計図が正しいかどうかを監視する力がありません。つまり、動いている最中に突然おかしなデータが送られてきても、型定義だけではそれを防ぐことができないのです。そのため、プログラムが動いているリアルタイムの段階で、届いたデータを一つひとつ確認する具体的な仕組みが不可欠になります。
3. 原始的な方法でデータの中身を確認する仕組み
まずは、特別な道具を使わずに、プログラムが標準で持っている機能を使ってデータを確かめる方法を学びましょう。最も単純な方法は、データの種類を調べる命令を使って、中身が文字か数字かを一つずつ確認していくやり方です。これを条件分岐の仕組みと組み合わせて行います。
以下の例では、受け取ったデータが正しい文字列であるかどうかを判定しています。初心者の方でも分かりやすいように、名前を確認するだけの簡単な仕組みにしています。
function checkUserData(data: any) {
if (typeof data === "string") {
console.log("正しい文字のデータです");
} else {
console.log("エラーです。文字ではありません");
}
}
checkUserData("山田太郎");
checkUserData(123);
実行した結果は以下のようになります。文字列を渡したときは正常に動き、数字を渡したときにはエラーを検知できていることが分かります。
正しい文字のデータです
エラーです。文字ではありません
この方法は単純で分かりやすいですが、調べる項目が数十個に増えたり、複雑に入り組んだデータになったりすると、プログラムが非常に長くなってしまい、作成するのが大変になるという弱点があります。
4. 便利な検証道具であるZodライブラリの基本
前述の通り、一つずつ手作業で確認していくのは非常に骨が折れる作業です。そこで、世界中の開発者が利用している便利な道具を紹介します。それがZod(ゾッド)というライブラリです。ライブラリとは、複雑な処理を誰でも簡単に使えるようにまとめた部品集のようなものです。
Zodを使うと、「このような形のデータだけを許可します」という明確なルールを簡単な記述で作ることができます。このルールのことをスキーマと呼びます。駅の自動改札機のようなものをイメージしてください。切符を持っている人だけを通し、持っていない人は止めるという役割を、短いプログラムで実現できるようになります。これにより、安全性が劇的に向上します。
5. Zodを使って簡単なデータを検証する具体的な手順
それでは、実際にZodを使ってデータを検査するプログラムを書いてみましょう。ここでは、ユーザーの識別番号と、お名前の二つの情報が入ったデータを想定します。識別番号は数字、お名前は文字でなければならないというルールを定めます。
プログラムの中にある命令は、それぞれの項目が指定した通りの種類になっているかを自動的に見極めてくれます。もしルールに違反しているデータが紛れ込んできたら、すぐにそれを教えてくれる仕掛けになっています。
import { z } from "zod";
const UserSchema = z.object({
id: z.number(),
name: z.string()
});
const result = UserSchema.safeParse({ id: 1, name: "佐藤" });
if (result.success) {
console.log("検査に合格しました");
console.log(result.data.name);
} else {
console.log("検査に不合格です");
}
このプログラムを動かしてみると、以下のような結果が画面に表示されます。すべてのルールを守っているため、無事に合格となります。
検査に合格しました
佐藤
もし識別番号のところに文字を入れてしまったり、お名前が抜けていたりすると、不合格の処理が実行されます。手作業で細かく条件を書く必要がなくなるため、全体の記述がとてもすっきりします。
6. 実際の通信を想定した応用的なデータ検証の方法
次に、実際のインターネット通信により近い形での検証方法を見ていきましょう。ウェブサイトからデータを取得するとき、もし通信相手のコンピューターに不具合があると、全く予期しないデータが送られてくることがあります。そうした場合でも、システム全体が壊れてしまわないように防御壁を構築します。
以下の例では、商品の情報を受け取る場面を想定しています。商品の価格が数字になっているか、説明文が正しく存在しているかを厳重にチェックする仕組みです。間違ったデータが届いた場合には、その理由を画面に表示するようにしています。
import { z } from "zod";
const ProductSchema = z.object({
title: z.string(),
price: z.number()
});
function validateProductResponse(response: any) {
const checkResult = ProductSchema.safeParse(response);
if (!checkResult.success) {
console.log("不正なデータを発見しました");
console.log(checkResult.error.message);
return;
}
console.log("商品名: " + checkResult.data.title);
console.log("価格: " + checkResult.data.price + "円");
}
validateProductResponse({ title: "おいしいリンゴ", price: "百円" });
この例では、価格として数字ではなく「百円」という文字を渡してしまっています。そのため、検査の仕組みが間違いを検知し、以下のような警告メッセージが出力されます。
不正なデータを発見しました
[{"code":"invalid_type","expected":"number","received":"string","path":["price"],"message":"Expected number, received string"}]
画面には英語で難しい内容が表示されていますが、これは「価格の場所には数字を期待していましたが、文字が届きました」ということを意味しています。これにより、どこが間違っているのかを瞬時に特定することができます。
7. 安全なプログラムを維持するために日頃から意識すべきこと
インターネットの世界は常に変化しています。自分が作っているプログラムが完璧であっても、データを送ってくる相手側のシステムが突然変更されることは珍しくありません。昨日までは正常に動いていたのに、今日になったら急に動かなくなったという現象は、こうしたデータの形式変化が原因であることが多々あります。
そのため、外部から取り入れるデータは最初から疑ってかかるという姿勢がとても大切になります。家に入ってくる前に必ず手を洗うのと同じように、プログラムの中にデータを取り込む前に必ずバリデーションを行う習慣をつけておきましょう。このひと手間を惜しまないことが、多くの利用者に安心して使ってもらえる頑丈なシステムを作り上げる鍵となります。基本を一つずつ積み重ねて、エラーに強い記述をマスターしていきましょう。