TypeScriptとGraphQLで実現する型安全なAPI実装
Kazuhito Hokamura 様(@hokaccha)
ユビー株式会社 / ソフトウェアエンジニア
内容メモ
今回の内容はzennにもまとめられている
Kotlin -> node, tsに書き換えなどを行っている
なぜAPIに型をつけたいのか
const response = await fetch("hogehoge") const user:User = await response.json()
これのuser:User
は祈り。これが通らないと、ぬるぽになる
→ 外部I/Oに対する片付けが不十分だと静的型付けの恩恵を十分に活かせない
ここを起因にして発生するエラーは多い
APIに型をつけるとは?
→ OpenAPI, gRPC, GraphQLなどを用いることで型をつけることができる。
gRPCはツールチェーンで実装まで型をつけてくれる機能がある。GraphQLだと、実装に型をつけるのはオプショナルになっている。
GraphQL Codegen
デファクトになっているコードジェネレーターツール
クライアントの便利機能
Client Presetというクライアント向けのセットがある
重要なポイントとしては、スキーマから型を生成するのではなく、クエリから型を生成する。
GraphQLはクライアントは必要なフィールドを指定できるという機能があるが、スキーマから型を生成すると、すべてのフィールドを含んだ型を生成してしまうので、GraphQLの柔軟性が活かせない
GraphQL CodeGenは、クエリから型を生成することができる!
(だからクライアント側とサーバー側でツールが異なるのか!)
Fragment Colocation
上で取得したデータをコンポーネント内の下層のコンポーネントで情報を使うというような流れ
→ しかし、下層のコンポーネントで、あるフィールドを使わなくなった時に、それに気づけない。どこかで使ってるかもしれないし、安易に消せない。サーバー側から本当は必要ではない情報があるのにいつまでも取得してしまう。
「すべての子が必要とするデータをトップレベルのクエリが知っている必要がある」という設計の歪み
-> Fragment Colocationを用いることで解決できる。
自分に必要な情報だけを定義しておいて、自分と子だけを管理するようにすることで、上のような状況を避けることができる。
→ これをGraphQL CodeGenが対応している!!
Fragment Masking
あるコンポーネントでしか仕様していないフィールドが、他のコンポーネントでも使用しようとしてしまう。大元で情報を取ってきているので、取れてしまうが、Fragment Maskingを用いることによって型エラーを出すことができる。
しかし、型が複雑になるというデメリットもある
サーバーサイド
GraphQL Codegen TypeScript Resolvers
graphql-js, Apollo Server, GraphQL Yogaなどのメジャーなライブラリに対応
NestJSだけちょっと特殊。
サーバーはスキーマから型を生成すれば良い。
例えばスキーマ内の取得しないフィールドがある場合、その取得無駄になることがある。
その時に、フィールドごとにリゾルバを定義することによって、遅延実行をすることができる。
しかし素直にすると、型エラーになってしまうが、打開策もある。
感想
GraphQLを用いて開発していたが、Fragment Colocation(クライアントサイド)や、フィールドごとにリゾルバを設定(サーバーサイド)などについて知らなかった。
今まで課題に感じていた部分の解決になりうる概念だったので、知ることができてとてもよかった。
また、TypeScriptとGraphQLでの web API 開発の親和性の高さを知ることができた。