I'm looking for some general tips for designing a GraphQL schema. Let's say you have the following schema:
type Query {
comments(threadId: ID!): [Comment!]!
# Get the latest comment by the current user
latestCommentByUser: Comment!
}
type Mutation {
# Return true if success
createComment(comment: CommentInput!): Boolean!
}
type Comment {
id: ID!
body: String!
time: DateTime!
}
It looks good and is easy to understand. The client can get the comments by:
query CommentsQuery {
comments(threadId: 10) {
id
body
}
latestCommentByUser {
id
body
}
}
Now, let's say the client wants to create a comment:
mutation CreateComment {
# Will return true if succeeded
createComment(body: "This is a comment")
}
Nice! but, we probably want to get the new comment so we can get the id and the timestamp.
So, let's update schema:
type Mutation {
# Return the new comment
createComment(comment: CommentInput!): Comment!
}
This is nice but we will probably also need to update our local version of latestCommentByUser
(sure, this is a silly example but my point is that we need to get more data in the response). We might be able to do something like:
mutation CreateComment {
# Will return true if succeeded
createComment(body: "This is a comment") {
id
body
}
}
query CommentsQuery {
latestCommentByUser {
id
body
}
}
But can we be sure that the mutation always will run before the query? Is it even valid to do both a mutation and a query in the same request?
It can be achieved by doing something like:
type Mutation {
# Return the new comment
createComment(comment: CommentInput!): CreateCommentResponse!
}
type CreateCommentResponse {
newComment: Comment!
latestCommentByUser: Comment!
}
We can divide this into two questions:
How do you handle breaking changes in GraphQL? New url path? Just adding new values/methods in the schema and deprecate the old ones? Some other way?
How do you add more data to the mutation response? I find myself in situations, from time to time, when I want to add some data to the mutation request, like in the example above. Should we always return a generic object (like
CreateCommentResponse
) so we can add and remove data as we go or is it a better way?
Top comments (3)
First of all you shouldn't pass data of queries that would be effected by the mutation inside the mutation payload instead you should refetch the query after mutation or update the local cache. Read more here
How do you handle breaking changes in GraphQL? New url path? Just adding new values/methods in the schema and deprecate the old ones? Some other way?
for adding new values just add them
for breaking changes
How do you add more data to the mutation response? I find myself in situations, from time to time, when I want to add some data to the mutation request, like in the example above. Should we always return a generic object (like CreateCommentResponse) so we can add and remove data as we go or is it a better way?
You can always add more data, but for removing you need to either
Thanks for your reply!
To refetch data after a mutation are a valid solution but it would be nice to fetch the data in the same mutation-request instead of making an extra one, especially when it comes to GraphQL which is all about query data.
Having said that; I just read the spec and it states that one can only make either a query or a mutation request, not both at the same time, and having that stated I only see two solutions: 1. making an extra query (as you suggested) or creating a wrapper type (as described above with the
CreateCommentResponse
-type).I guess all mutation could always return the root
Query
-type to make it possible to query any data after the mutation 🤔 But I think I will just return the changed data to keep it simple.You idea is nice, I think it can work.
Other way is to update the local cache accordingly which means you dont have to query the new data from server apollographql.com/docs/react/cachi...