-
-
Notifications
You must be signed in to change notification settings - Fork 454
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
chore(core): Deprecate maskTypename option #3299
Conversation
We are using |
@yurks You can copy and use the utility as it was manually, or construct the mutation inputs. We don't want to encourage GraphQL outputs and inputs to be made to match, since that's — at least with fragment masking — extremely unlikely to happen naturally. If it's done on purpose it is a constraint that quickly takes a lot of benefits out of GraphQL's predictability when things go wrong, in terms of being able to modify selection sets quickly and independently from mutation inputs. So, overall, if you do need it for migration purposes, try replicating the utility in your own codebase. Otherwise, try creating creation/transform utilities that return the input type(s) you'll need for your mutations. |
thanks, are there any examples how to implement "creation/transform utilities that return the input type(s) you'll need for your mutations"? |
I mean, you just take your inputs and return the output object is what I meant. Instead of striving to pass the same output objects as input objects, to create functions that create the input |
@kitten Do you mind extending on our answer here? Why is this unlikely? How is this connected to Fragment masking? Would appreciate a "newbie" answer here. |
Say, you have a GraphQL object and input that look like this: type Author {
id: ID!
name: String!
}
input UpdateAuthor {
id: ID!
name: String!
} And a corresponding mutation that looks like this: type Mutation {
updateAuthor(input: UpdateAuthor!): Author!
} Note Let's ignore here that and this is requested as part of an fragment AuthorOverview on Author {
id
name
} The component also has a button that opens a section using which someone can update this author. Let's say, because this is a sub-component it now uses a similar or the same fragment and then has a mutation such as the following: mutation UpdateAuthor($input: UpdateAuthor!) {
updateAuthor(input: $input) {
id
...AuthorOverview
}
} Because the shape of All is good so far But, let's say, we now add a small sub-selection here. Say, the type ProfilePicture {
id: ID!
baseUrl: String!
url: String!
width: Int
height: Int
}
type Author {
id: ID!
name: String!
profilePicture(aspectRatio: Float): ProfilePicture
} This is a bit contrived, but all that is important here is that we've added a non-primitive field. We now extend the fragment AuthorOverview on Author {
id
name
profilePicture(aspectRatio: 1) {
id
url
}
} We now will get an error because we can't pass the above fragment's selection into the mutation any longer. How could we fix this? How could we prevent this? These are basically the same question. If we construct an const updateAuthorInput = {
id: authorData.id,
name: updatedData.name ?? authorData.name,
}; So, the whole premise of why The problem here fundamentally is that selections evolve separately. Even if a tool (such as On the API-side, they're separate types in our schema, so they may diverge. A quick side-note on performance: While this is solveable, it wasn't something we did want to do. Basically, So, it can also be annoying to deal with This of course could be solved with a small A note on why we removed this: Ultimately, the decision to remove this comes from the main motivation that it a good feature: The reason it's a "bad" feature is because it leads developers down the wrong path. It provides a solution to a problem that appears easier than the alternative. However, comparatively, given the above problems (and others) the end-result will lead to more pain and make fixing the problem harder. I'd consider this feature a mistake today because it made doing the right thing less desireable and leads to many mutations breaking (if no checks are in place) unexpectedly, and makes the better solution appear harder than it really is. Hope that makes sense ❤️ Can I still have this feature back? Yes, you can still have this feature back if you copy its code. And you can use a import { mapExchange, cacheExchange, fetchExchange, Client } from '@urql/core';
new Client({
url: '/graphql',
exchanges: [
mapExchange({
onResult(result) {
return result.operation.kind === 'query'
? { ...result, data: maskTypename(result.data, true) }
: result;
},
}),
cacheExchange,
fetchExchange,
],
}) |
@kitten Thank you very much for this detailled answer. This was very educational for me (and others, I suppose). Very much appreciated! |
See: #3297 (comment)
Summary
This PR, when applied, deprecates
maskTypename
(both the option and the utility).The option may be removed sooner than the utility function.
In short, applying
maskTypename
was not recommended and applying it in an exchange yourself is still supported in the future.This is because it's often only used (and requested) by users given that they want to pass GraphQL objects back into their mutations as GraphQL input objects, which requires the following pre-conditions to apply:
However, compared to Graphcache's output for example, it leads to non-faithful data shapes, and is only used to “save” time and not map to input objects, which is a short task.
Set of changes
maskTypename
option and function