Hello Tyler!
Good to talk to you again.
A while ago, I was thinking exactly the same way about grouping mutations to reduce the number of requests.
For example, imagine having both createUser and login mutation fields.
Obviously, createUser could return a JWT token, but that would give it an extra responsibility, and conceptually, creating a user should only handle user creation, not authentication.
As you mentioned, mutations are executed sequentially when defined at the root level.
However, when fields are nested, they are executed in parallel.
In my case, I also had a namespace-based structure, and to deal with sequential vs. parallel execution, I had to use aliases for the namespaces.
One interesting behavior I noticed was this:
when the user creation succeeds, the login works fine.
When the createUser field throws an error using throw new GraphQLError, the login field is not called, which is the expected behavior.
However, when using a type-based error handling approach, the login field is always executed, since the previous field (createUser) didn’t actually throw, even though it failed logically.
This happens both when using a success: Boolean! flag or when modeling responses with a union type.
I had tried to create a directive called @dependsOn(requires: "") to reduce the number of requests, but no success.
Check out my idea:
mutation CreateAndLoginMutation(
$loginInput: LoginInput!
$createUserInput: CreateUserInput!
) {
accountsCreate: accounts {
createUser(input: $createUserInput) {
__typename
... on User {
id
name
email
}
... on EmailIsAlreadyTakenError {
message
code
}
... on GenericError {
message
code
}
}
}
accountsLogin: accounts {
login(input: $loginInput) @dependsOn(requires: "accountsCreate.createUser.__typename === 'User'") {
__typename
... on Auth {
token
user {
id
name
email
}
}
... on InvalidCredentialsError {
message
}
... on GenericError {
message
}
}
}
}
I think I’ll try again, but using Apollo Plugins, @graphql-tools/utils (mapSchema) and metadata, just like I did in the previous topic