apollo-feature-requests
apollo-feature-requests copied to clipboard
Add a way to strip __typename field from query and mutation results
Migrated from: apollographql/apollo-client#1564
Isn't this whats filter
from graphql-anywhere
is about?
https://www.apollographql.com/docs/react/advanced/fragments.html#filtering-with-fragments
@smeijer not really. It's the same as addTypename but per query or mutation instead of a global one.
This is really killing us right now and it'd be a huge help if this functionality was built into apollo
For anyone using Angular, I am currently using this middleware which strips the '__typename' off all requests. It has come in very handy for me. perhaps someone else would benefit.
` import { ApolloLink } from 'apollo-link';
export class AppModule {
constructor(private httpLink: HttpLink, private apollo: Apollo) {
const http = httpLink.create({
uri: environment.graphqlLink,
withCredentials: true
});
const typenameMiddleware = new ApolloLink((operation, forward) => {
if (operation.variables) {
operation.variables = JSON.parse(JSON.stringify(operation.variables), this.omitTypename)
}
return forward(operation)
});
const myAppLink = ApolloLink.from([typenameMiddleware, http]);
apollo.create({
link: myAppLink,
cache: new InMemoryCache()
});
}
private omitTypename(key, value) {
return key === '__typename' ? undefined : value
}
}
`
For anyone using Angular, I am currently using this middleware which strips the '__typename' off all requests. It has come in very handy for me. perhaps someone else would benefit.
` import { ApolloLink } from 'apollo-link';
export class AppModule { constructor(private httpLink: HttpLink, private apollo: Apollo) { const http = httpLink.create({ uri: environment.graphqlLink, withCredentials: true }); const typenameMiddleware = new ApolloLink((operation, forward) => { if (operation.variables) { operation.variables = JSON.parse(JSON.stringify(operation.variables), this.omitTypename) } return forward(operation) }); const myAppLink = ApolloLink.from([typenameMiddleware, http]); apollo.create({ link: myAppLink, cache: new InMemoryCache() }); } private omitTypename(key, value) { return key === '__typename' ? undefined : value } }
`
This solution will be failed if you upload file. The file will be converted to object ({}) after use JSON.parse.
Working with Apollo cache in JS outside of React I ran into this issue. For some reason none of the suggested fixes worked for me. Maybe that's because I have a deeply nested data structure and to accommodate that I use Fragments.
In JS, stripping __typename
turned out to be fairly easy. After some trial and error, I came up with this:
// Deep copy of uiParent
const uiParentCleaned = JSON.parse(JSON.stringify(uiParent))
// Strip __typename from uiParent and item list
delete uiParentCleaned.__typename
uiParentCleaned.items.map((item) => (
// eslint-disable-next-line no-param-reassign
delete item.__typename
))
Any updates here?
Please add this functionality. Super frustrating this is an issue.
For all the people using the JSON.parse approach
like https://github.com/apollographql/apollo-client/issues/1564#issuecomment-357492659 or https://github.com/apollographql/apollo-feature-requests/issues/6#issuecomment-428560796 a word of caution: if there is anything that is not a JS primitive like a File or a Blob , that will be removed (as in cast to the closest stringifiable value like {}) and never be sent as part of the graphQl call and File Upload for instance won't work anymore, this was the cause of a bad afternoon for me so hopefully you won't find yourself in this situation π
We ran into the exact same issue today @matteo-hertel. Out of curiosity, what did you end up doing to get around it? We have a deeply nested structure and it feels very icky to drill into it to avoid stringifying the File.
I've sticked togheter a function to deeply remove the typename and making sure I'm not touching the File object
function stripTypenames(obj: any, propToDelete: string) {
for (const property in obj) {
if (typeof obj[property] === 'object' && !(obj[property] instanceof File)) {
delete obj.property;
const newData = stripTypenames(obj[property], propToDelete);
obj[property] = newData;
} else {
if (property === propToDelete) {
delete obj[property];
}
}
}
return obj;
}
and added it as middleware
const removeTypenameMiddleware = new ApolloLink(
(operation: OperationInterface, forward: ()=>voil | null) => {
if (operation.variables) {
operation.variables = stripTypenames(operation.variables, '__typename');
return forward ? forward(operation) : null;
}
},
);
it worked quite well but I'm not 100% happy with it
It's frustrating that this feature is still not implemented. It isn't big of a deal for flat structures but if you have nested objects then it's really annoying to have to remove all the __typename
s. There should be an option to remove __typename
s when querying. From what I understand __typename
is only necessary to properly identify types in the cache
@lukejagodzinski When working with a union
you need something like the __typename
to act as a discriminant so you know what nested object you're working with. So that's not a feature you always want.
@fbartho So that's not a feature you always want.
No argument there. But it would sure be nice to have a simple documented way to eliminate __typename
when it gets in the way. Or even better, to automatically do the necessary parameter modifications so it becomes a non-issue for all devs.
As I recall, I spent a few days wrestling with this issue before I got it settled.
Removing the __typename shouldnβt be the way to go as itβs important to keep the cache in sync (cache uses the typenames). I am using the βomitβ function of lodash to remove the typename from the object before doing an mutation, this works great for us.
@freshcoat no one talks about removing __typename
from the cache. We just would like to have an option to not fetch it from the cache or at least remove it automatically when sending to mutation. That's it
You are able to change what the cache uses for an ID also, instead of '__typename'.
Why does the mutation care if there are additional fields in variables
at all? Can't Apollo Client just use the fields it needs and filter out the rest?
In most cases @elie222 the '__typename' is not something we include in our inputs, therefore when the mutation is sent to the server along with a '__typename' it doesn't match the shape of our input which is causing issues.
When pulling down data from the server it is already on the objects & become redundant to continually remove it.
Personally, in my projects, I create an ApolloLink which strips '__typename' out along with undefined values as shown above.
@RyannGalea I understand the issue. I'm saying that in general, whether the extra field is called __typename
, extraType
or anything else, it should be stripped from the request. I ran into this issue myself that there was an extra id
field for a mutation I was making. I had to strip this field myself.
The middleware solution isn't working for me. Why there is no __typename
field within the operations.variables.
A nice solution would be to make the __typename
field invisible to JSON serialization. A classic way to add metadata to objects is to use a symbol as the property key, which makes the property automatically non-enumerable.
const obj = { hello: "world" }
const typenameSymbol = Symbol("apolloTypename")
Object.defineProperty(obj, typenameSymbol, { value: "Test" })
console.log(obj)
// --> { hello: 'world' }
console.log(JSON.stringify(obj))
// --> {"hello":"world"}
console.log(obj[typenameSymbol])
// --> Test
+1 for symbols sounds great :+1:
In case someone needs stripping only for firing of mutations and not on each query/subscription (in TypeScript):
import { GraphQLError, OperationDefinitionNode } from "graphql";
import { getMainDefinition } from "apollo-utilities";
const cleanTypenameLink = new ApolloLink((operation, forward) => {
const omitTypename = (key: any, value: any) =>
key === "__typename" ? undefined : value;
const def = getMainDefinition(operation.query);
if (def && (<OperationDefinitionNode>def).operation === "mutation") {
operation.variables = parse(stringify(operation.variables), omitTypename);
}
return forward ? forward(operation) : null;
});
Why is this still not implemented? We need QoL for the development process please
I run into this issue too. Unluckily the v2.6 client set the object to strict mode. This means it is even not possible to delete __typename "in-place" . A copy of the origin object is required to delete __typename.
Dear @hwillson and @benjamn,
I'm mentioning you here as I think you are currently the most active maintainers of the Apollo Client.
I think the livelihood of an open-source project is not only determined by the number of its contributors or its Github stars but it is also determined by how its maintainers participate in the community discussions.
I couldn't find any maintainers response in this issue and some other issues with high number of community reactions.
Leaving a PRs welcome comment and guiding the community on how to contribute this feature, or even rejecting it altogether because of some architectural decisions, ensures us that this project is alive and community voice is being heard.
Thank you for this awesome tool.
The solution provided by @dafrie does work, but can cause problems when you have non serializable objects in your payload. In my case I'm uploading files in my mutation. These File Objects get stripped out to only include the pathname.
I have adapted it to use omit-deep-lodash to prevent this.
import omitDeep from 'omit-deep-lodash'
const cleanTypenameLink = new ApolloLink((operation, forward) => {
const keysToOmit = ['__typename'] // more keys like timestamps could be included here
const def = getMainDefinition(operation.query)
if (def && def.operation === 'mutation') {
operation.variables = omitDeep(operation.variables, keysToOmit)
}
return forward ? forward(operation) : null
})
@nik-lampe Thanks for the workaround, not ideal that this is necessary, but seems to be working nicely!
Our team is migrating to Apollo, this is a show-stopper.
Not because of the issue, simply because of the lack of any meaningful response on such a large issue in such a long period. It has dropped our confidence level that Apollo is actually being maintained.