web-dev-qa-db-ja.com

GraphQLInterfaceTypeとGraphQLUnionTypeの実際の例

GraphQLInterfaceTypeGraphQLUnionTypeをいつ使用するかを理解するのに苦労しています。

私はRTFMを持っています:

これらが私の厚い頭を通り抜けるのに役立つとき、誰かが実際の例を提供できますか?

15
Matt K

どちらも、異種のタイプセットを使用してスキーマを設計するのに役立つことを目的としており、両方を使用して同じ機能を実現できますが、タイプが基本的に同じで一部のフィールドが異なる場合は、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フィールドにアクセスできるようにすることをお勧めします。

27
Jesús Carrera

GraphQLInterfaceTypeのセマティックは、ほとんどのプログラム言語のinterfaceに似ています。そしてgraphqlはそれにいくつかの振る舞いを追加します。派生クラスがすべてのフィールドを実装しているかどうかを確認するように、派生インスタンスに動的に解決します。
GraphQLUnionTypeのセマティックはUnionではなく、ORのようなものです(フロータイプのタイプチェックに少し似ていますか?)
実際の例は Relayの例 => RelayのNode design。
GraphQLInterfaceTypeGraphQLUnionTypeとはまったく関係ありません。
多分あなたはこれに混乱したと思いますか?

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はタイプ制限です)

2
chen x