Я хотел бы уточнить, какой подход я должен использовать для своих функций распознавателя в Apollo + GraphQL.

Предположим следующую схему:

type Post {
  id: Int
  text: String
  upVotes: Int
}

type Author{
  name: String
  posts: [Post]
}

schema {
  query: Author
}

учебник ApoloGraphql предлагает такую ​​карту преобразователя:

{Query:{
    author(_, args) {
      return author.findAll()
          }
    }
},
Author {
   posts: (author) => author.getPosts(),
}

Насколько я знаю, каждая логика относительно сообщений, например. get author with posts where the count of post upVotes > args.upVotes, должны обрабатываться в методе author. Это дает нам следующую карту резолвера:

{Query:{
    author(_, args) {
      return author.findAll({
              include:[model: Post]
              where: {//post upVotes > args.upVotes}
                })
             }
},
Author {
   posts: (author) => author.getPosts(),
}

Вызов author сначала выберет автора сообщений в одном объединенном запросе, где сообщения больше, чем args.upVotes. Затем он снова выберет сообщения для этого автора в дополнительном запросе из-за Author ... getPosts()

Технически я могу достичь того же результата, удалив Author, поскольку сообщения уже включены в небольшой метод author.

У меня следующие вопросы:

  1. Мне нужно это заявление? В каких случаях?

    Author { posts: (author) => author.getPosts(), }

  2. Если нет, то как я могу узнать, было ли запрошено поле сообщений, чтобы Могу включить посты условно, в зависимости не только от аргументы, но и в запрошенных полях?

  3. Если да, то какие сообщения будут содержать окончательный результат? Сообщения из оператор include или getPosts ()?

1
henk 24 Ноя 2017 в 03:21

1 ответ

Лучший ответ

Карта преобразователя, которую вы включили в свой вопрос, недействительна. Я предполагаю, что вы имели в виду что-то вроде этого для типа Author:

Author {
  posts: (author) => author.getPosts(),
}

Пока ваш запрос author всегда разрешается в массив объектов, включающих свойство posts, вы правы, думая, что нет смысла включать распознаватель клиентов для поля posts типа Author. В этом случае преобразователь вашего запроса уже заполняет все необходимые поля, и нам больше ничего делать не нужно.

GraphQL использует преобразователь по умолчанию, который ищет свойства родительского (или корневого) объекта, переданного преобразователю, и использует их, если они соответствуют имени разрешаемого поля. Таким образом, если GraphQL разрешает поле posts и нет преобразователя для posts, по умолчанию он смотрит на объект Author, с которым имеет дело, и, если в нем есть свойство, имя posts, оно преобразует поле в его значение.

Когда мы предоставляем настраиваемый преобразователь, он переопределяет поведение по умолчанию. Итак, если ваш преобразователь был, например:

posts: () => []

Тогда GraphQL всегда будет возвращать пустой набор сообщений, даже если объекты, возвращенные author.findAll(), включают сообщения.

Итак, когда вам нужно включить преобразователь для posts?

Если ваш преобразователь author не "включил" сообщения, но клиент запросил это поле. Как вы сказали, проблема в том, что в некоторых случаях мы потенциально делаем ненужный дополнительный вызов, в зависимости от того, "включает ли ваш преобразователь author" сообщения или нет. Вы можете обойти это, сделав что-то вроде этого:

posts: (author) => {
  if (author.posts) return author.posts
  return author.getPosts()
}
// or more succinctly
posts: author => author.posts ? author.posts : author.getPosts()

Таким образом, мы вызываем getPosts только в том случае, если нам действительно нужно получить сообщения. В качестве альтернативы вы можете опустить преобразователь posts и обработать его внутри вашего преобразователя author. Мы можем посмотреть на четвертый аргумент, переданный преобразователю, чтобы получить информацию о запросе, в том числе о том, какие поля были запрошены. Например, ваш преобразователь может выглядеть примерно так:

author: (root, args, context, info) => {
  const include = []
  const requestedPosts = info.fieldNodes[0].selectionSet.selections.includes(s => s.name.value === 'posts'
  if (requestedPosts) include.push(Post)
  return Author.findAll({include})
}

Теперь ваш преобразователь будет включать сообщения для каждого автора, только если клиент специально запросил это. Объект дерева AST, предоставленный преобразователю, неудобен для синтаксического анализа, но есть библиотеки (например, этот), чтобы помочь с этим.

1
Daniel Rearden 24 Ноя 2017 в 17:33