apollo-feature-requests icon indicating copy to clipboard operation
apollo-feature-requests copied to clipboard

Add a way to strip __typename field from query and mutation results

Open vladshcherbin opened this issue 6 years ago β€’ 79 comments

Migrated from: apollographql/apollo-client#1564

vladshcherbin avatar Jul 27 '18 17:07 vladshcherbin

Isn't this whats filter from graphql-anywhere is about?

https://www.apollographql.com/docs/react/advanced/fragments.html#filtering-with-fragments

smeijer avatar Aug 31 '18 14:08 smeijer

@smeijer not really. It's the same as addTypename but per query or mutation instead of a global one.

vladshcherbin avatar Aug 31 '18 22:08 vladshcherbin

This is really killing us right now and it'd be a huge help if this functionality was built into apollo

joshdotblack avatar Oct 01 '18 09:10 joshdotblack

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
    }
  }

`

RyannGalea avatar Oct 10 '18 12:10 RyannGalea

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.

nvuhung avatar Oct 27 '18 08:10 nvuhung

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
  ))

gihrig avatar Nov 04 '18 19:11 gihrig

Any updates here?

RealSilo avatar Feb 08 '19 04:02 RealSilo

Please add this functionality. Super frustrating this is an issue.

djmgh avatar Feb 09 '19 05:02 djmgh

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 πŸ˜„

matteo-hertel avatar Feb 09 '19 19:02 matteo-hertel

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.

casto101 avatar Feb 19 '19 20:02 casto101

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

matteo-hertel avatar Feb 19 '19 20:02 matteo-hertel

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 __typenames. There should be an option to remove __typenames when querying. From what I understand __typename is only necessary to properly identify types in the cache

lukejagodzinski avatar Apr 08 '19 16:04 lukejagodzinski

@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 avatar Apr 08 '19 17:04 fbartho

@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.

gihrig avatar Apr 08 '19 18:04 gihrig

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 avatar Apr 15 '19 11:04 freshcoat

@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

lukejagodzinski avatar Apr 15 '19 11:04 lukejagodzinski

You are able to change what the cache uses for an ID also, instead of '__typename'.

RyannGalea avatar Apr 15 '19 11:04 RyannGalea

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?

elie222 avatar Jul 09 '19 13:07 elie222

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 avatar Jul 10 '19 05:07 RyannGalea

@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.

elie222 avatar Jul 10 '19 09:07 elie222

The middleware solution isn't working for me. Why there is no __typename field within the operations.variables.

ackvf avatar Sep 18 '19 18:09 ackvf

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

phjardas avatar Oct 17 '19 17:10 phjardas

+1 for symbols sounds great :+1:

CanRau avatar Oct 17 '19 19:10 CanRau

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;
});

dafrie avatar Nov 11 '19 10:11 dafrie

Why is this still not implemented? We need QoL for the development process please

joshuarobs avatar Dec 30 '19 06:12 joshuarobs

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.

Hale-ma avatar Jan 16 '20 12:01 Hale-ma

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.

HosseinAgha avatar Jan 18 '20 18:01 HosseinAgha

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 avatar Jan 21 '20 13:01 nik-lampe

@nik-lampe Thanks for the workaround, not ideal that this is necessary, but seems to be working nicely!

aharasta avatar Feb 12 '20 03:02 aharasta

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.

andrewmclagan avatar Apr 15 '20 01:04 andrewmclagan