この記事は、Supershipグループ Advent Calendar 2021の14日目の記事になります。
はじめに
The Int scalar type represents a signed 32‐bit numeric non‐fractional value. Response formats that support a 32‐bit integer or a number type should use that type to represent this scalar.
GraphQLのScalerのIntは32bitと定義されてあり、いまどきにしては扱える数値の範囲が狭い理由が何故なのかが気になり、調べた話です。
graphql-spec
It wasn't included because it's not available across all platforms, most importantly and practically in our case: JavaScript.
graphql-specの過去のissueによると、すべてのプラットフォーム(JavaScript)で利用できないからというコメントがありました。
JavaScriptのNumber
検証環境はChromeの96.0.4664.93です。
JavaScriptのNumberは安全に扱える整数の上限のNumber.MAX_SAFE_INTEGERが定義されてます。
JavaScriptのNumberはIEEE754なのでその辺の精度の話になります。
Number.MAX_SAFE_INTEGER // 9007199254740991
安全に扱えるってどうゆうことなのか
Number.MAX_SAFE_INTEGERより大きい値を使って比較した例です
9007199254740992 === 9007199254740993 // true
この場合は数値が違うのでfalseになるのが正しいようにみえますが、丸められてしまってtrueになってしまいます。
GraphQL APIがJSONでNumber.MAX_SAFE_INTEGERより大きい値を返した来た場合どうなるのか
バックエンドはGraphQLでフロントエンドがReactやVueなどのJavaScriptのフレームワークでWebアプリケーションを作ってる場合、 Apollo ClientのようなGraphQLクライアントでAPIから値を取得してから使うことになりますが、その場合はHTTPのレスポンスボディで返るJSONの文字列をJavaScriptのオブジェクトに変換するのにJSON.parseする必要が出ます。
JSON.parse(`{"value": 90071992547409918712}`) // {value: 90071992547409920000}
Number.MAX_SAFE_INTEGERより大きい値が入ってる状態でJSON.parseすると90071992547409918712
が90071992547409920000
になってしまい数値が変わってしまいました。
このJavaScriptオブジェクトを使ってしまうと、APIが返してる数値と違う数値を使ってしまうことが起こるので気をつける必要があります。
まとめ
Number.MAX_SAFE_INTEGERを超えた場合の整数を正しく扱う場合は以下のことを気をつける必要がありそうです。
- 該当するGraphQLのフィールドのScalerをIntからStringに変更する
- JavaScriptで比較や演算する場合はBigIntを使う
最後に
Supershipではプロダクト開発やサービス開発に関わる人を絶賛募集しております。
ご興味がある方は以下リンクよりご確認ください。
Supershipグループ 採用サイト
是非ともよろしくお願いします。