In the guide to mutations, you have the following example, which takes two Relay ids as input arguments:
AddCommentMutation = GraphQL::Relay::Mutation.define do
# Used to name derived types, eg `"AddCommentInput"`:
name "AddComment"
# Accessible from `input` in the resolve function:
input_field :postId, !types.ID
input_field :authorId, !types.ID
input_field :content, !types.String
# The result has access to these fields,
# resolve must return a hash with these keys
return_field :post, PostType
return_field :comment, CommentType
# The resolve proc is where you alter the system state.
resolve ->(object, inputs, ctx) {
post = Post.find(inputs[:postId])
comment = post.comments.create!(author_id: inputs[:authorId], content: inputs[:content])
# The keys in this hash correspond to `return_field`s above:
{
comment: comment,
post: post,
}
}
end
Given that you are using Post.find(inputs[:postId]), it appears that you expect the mutation to receive an integer as an argument. However, on the client side, the IDs we receive from Relay are globally unique UUIDs.
Based on my testing, types.ID will accept a UUID like "VXNlci0z" (without validation errors), but it will not translate it to an integer ID, which causes ActiveRecord's find lookup to fail.
So far, my testing has been limited to the GraphiQL console, so am I missing something? On the client side we only have UUIDs to work with, but will Relay translate these into integer IDs client side before sending the GraphQL request, or do we need to translate them on the server-side?
Am I missing something?
No, you're not missing anything! It was just a hurried example. In that example, the IDs are just plain database ids, not Relay IDs.
If the incoming value is a Relay ID, then the resolve function should reverse the process in the Schema#id_from_object proc. Does that work in your case?
Ok, we'll try creating a custom input type to do this.
This sounds like a good opportunity for us to clarify the docs a bit too I guess.
I wrote my first mutation using graphql-ruby this week and was a little surprised to see that mutations were under GraphQL::Relay.
Would it make sense to unnest Mutation from GraphQL::Relay and make the docs a bit more generic, @rmosolgo?
Interesting, especially in light of the folder structure mentioned at https://github.com/rmosolgo/graphql-ruby/issues/511#issuecomment-275567456 . I guess people use GraphQL::Relay::Mutation even when they aren't using Relay!
What are the advantages there? I can guess some:
Anything else? Do you use clientMutationId even though you're not on Relay? Do you care that the field can only have one argument, input: ?
Your guess is correct.
It is convenient to get complex inputs and composite return types for free. But it doesn't mean you need to go full Relay just to use mutations.
And no, I just completely ignore clientMutationId
I wonder if we could extract the good parts into a general Mutation, and then extend that in Relay::Mutation to add the only-one-input and clientMutationId considerations...
I like this idea very much!
Here's an attempt to pluck the best parts of Relay::Mutation into something generally reusable: https://github.com/rmosolgo/graphql-ruby/pull/545
This seems strange to me that the mutations also need to be responsible for resolving relational Ids.
Shouldn't there be a type.GlobalID or something that automatically resolves global id inputs to database ids? I don't think object_from_id is the correct method, because I am not trying to load the object from the database, I am just trying to get the ID.
I found this sample project that resolves the id within the mutation class
Is there a better way to do this than adding boilerplate logic in the mutations to transform relational global ids to database ids (ints / uuids)?
Most helpful comment
I wonder if we could extract the good parts into a general
Mutation, and then extend that inRelay::Mutationto add the only-one-input andclientMutationIdconsiderations...