Apollo boost - __typename 在查询中防止新突变
Apollo boost - __typename in query prevent new mutation
我的 meteor/react/apollo(使用 boost)项目有问题。当我从服务器查询数据时,它会将 __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 字段添加到我的所有模式中...
但其中 none 似乎适合 'simplcity' apollo 应该带来的查询。我希望有一个更简单、更合乎逻辑的解决方案,但到目前为止,找不到任何解决方案。
即使使用 apollo-client
而不是 apollo-boost
,您也不应将 addTypename
设置为 false,除非您有令人信服的理由这样做。 __typename
字段被 InMemoryCache
用来标准化您的查询结果,因此省略它可能会导致围绕缓存的意外行为。
很遗憾,没有"silver bullet"这个问题。请求查询然后将该查询的数据用作其他查询的变量可能被解释为滥用 API。查询返回的 Type
和用作参数的 Input Type
是完全不同的东西,即使作为 Javascript 对象它们共享一个或多个字段。就像您不能在模式中互换使用类型和输入类型一样,也不应期望它们可以在客户端互换使用。
这也意味着,如果您发现自己处于这种情况,您可能需要重新审视您的架构设计。毕竟,如果数据已经存在于服务器上,为其传递一个 id 并在服务器端检索它就足够了,而不必传递整个对象。
如果您正在使用一些查询来填充一个或多个输入,然后在突变中使用这些输入的值,那么您可能已经将初始查询数据转换为组件状态,然后在您的突变。在这种情况下,__typename
或任何其他不可编辑的字段可能首先不应作为组件状态的一部分包含在内。
归根结底,进行此类操作有望成为例外,而不是常规。我会为 "sanitize" 你的输入创建某种辅助函数并继续。
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
}
}
来自
here。 '__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
})
}
您不应该删除 __typename。它们用于缓存,也用于联合类型。可能应该等待 apollo 的更新。
不幸的是我现在也有问题,我找不到合适的解决方案。首先我曾经简单地删除所有类型名称,但现在我对联合类型有问题。
我的 meteor/react/apollo(使用 boost)项目有问题。当我从服务器查询数据时,它会将 __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 字段添加到我的所有模式中...
但其中 none 似乎适合 'simplcity' apollo 应该带来的查询。我希望有一个更简单、更合乎逻辑的解决方案,但到目前为止,找不到任何解决方案。
即使使用 apollo-client
而不是 apollo-boost
,您也不应将 addTypename
设置为 false,除非您有令人信服的理由这样做。 __typename
字段被 InMemoryCache
用来标准化您的查询结果,因此省略它可能会导致围绕缓存的意外行为。
很遗憾,没有"silver bullet"这个问题。请求查询然后将该查询的数据用作其他查询的变量可能被解释为滥用 API。查询返回的 Type
和用作参数的 Input Type
是完全不同的东西,即使作为 Javascript 对象它们共享一个或多个字段。就像您不能在模式中互换使用类型和输入类型一样,也不应期望它们可以在客户端互换使用。
这也意味着,如果您发现自己处于这种情况,您可能需要重新审视您的架构设计。毕竟,如果数据已经存在于服务器上,为其传递一个 id 并在服务器端检索它就足够了,而不必传递整个对象。
如果您正在使用一些查询来填充一个或多个输入,然后在突变中使用这些输入的值,那么您可能已经将初始查询数据转换为组件状态,然后在您的突变。在这种情况下,__typename
或任何其他不可编辑的字段可能首先不应作为组件状态的一部分包含在内。
归根结底,进行此类操作有望成为例外,而不是常规。我会为 "sanitize" 你的输入创建某种辅助函数并继续。
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
}
}
来自 here。 '__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
})
}
您不应该删除 __typename。它们用于缓存,也用于联合类型。可能应该等待 apollo 的更新。 不幸的是我现在也有问题,我找不到合适的解决方案。首先我曾经简单地删除所有类型名称,但现在我对联合类型有问题。