GraphQL学習メモ
Apolloのドキュメントがすごく良い。
スキーマはクライアントに露出するインターフェースの定義になるので、当たり前といえば当たり前だが、スキーマはデータストアの都合じゃなくクライアントがどうデータを使うのかをもとに設計すべし。
Design your schema based on how data is used, not based on how it's stored.
あとからフィールドを足すほうが、削除するより簡単で安全。
It's easier and safer to add a new field to a schema than it is to remove an existing field that some of your clients are using.
Mutationのレスポンスボディには変更されたデータを入れて返すことが推奨される。Apolloドキュメントより。
In GraphQL, it's recommended for every mutation's response to include the data that the mutation modified. This enables clients to obtain the latest persisted data without needing to send a followup query.
リクエスト回数の削減のためにこういう工夫もあるのか。
Mutationのレスポンスの型は下記のようなインターフェースを実装するようにするとよい。
interface MutationResponse {
code: String!
success: Boolean!
message: String!
}
クエリで利用される型とは別にこのようなインターフェースを実装したレスポンスの型を書くようにすると、たとえば複数の型のデータを更新するようなMutationのレスポンスの型の定義もすんなりできる。下記は User
と Post
を同時に更新するMutationのレスポンスの型の例。
type LikePostMutationResponse implements MutationResponse {
code: String!
success: Boolean!
message: String!
post: Post
user: User
}
graphqlパッケージの GraphQLScalarType
クラスのインスタンスを生成することで独自のスカラ型を定義することができる。その際に下記のような、定義しようとしているスカラ型をサーバが扱うためのロジックを定義する。
- JSON互換の型 → バックエンドで扱う型の変換ロジック (
parseValue
) - バックエンドで扱う型 → JSON互換の型の変換ロジック (
serialize
) - クエリに含まれるリテラルをバックエンドで扱う型の値としてパースするロジック (
parseLiteral
)
Apolloドキュメントの例:
const { GraphQLScalarType, Kind } = require('graphql');
const dateScalar = new GraphQLScalarType({
name: 'Date',
description: 'Date custom scalar type',
serialize(value) {
return value.getTime(); // Convert outgoing Date to integer for JSON
},
parseValue(value) {
return new Date(value); // Convert incoming integer to Date
},
parseLiteral(ast) {
if (ast.kind === Kind.INT) {
return new Date(parseInt(ast.value, 10)); // Convert hard-coded AST string to integer and then to Date
}
return null; // Invalid hard-coded value (not an integer)
},
});
スキーマ定義ではスキーマの一部やオペレーションを修飾するDirectiveを利用することができる。Directiveは対象のスキーマ定義やオペレーションの付加的な情報を表現するのに利用する。GraphQLの仕様で定義されているDefault Directivesは @deprecated
@skip
@include
の3つ。