Запускать вложенные преобразователи с помощью GraphQL - PullRequest
0 голосов
/ 16 января 2019

Я думаю, что мне не хватает чего-то очевидного в том, как работают распознаватели GraphQL. Это упрощенный пример моей схемы (Place, которая может иметь AdditionalInformation):

import { ApolloServer, gql } from 'apollo-server';

const typeDefs = gql`
  type Place {
    name: String!
    additionalInformation: AdditionalInformation
  }

  type AdditionalInformation {
    foo: String
  }

  type Query {
    places: [Place]
  }
`;

И связанные резольверы :

const resolvers = {
  Query: {
    places: () => {
        return [{name: 'Barcelona'}];
    }
  },
  AdditionalInformation: {
    foo: () => 'bar'
  }
};

const server = new ApolloServer({typeDefs, resolvers});

server.listen().then(({ url }) => {
  console.log(`API server ready at ${url}`);
});

Когда я выполняю основной запрос:

{
  places {
    name,
    additionalInformation {
      foo
    }
  }
}

Я всегда получаю null как additionalInformation:

{
  "data": {
    "places": [
      {
        "name": "Barcelona",
        "additionalInformation": null
      }
    ]
  }
}

Это мое первое приложение GraphQL, и я до сих пор не понимаю, почему распознаватель AdditionalInformation не выполняется автоматически. Есть ли какой-нибудь способ сообщить GraphQL, что он должен его запустить?

Я нашел этот обходной путь, но нахожу его немного хитрым:

Place: {
  additionalInformation: () => { return {}; }
}}

1 Ответ

0 голосов
/ 16 января 2019

Давайте на минутку предположим, что additionalInformation был Скаляр, а не Тип объекта:

type Place {
  name: String!
  additionalInformation: String
}

Значение, возвращаемое распознавателем places:

[{name: 'Barcelona'}]

Если бы вы сделали аналогичный запрос ...

query {
  places {
    name
    additionalInformation
  }
}

Что бы вы ожидали от additionalInformation? Это значение будет нулевым, поскольку у объекта Place, возвращенного распознавателем places, нет свойства additionalInformation.

Даже если мы сделаем additionalInformation типом объекта (например, AdditionalInformation), результат будет тем же - поле additionalInformation будет иметь нулевое значение. Это потому, что распознаватель по умолчанию (тот, который используется, когда вы не указываете функцию распознавателя для поля) просто ищет свойство с тем же именем, что и поле в родительском объекте. Если ему не удается найти это свойство, возвращается ноль.

Возможно, вы указали преобразователь для поля в AdditionalInformation (foo), но этот преобразователь никогда не запускается, потому что в этом нет необходимости - все поле additionalInformation является нулевым, поэтому все преобразователи для любых полей соответствующего типа пропускаются.

Чтобы понять, почему это желаемое поведение, представьте другую схему:

type Article {
  title: String!
  content: String!
  image: Image
}

type Image {
  url: String!
  copyright: String!
}

type Query {
  articles: [Article!]!
}

У нас есть база данных с таблицей articles и таблицей images в качестве слоя данных. Статья может иметь или не иметь изображение, связанное с ней. Мои резольверы могут выглядеть так:

const resolvers = {
  Query: {
    articles: () => db.getArticlesWithImages()
  }
  Image: {
    copyright: (image) => `©${image.year} ${image.author}`
  }
}

Допустим, наш звонок getArticlesWithImages преобразуется в одну статью без изображения:

[{title: 'Foo', содержание: 'All about foos'}]

Как потребитель API, я прошу:

query {
  articles {
    title
    content
    image
  }
}

Поле image является необязательным. Если я возвращаю объект статьи с нулевым полем image, я понимаю, что в БД не было никакого связанного изображения. Как клиент переднего плана я знаю, что не нужно отображать изображение.

Что произойдет, если GraphQL вернет значение для image независимо? Очевидно, наш распознаватель сломался бы, так как ему не передавалось бы какое-либо родительское значение. Более того, однако, как потребитель API, мне теперь нужно было бы проанализировать содержимое image и каким-то образом определить, действительно ли изображение было связано со статьей, и я должен что-то с этим сделать.

TLDR;

Как вы уже предложили, решение здесь состоит в том, чтобы указать преобразователь для additionalInfo. Вы также можете просто вернуть это значение в своем places резолвере, т.е.

return [{name: 'Barcelona', additionalInfo: {}}]

В действительности, если форма вашей схемы совпадает с формой вашего нижележащего слоя данных, вряд ли вы столкнетесь с такой проблемой при работе с реальными данными.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...