объединение резольверов для интерфейса и конкретных типов - PullRequest
0 голосов
/ 29 мая 2019

По какой-то причине мне трудно понять, как объединить преобразователи для интерфейса GraphQL и типа, который реализует указанный интерфейс.

Скажем, у меня есть следующая схема:

interface IPerson {
  id: ID!
  firstName: String!
  lastName: String!
}

type ClubMember implements IPerson {
  ...IPerson fields
  memberType: String!
  memberSince: DateTime!
}

type StaffMember implements IPerson {
  ...IPerson fields
  hireDate: DateTime!
  reportsTo: StaffMember
}

extend type Query {
  people(ids: [Int!]): [IPerson]
}

Полный запрос ClubMember со всеми полями, такими как:

query {
  people(ids: [123456,234567,345678]) {
    id
    firstName
    lastName
    ... on ClubMember {
      memberType
      memberSince
    }
  }
}

, даст ответ, подобный следующему:

[
  {
    "id": 123456,
    "firstName": "Member",
    "lastName": "McMemberface",
    "memberType": "VIP",
    "memberSince": "2019-05-28T16:05:55+00:00"
  },
  ...etc.
]

Я использовал makeExecutableSchema() изapollo-server с inheritResolversFromInterfaces: true, и я хочу иметь возможность использовать средства разрешения по умолчанию для каждого интерфейса / типа, имея классы моделей, поддерживающие IPerson, ClubMember и т. Д., Возвращающие объекты только с полями, относящимися к каждомутип, т. е. класс модели для IPerson выбирает только те поля, которые требуются для IPerson и т. д. То есть приведенный выше ответ будет выполнять 2 оператора SQL:

SELECT id, firstName, lastName FROM Contacts WHERE id IN(?);

и

SELECT contactId, memberType, memberSince FROM Members WHERE contactId IN(?);

Конечно, я мог бы получить все данные в одном операторе SQL, выполнив JOIN на уровне базы данных, но я действительно хочу иметь один (и только один) способ разрешения полей, требуемых IPerson, ипусть другие типы дополняют эти данныеУ них есть собственные распознаватели.

У меня такой вопрос, нужно ли мне самому "объединять" результирующие объекты в распознаватель для типа запроса people?Например,

const resolvers = {
  Query: {
    people: function( parent, args, context, info ) {
      let persons = context.models.Person.getByIds( args.ids );
      let members = context.models.Member.getByIds( args.ids );
      /*
      return an array of {...person, ...member} where person.id === member.id
      */
    }
  }
}

Или Аполлон справляется с этим для нас?Хочу ли я что-то вроде apollo-resolvers?Документы по объединениям и интерфейсам не очень полезны;У меня __resolveType на IPerson, но в документах не указано, как разрешаются поля для каждого конкретного типа.Есть ли лучший способ добиться этого с помощью Dataloader или другой подход?

Я думаю этот вопрос связан с моей проблемой, поскольку я не хочу получать данные дляконкретный тип, если запрос не запрашивает какие-либо поля этого типа через фрагмент.Есть также эта проблема на Github.

Большое спасибо!

Редактировать: __resolveType выглядит следующим образом:

{
  IPerson: {
    __resolveType: function ( parent, context, info ) {
      if ( parent.memberType ) {
        return 'ClubMember';
      }
      ...etc.
    }
  }
}

1 Ответ

0 голосов
/ 30 мая 2019

Эта проблема на самом деле не специфична для Apollo Server или даже для GraphQL - запросы к нескольким таблицам и получение единого набора результатов, особенно когда вы работаете с несколькими моделями данных, будут сложными.

Можно, конечно, запрашивать каждую таблицу отдельно и комбинировать результаты, но это не особенно эффективно.Я думаю, что на сегодняшний день самый простой способ справиться с подобным сценарием - создать представление в вашей базе данных, например:

CREATE VIEW people AS
    SELECT club_members.id           AS id,
           club_members.first_name   AS first_name,
           club_members.last_name    AS last_name,
           club_members.member_type  AS member_type,
           club_members.member_since AS member_since,
           null                      AS hire_date,
           null                      AS reports_to,
           'ClubMember'              AS __typename
    FROM club_members
    UNION
    SELECT staff_members.id         AS id,
           staff_members.first_name AS first_name,
           staff_members.last_name  AS last_name,
           null                     AS member_type,
           null                     AS member_since,
           staff_members.hire_date  AS hire_date,
           staff_members.reports_to AS reports_to
           'StaffMember'            AS __typename
    FROM staff_members;

Вы также можете просто использовать одну таблицу вместо этого, но представление позволяет вамхраните ваши данные в отдельных таблицах и запрашивайте их вместе.Теперь вы можете добавить модель данных для вашего представления и использовать ее для запроса всех «людей».

Примечание. Я добавил для удобства столбец __typename - вернув __typename, выможете не указывать собственную функцию __resolveType - GraphQL назначит для вас соответствующий тип во время выполнения.

...