Converting Mutations
We made some changes to how mutations work in the new version of Relay in order to makes them more straight forward to use and more customizable. Mutations are currently not covered by an automatic conversion and require a manual upgrade. However, limited number of changes is needed to make your existing mutations work with both the old and new environment.
Simplifying FatQueries to Standard GraphQL Queries
FatQueries in Relay Classic mutations was a concept that was confusing for a number of people. It required Relay to keep track of a significant amount of metadata regarding each record and automatically figure out the query to send to the server for the mutation. The logic to deduce the queries to send to the server was both complicated to maintain and slow to run. On top of that, we often had questions about why a particular field is included or skipped. We decided to allow people to have more control by allowing them write out exactly what data they want to update as the result of a mutation. Both individual fields and fragments can be included in these queries. Similar to container fragments, this is subjected to masking. That means only fields listed out directly will be accessible in the callbacks and the updater functions. The data fetched by in referenced fragments will still be updated in the store.
Example of existing fat query:
RelayClassic.QL`
fragment on MarkReadNotificationResponsePayload @relay(pattern: true) {
notification
}
`;
Example of converted mutation query:
graphql`
mutation MarkReadNotificationMutation(
$input: MarkReadNotificationData!
) {
markReadNotification(data: $input) {
notification {
seenState # include fields to be updated
... MyNotificationComponent_notification # reuse fragments from components to be updated
}
}
}
`;
Migrating Configs
FIELDS_CHANGE
This is no longer needed in Compatibility Mode for neither environments. Relay will normalized the data using the mutation query and id to update the store automatically. You can remove it completely.
RANGE_ADD
RANGE_ADD
needs one additional property in the config named connectionInfo
to work with the new environment. Learn more about connectionInfo
Mutation/RANGE_ADD
RANGE_DELETE
RANGE_DELETE
needs one additional property in the config named connectionKeys
to work with the new environment. Learn more about connectionKeys
Mutation/RANGE_DELETE
NODE_DELETE
NODE_DELETE
config will work as-is with the new environment. No change is needed.
Converting a Simple Mutation
Take this example of a simple mutation in Relay Classic:
class LikeStoryMutation extends RelayClassic.Mutation {
getMutation() {
return RelayClassic.QL`mutation {likeStory}`;
}
getVariables() {
return {storyID: this.props.story.id};
}
getFatQuery() {
return RelayClassic.QL`
fragment on LikeStoryPayload @relay(pattern: true) {
story {
likers,
likeSentence,
viewerDoesLike,
},
}
`;
}
getConfigs() {
return [{
type: 'FIELDS_CHANGE',
fieldIDs: {
story: this.props.story.id,
},
}];
}
static fragments = {
story: () => Relay.QL`
fragment on Story {
id
}
`,
};
}
getMutation()
and getFatQuery()
Converting We combine these two into a regular GraphQL mutation, which list out specific fields that needs to be updated.
const mutation = graphql`
mutation LikeStoryMutation($input: LikeStoryData!) {
story(data: $input) {
likers {
count
}
likeSentence
viewerDoesLike
}
}
`;
getConfigs()
Converting As specified above, FIELDS_CHANGE
configs can be omitted.
getVariables()
Converting To convert getVariables()
, we take the return value from the original function and wrap it in an object that contains a property that matches the variable name for the mutation. In this case, the mutation has a input
variable that is of type LikeStoryData
.
const variables = {
input: {
storyID: args.storyID
}
}
Final Result
As you can see, our resulting mutation is a lot simpler and more like regular GraphQL than the Relay Classic version we started out with.
const mutation = graphql`
mutation LikeStoryMutation($input: LikeStoryData!) {
story {
likers {
count
},
likeSentence,
viewerDoesLike
}
}
`;
// environment should be passed in from your component as this.props.relay.environment
function commit(environment: CompatEnvironment, args) {
const variables = {
input: {
storyID: args.storyID
}
};
return commitMutation(environment, {
mutation,
variables,
});
}
See Mutation for additional options on commitMutation
for more complex mutations.