Graphql-js: Mutations for nested resources

Created on 6 Nov 2015  路  10Comments  路  Source: graphql/graphql-js

OK, mutations are queries for manipulating data. If so then my root query and root mutation tree should look similar and they both should allow nested fields (nested mutations). I was playing with this (using express-graphql) and it works.

Example:

// PUT /projects/:project_id/products/:id
mutation {
  findProject(id: 1) { // make sure that the project exists and we can access it before mutating data
    updateProduct(id: 1, name: "Foo") { // the resolve function receives a valid `project` as the first arg
      id
    }
  } 
}

Is this a valid example? Should mutations be nested like this? If no, how should I handle nested resources? I can not find any real-life example that would mutate nested resources. All examples define mutations only on the first level (fields on the root mutation).

Most helpful comment

We do not support mutations at any level other than the top. In cases where we need to support more complex mutations that rely on more information, we implement that in the resolve() function of the mutation.

In this case, updateProduct's resolve() function should first ensure that the product ID is valid before performing the update and do whatever is reasonable in the case that no product by that ID was found - perhaps return an error.

All 10 comments

There is at the moment one big difference between query and mutation type. Root mutation fields are guaranteed to execute strictly sequentially even if you are returning promises as a result of the resolve method. Root query fields, as well as nested mutation fields, do not have this execution guarantee.

So in other words only root mutation fields are meant to be used for mutations (at least I interpret it like this). I guess it depends on your use-case. So maybe it still make sense to do mutations in the nested fields as long as execution order does not matter.

In many cases though the order of execution matters. I think this GH issue raises a valid concern. Since sequential execution is limited only to root mutation fields, all possible application mutations should be defined as fields directly in the mutation type. In big applications and schemas it can cause very big mutation type with many unrelated/independent fields. Also I see namespacing as a problem. Given this flat mutation structure, I don't see other way but to follow naming convention to group related mutations together, which I find suboptimal in my case. That said, as long a number of mutation fields is low, I guess it should be manageable. Polymorphic input types can definitely help in this respect (at least in my case).

We do not support mutations at any level other than the top. In cases where we need to support more complex mutations that rely on more information, we implement that in the resolve() function of the mutation.

In this case, updateProduct's resolve() function should first ensure that the product ID is valid before performing the update and do whatever is reasonable in the case that no product by that ID was found - perhaps return an error.

@leebyron thanks!

Thanks for bringing this up @xpepermint.

We at Graphcool have written a blog post about nested mutations and how to use them to improve DX. You can find it here: https://www.graph.cool/blog/improving-dx-with-nested-mutations-vietahx7ih

The schema design we're proposing looks like this:

image

@schickling is createPost returning the 'post' type - the same as perhaps an expected query would return like getPost or a post object in the query.

If the result of createPost is the same type as the query of a post, could then the query of the author is independent of the results of the insert, as in I depending on implementation I could see where the author(data) in the result might not return the same author(data) as expected.

I totally understand why the graphql authors are being careful about their implementation, and some features that are requested, and the slowness of new features. This stuff hurts my head on a good day. AND it needs to be viable for many types of implementations.

I believe my package is the best way to achieve nested mutations.
Check it out https://github.com/oney/graphql-mutate

@leebyron Hi. I had a doubt. I read this issue and this blog post here: https://www.freecodecamp.org/news/beware-of-graphql-nested-mutations-9cdb84e062b5/ on nested mutations. The following is an example on how I am using it to organize my mutations into appropriate groups.

Will I face any issue while doing this? I am not relying on the parent's value in the child but I am just using them as a way to organize my mutations. Is this valid and okay to use?

Sample Schema:

type Mutation {
    User: UserOps
    Posts: PostOps
}

type UserOps {
    createUsers(users: [CreateUserInput]): [UserSchema]
}

Sample query

mutation createUsers($userObjList: [CreateUserInput]) {
  User {
    createUsers(users: $userObjList) {
      name
      dob
      id
    }
  }
}

@schickling The link is broken. Thought I would let you know.

Thanks.

@tvvignesh

We do not support mutations at any level other than the top

I would echo this and encourage you not to do what you're trying to do. There are better ways to namespace and keep organized than by breaking the spec in this way.

@dncrews Regarding "better ways to namespace", any ideas on how we can do that (not from a developer perspective, but for users who are actually exploring the GraphQL documentation through the explorer)

I'm not sure where I should be answering this question, since you're asking it on both threads, but since OP had a different question than this one, I'm putting my answer on your other issue: https://github.com/apollographql/apollo-server/issues/3635#issuecomment-570740807

Was this page helpful?
0 / 5 - 0 ratings