【TypeScript】型安全なAPIレスポンスの実装
投稿日: 2025年01月17日
TypeScriptについて前回は推論について書きましたが、今回はAPIレスポンスに特化して型を味方にして型安全を担保する方法について書いてみます。
このあたりが理解できて使えるようになると、エンドポイントを作成するときにまず型を書くことが習慣化してきて、めんどくさいではなく自分のために書くようになります。
↓前回のTypeScriptの記事
既存のエンドポイントを元に「この機能を追加したいからレスポンスにこのデータも含めよう!」ってなった時に、型をしっかり指定している時の楽さと、指定していない時のリスクについて取り上げます。
というのも、最近開発しているアプリでまさにこのパターンありまして、「ほんとTypeScript最高!JavaScriptもう怖くて書けないかも」って改めて感じたのです笑
import { Homework, LongVacation } from "@prisma/client";
export type Data = {
child: {
id: string;
name: string;
};
homeworks: Homework[];
longVacation: LongVacation | null;
};
export type DashboardResponse = { data: Data[] };
基本的なことになりますが、まずは型の指定についてです。
バックエンドでは、`NextResponse.json`に型引数を渡して、レスポンスの型を明示的に指定します。
return NextResponse.json<DashboardResponse>(
{
data,
},
{ status: 200 }
);
フロントエンドでは、`fetch`で取得したデータを`json`に変換する際に型を指定します。
const data:DashboardResponse = await resp.json();
ちなみに・・
ちょくちょく、下記のようにされている方を見かけます。
const data = await resp.json();
const {data}:DashboardResponse = data;
こうすると、dataがany型になってしまうため、型安全性が低下します。await resp.json();
の結果がどのような型であるかが明示されないため、その後の処理で予期しないエラーが発生する可能性があると思います。await resp.json();
の時点で型の指定をした方が良いと思います。
このレスポンスの型をバックエンドとフロントエンドで共通のものを使うというのが重要です!別で定義してはダメとは言わないけどダメです!!w
このように型を指定していることを前提に、レスポンスにデータを追加したいなぁとなった時が今回のお話です。
import { Homework, LongVacation } from "@prisma/client";
export type Data = {
child: {
id: string;
name: string;
};
progress: number; //追加
homeworks: Homework[];
longVacation: LongVacation | null;
};
export type DashboardResponse = { data: Data[] };
その時、エディターでバックエンドのファイルがエラーを吐きます!!
レスポンスに含めないといけない追加したプロパティがないよって教えてくれます🤩
赤いから絶対修正しますよね!!
親切にエラー吐いてくれるのでTypeScript最高です。
これ、TypeScriptで書いていたとしてもレスポンスの型を指定をしていないとなんでもreturnできてしまうためにエラー吐いてくれないので、レスポンスに含めないままフロント側で存在するものとして処理を書いたらundefindeになって処理できずにエラー吐くみたいなことが起こる可能性があります。(他の箇所で一部型使ってるとそっちでエラー吐くことはあります)
この例のパターンはフロントエンド側は赤くならない(元々あるものとして処理を書いていないので)ですが、命名の変更とかですでに存在しているプロパティの型に変更を加えるとバックエンドもフロントエンドも赤くなります。
やってみます。
フック使ってデータ取得しているのでちょっとわかりにくいです💦
元がこれです。(左:バックエンド、中:型、右:フロントエンド)
型のプロパティ名を変える
import { Homework, LongVacation } from "@prisma/client";
export type Data = {
child: {
id: string;
name: string;
};
progres: number; //progress→progresにしてみる
homeworks: Homework[];
longVacation: LongVacation | null;
};
export type DashboardResponse = { data: Data[] };
その結果・・・
吐きました!!!!
しかもエラーメッセージ親切・・!
型指定していないと、配列処理する際にエラー吐いてるのも結構見かける気がします。
mapの引数の値の型がanyになるというエラーでるパターン多い気がします。
推論されるところは基本的に指定する必要ありませんが、指定しないとanyになるとかunknownになるところはどんどん指定して楽しましょう。
エラー吐いてくれて安全に書けるということもありますが、推論も働くのでタイポしてundefindeということもなくなります。
型指定をすることで、開発中のエラーを早期に発見でき、結果として開発効率が向上します。
慣れたり良さが理解できるまでは手間だよと思うかもしれませんが、慣れたら絶対型は味方になるので慣れるまで耐えて書きましょうw
既存のコード使ったため、解説用にコード書かなかったので微妙カモしれないですが便利さ伝わっていたらいいな・・と思います。
どういうこと??ってなったらDMくださいw