Apollo boost - __typename в запросе предотвращает новую мутацию

У меня есть проблема в моем проекте метеор / реагировать / Аполлон (с наддувом). Когда я запрашиваю данные с сервера, он добавляет __typename к каждому объекту и подобъекту в моем запросе, но в моем случае это создает серьезную проблему, так как я обычно повторно использую эти данные для отправки их в другую мутацию. Теперь другая мутация говорит мне, что произошла ошибка, потому что поле __typename не определено в моей схеме graphql.

Я попытался исправить это, добавив поле addTypename: false в мой клиент apollo, но оно ничего не изменило (обратите внимание, что я использую apollo boost, возможно, поэтому оно не работает):

const client = new ApolloClient({
    uri: Meteor.absoluteUrl('graphql'),
    addTypename: false,
    request: operation =>
        operation.setContext(() => ({
            headers: {
                authorization: Accounts._storedLoginToken()
            }
        }))
})

Также кажется, что даже если это сработало, оно не очень оптимизировано. Мне кажется очень проблематичным, что в результаты запроса добавляется поле, и я удивлен, что не нашел в Интернете какого-либо четкого решения. Некоторое предлагаемое решение, где:

  • фильтр вручную на стороне клиента
  • добавить промежуточное ПО в apollo
  • добавить поле __typename ко всем моим схемам...

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

1 ответ

Решение

Даже если использовать apollo-client и не apollo-boostВы не должны устанавливать addTypename ложно, если у вас нет веских причин для этого. __typename поле используется InMemoryCache чтобы нормализовать результаты запроса, поэтому его пропуск может привести к неожиданному поведению при кэшировании.

К сожалению, в этой проблеме нет "серебряной пули". Запрос запроса и затем использование данных этого запроса в качестве переменной для другого запроса может быть истолковано как неправильное использование API. Type возвращается запросом и Input Type в качестве аргумента используются совершенно разные вещи, даже если в качестве объектов Javascript они используют одно или несколько полей. Точно так же, как вы не можете использовать типы и типы ввода взаимозаменяемо в схеме, не следует ожидать, что они могут использоваться взаимозаменяемо на стороне клиента.

Это также означает, что если вы оказались в такой ситуации, вы можете еще раз взглянуть на свой дизайн схемы. В конце концов, если данные уже существуют на сервере, должно быть достаточно передать их идентификатор и получить их на стороне сервера, а не передавать весь объект целиком.

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

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

function stripTypenames (value) {
    if (Array.isArray(value)) {
        return value.map(stripTypenames)
    } else if (value !== null && typeof(value) === "object") {
      const newObject = {}
      for (const property in value) {
          if (property !== '__typename') {
            newObject[property] = stripTypenames(value[property])
          }
      }
      return newObject
    } else {
      return value
    }
}

Вы не должны удалять __typename. Они предназначены для кеша, а также необходимы для типов объединения. Наверное стоит дождаться обновления от apollo. К сожалению, у меня тоже возникла проблема, и я не смог найти подходящего решения. Сначала я просто удалял все имена типов, но теперь у меня проблема с типами объединения.

Отсюда"__typename" можно удалить с помощью следующей вспомогательной функции

const cleanedObject = omitDeep(myObject, "__typename")

const omitDeep = (obj, key) => {
    const keys = Object.keys(obj);
    const newObj = {};
    keys.forEach((i) => {
      if (i !== key) {
        const val = obj[i];
        if (val instanceof Date) newObj[i] = val;
        else if (Array.isArray(val)) newObj[i] = omitDeepArrayWalk(val, key);
        else if (typeof val === 'object' && val !== null) newObj[i] = omitDeep(val, key);
        else newObj[i] = val;
      }
    });
    return newObj;
  };

const omitDeepArrayWalk = (arr, key) => {
  return arr.map((val) => {
    if (Array.isArray(val)) return omitDeepArrayWalk(val, key)
    else if (typeof val === 'object') return omitDeep(val, key)
    return val
  })
}
Другие вопросы по тегам