Namespacing queries and mutations

I haven’t seen many articles or tutorials, so I am curious how you handle large schemas and help your consumers find the queries and/or mutations they need.

There is one solution, which is to have a consistent naming pattern. All the mutations related to managing a user could start with something like user_. But that feels broken since mutations should generally start with a verb.

If you follow the verb-first pattern, you end up with unrelated mutations near each other in the schema/documentations: createDeal, createSegment, createUser, etc. That seems unconducive for consumers trying to find what they need.

Another pattern is to have a mutation that returns a set of related mutations. Example:

type Mutation {
  accountManagement: AccountManagementMutations
}

type AccountManagementMutations {
  createUser(...)
  updateUser(...)
  createGroup(...)
  addUserToGroup(...)
}

This pattern, though, seems like it could be arbitrary on how the mutations are grouped.

So, I am asking for input from those of you with large schemas. What has your experience been like? Do consumers find it challenging to locate the queries and mutations they need? Am I just overthinking this?

The one big caveat that you will need to keep in mind is that the GraphQL spec specifies that only root level fields must execute in serial order.

eg

mutation InOrder {
  createUser(id: 1) { ... }
  updateUser(id: 1, email: "new@example.com") { ... }
}

The spec says that I should be able to rely that these operations execute in order, assuming they all pass. Is that something you should do from a client perspective? Maybe not, but it is in the spec.

So moving field under a type namespace means that you can no longer rely on any spec-defined order and it would just be up to the implementation to decide if the order is followed or not for this operation

mutation Nested {
  accountManagement {
    createUser(id: 1) { ... }
    updateUser(id: 1, email: "new@example.com") { ... }
  }
}
1 Like

Thanks, @shanemyrick, for bringing that to the discussion. We did see that in the spec, but we haven’t encountered a use case yet where we would do more than one mutation at a time, so it’s not a problem.

It might be too granular, in general or in relation to this thread, but I’ve seen nested mutations being even more specific than sweeping crud operations and instead focus on exposing specific actions (that can be used within a single operation from the consumer).

For example:

mutation Nested {
  accountManagement {
    createUser(id: 1) { ... }
    updateUserEmail(id: 1, email: "new@example.com") { ... }
    setDefaultUserEmail(id: 1, email: "new@example.com") { ... }
    updateUserPhone(id: 1, phone: "1231231234") { ... }
  }
}

Hmm, that’s an interesting thought. :pray: