Next.js + サーバーサイドTypeScript + 関数フレーバーでクリーンなアプリを作ったので実装意図とか書く Advent Calendar 2022
の24日目。株式会社mofmofに生息しているshwldです。
前日はNext.jsとgraphile-workerをRailwayにデプロイするについて書きました
GraphQLのUnionやInputType
Either型の記事で書きましたが、mutationの戻り値は、Unionになっています。
例えば、create account
のmutationはこんな感じ
export type CreateAccountMutationResult =
| CreateAccountSuccessResult
| InternalErrorResult
| InvalidArgumentsResult
| UnauthorizedResult;
GraphQL的には
union CreateAccountMutationResult =
CreateAccountSuccessResult
| InvalidArgumentsResult
| UnauthorizedResult
| InternalErrorResult
こう。
フロントエンドでこのmutationを叩く際には
mutation AccountCreateButton_CreateAccount($input: CreateAccountInput!) {
createAccount(input: $input) {
... on CreateAccountSuccessResult {
result {
...AccountList_Result
}
}
}
}
こんな感じになります。
Reactで、エラー処理をする際には正常に終了した結果を取得し型を ts-patternなどで分岐して処理する必要があります。
エラー処理どうするか問題
GraphQLでエラーを扱う際にどう扱うか問題があります。
GraphQLにはmutationやqueryの結果にerrorsというfieldが生えているので、そこを拡張して返してあげるという手もあります。
しかし今回はそれを採用しませんでした。
理由としてはエラーの型付けです。errorsの拡張は型をつけられず、フロントから扱いづらいです。
unionで返しておくことでどんな結果になるのかを型レベルで知ることができ、よりTypeScriptフレンドリーと言えます。
多少型をチェックするのが手間にはなりますが上記メリットは大きいです。
また、サーバーサイドの実装自体も、どんなエラーが発生するかを型レベルで知ることができる作りにしているため、フロントからもその恩恵を受けられ、一貫性があります。
また、本当?の例外(事前にパターン定義されていないエラー)はerrors fieldの拡張を使うという設計方針もありますが、サーバーサイドの実装自体が例外を発生させない作りになっているため、今回は errors fieldはmutationからは全く利用していません(GraphQlの基盤自体が起こすエラーではerrors fieldを使っています)
Input type
mutationのargumentはinput
という名前に統一しています。
input CreateAccountInput {
id: ID!
name: String!
}
type Mutation {
createAccount(input: CreateAccountInput!): CreateAccountMutationResult!
}
理由は単純にargumentsの変更に強いからです。
このルールを浸透させるため、PLOPを使って.graphqlファイルのテンプレートも生成しています。
次回予告
明日は、総まとめします。