У меня проблема в моем проекте meteor / response / apollo (с ускорением). Когда я запрашиваю данные с сервера, он добавляет __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-client, а не apollo-boost, вы не должны устанавливать для addTypename значение false, если у вас нет веских причин для этого. Поле __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, состоит в том, чтобы включить нормализацию кеша. Ознакомьтесь с разделом нормализации в (docs) [apollographql.com/docs/react/advanced/…. Если все ваши запросы были network-only или вы хотели реализовать свою собственную функцию dataIdFromObject, то вы могли бы обойтись установкой addTypename в false.
Я не говорю, что это правильно или неправильно, но Apollo делает определенные предположения о том, как будет использоваться API на основе соглашения. Это отражается в том, как компоненты Query и Mutation функционируют по-разному - хотя и запрос, и мутация могут делать одно и то же, существует предположение, что запросы будут извлекать данные, а мутации будут изменять данные. Точно так же, я считаю, есть предположение, что данные запроса не будут использоваться в качестве входных для другого запроса, поскольку это было бы нетрадиционным, даже если это технически возможно в некоторых случаях.
Если кому-то интересно, как я это решил: я использовал функцию пропуска из lodash для каждого объекта (и подобъекта), чтобы удалить ключ __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
})
}
Как использовать этот код с клиентом apollo и должны ли мы включить поле с именем addTypeName как истинное
Вы не должны удалять __typename. Они предназначены для кеша, а также необходимы для типов объединения. Наверное стоит дождаться обновления от apollo. К сожалению, у меня тоже возникла проблема, и я не смог найти подходящего решения. Сначала я просто удалял все имена типов, но теперь у меня проблема с типами объединения.
Дэниел, еще раз спасибо за быстрый и четкий ответ. Я действительно повторно использую данные в состоянии компонента, изменяя их, если необходимо, а затем отправляю их обратно, поэтому я должен фильтровать их, как вы упомянули, поэтому я думаю, что решение - правильное решение. Однако я действительно сомневаюсь в graphQL / Apollo в том, как решается эта проблема. Для API, который должен идти прямо к делу и предоставлять только запрошенные данные, добавление поля к каждому отдельному объекту и подобъекту кажется излишним и противоречит сути. И многие запросы о выпуске github, похоже, повторяют это мнение.