GraphQLInterfaceType
とGraphQLUnionType
をいつ使用するかを理解するのに苦労しています。
私はRTFMを持っています:
これらが私の厚い頭を通り抜けるのに役立つとき、誰かが実際の例を提供できますか?
どちらも、異種のタイプセットを使用してスキーマを設計するのに役立つことを目的としており、両方を使用して同じ機能を実現できますが、タイプが基本的に同じで一部のフィールドが異なる場合は、GraphQLInterfaceType
の方が適しています、およびGraphQLUnionType
は、タイプが完全に異なり、フィールドが完全に異なる場合。
最終的には、スキーマの設計に応じてどちらを使用するか。
実際の例として、ブログのリストがあるとしますが、フレームワークAを使用するブログは認証としてユーザー名とパスワードを使用し、フレームワークBを使用するブログは電子メールとパスワードを使用します。次のようにGraphQLInterfaceType
を使用して設計します。
const BlogType = new GraphQLInterfaceType({
name: 'Blog',
fields: {
url: { type: new GraphQLNonNull(GraphQLString) }
password: { type: new GraphQLNonNull(GraphQLString) }
},
resolveType: resolveBlogType
});
const BlogAType = new GraphQLObjectType({
name: 'BlogA',
interfaces: [Blog],
fields: {
url: { type: new GraphQLNonNull(GraphQLString) }
username: { type: new GraphQLNonNull(GraphQLString) },
password: { type: new GraphQLNonNull(GraphQLString) }
}
});
const BlogBType = new GraphQLObjectType({
name: 'BlogB',
interfaces: [Blog],
fields: {
url: { type: new GraphQLNonNull(GraphQLString) }
email: { type: new GraphQLNonNull(GraphQLString) },
password: { type: new GraphQLNonNull(GraphQLString) }
}
});
function resolveBlogType(value) {
return value.username ? BlogAType : BlogBType;
}
username
を送信する新しいブログを作成すると、BlogA
が作成されます。
次のようにクエリできます。
query MyQuery {
blogs: {
url
password
... on BlogA {
email
}
... on BlogB {
username
}
}
}
同じ機能を取得しましょう。ただし、GraphQLUnionType
を使用します。これは、1種類のブログと2種類の認証方法を使用することを好むためです。
const AuthAType = new GraphQLObjectType({
name: 'AuthA',
fields: {
username: { type: new GraphQLNonNull(GraphQLString) },
password: { type: new GraphQLNonNull(GraphQLString) }
}
});
const AuthBType = new GraphQLObjectType({
name: 'AuthB',
fields: {
email: { type: new GraphQLNonNull(GraphQLString) },
password: { type: new GraphQLNonNull(GraphQLString) }
}
});
const AuthType = new GraphQLUnionType({
name: 'Auth',
types: [AuthAType, AuthBType]
resolveType: resolveAuthType
});
const BlogType = new GraphQLInterfaceType({
name: 'Blog',
fields: {
url: { type: new GraphQLNonNull(GraphQLString) }
auth: { type: AuthType }
},
});
function resolveAuthType(value) {
return value.username ? AuthAType : AuthBType;
}
次のようにクエリできます。
query MyQuery {
blogs: {
url
auth {
... on AuthA {
username
password
}
... on AuthB {
email
password
}
}
}
}
この例でわかるように、インターフェイスまたはユニオンで同じことを実現しますが、スキーマの設計によってはどちらかがより適切な場合があります。
たとえば、電子メールとパスワードも使用するブログフレームワークCを追加するとします。 resolveBlogType
関数でブログフレームワークBと区別できるようにするには、別のフィールドを含める必要があります。 type
フィールドを追加しましょう。ユニオンの例では、ユニオン内のフィールドにしかアクセスできないため、type
をユニオンに追加します。将来、複数のフレームワークに同じフィールドを持つ別のユニオンを追加したい場合は、そこにもtype
フィールドを追加する必要があります。スキーマでtype
を複数回複製するのはそれほど良いことではありません。インターフェイスを使用し、インターフェイスを使用するすべてのオブジェクトがtype
関数で単一のresolveBlogType
フィールドにアクセスできるようにすることをお勧めします。
GraphQLInterfaceType
のセマティックは、ほとんどのプログラム言語のinterface
に似ています。そしてgraphqlはそれにいくつかの振る舞いを追加します。派生クラスがすべてのフィールドを実装しているかどうかを確認するように、派生インスタンスに動的に解決します。GraphQLUnionType
のセマティックはUnion
ではなく、OR
のようなものです(フロータイプのタイプチェックに少し似ていますか?)
実際の例は Relayの例 => RelayのNode design。GraphQLInterfaceType
はGraphQLUnionType
とはまったく関係ありません。
多分あなたはこれに混乱したと思いますか?
interface Node{
id: string
}
type Dog implements Node{
id: string
}
type Cat implements Node{
id: string
}
union Animal = Dog | Cat
type User{
node: Node
animal: Animal
}
これに混乱した場合は、強い型の言語の本を読んでもらう必要があります(C#やJavaなど)。Flowも見てください。この使用法Dog|Cat
はタイプ制限です)