Подтвердить что ты не робот

Пример реального мира GraphQLInterfaceType и GraphQLUnionType

Мне сложно понять, когда использовать GraphQLInterfaceType и GraphQLUnionType.

У меня есть RTFM:

Может ли кто-нибудь предложить пример реального мира, когда это было бы полезно, чтобы получить его через мою толстую голову?

4b9b3361

Ответ 1

Оба предназначены для разработки схемы с гетерогенным набором типов, и вы можете добиться одинаковой функциональности с помощью обоих, но 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, потому что мы предпочитаем использовать только один тип блога и два типа методов проверки подлинности:

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, в которой также используется электронная почта и пароль. Вам нужно будет включить другое поле, чтобы можно было отличить его от рамки B блога в нашей функции resolveBlogType. Добавьте поле type. В нашем примере Союза, поскольку у нас есть только доступ к полям в Союзе, вы должны добавить type в Союз. Если в будущем мы хотели бы добавить еще один Union с одинаковыми полями для нескольких фреймворков, нам нужно будет также добавить поле type. Не так приятно иметь type дублировать несколько раз в нашей схеме. Лучше было бы использовать интерфейс и иметь одно поле type, доступное в функции resolveBlogType всеми объектами, использующими интерфейс.

Ответ 2

Семантика GraphQLInterfaceType похожа на interface большинства языков программирования. и GraphQL добавить еще несколько вариантов поведения для него. Например, проверьте, реализует ли производный класс все поля, динамическое преобразование в производный экземпляр.
Семантика GraphQLUnionType - это не Union, а что-то вроде OR. (Немного похоже на проверку типа потока?)
Примером из реальной жизни является пример в дизайне Relay => Relay Node.
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
}

Если вас это смущает, вам нужно почитать какую-нибудь книгу с языком сильных типов (например, С# или Java или что-то еще. Может быть, вам стоит взглянуть и на Flow, такое использование Dog|Cat является ограничением типов)