It may be preferable to put the validation/capability checks into a service layer.
GraphQL is just one entry point into your application. Hence it shouldn't hold validation & capability checks.
If you think of an application that has multiple access layers (REST & GraphQL). You'll be duplicating code by adding validation checks in the GraphQL layer.
Best approach would be to have a code layer to handle this, e.g UserService. This would hold your logic for validation & capability checks.
GraphQL & REST API would just be formatters converting the response to the acceptable format for the respective response types. An example is below for illustration purposes:
class UserService {
public function updateName(string $name) {
// validation/capability check code here.
// if validation fails, throw a user input exception or appropriate exception
//return value.
}
}
GraphQl Mutation
class UserResolver {
public function updateUserName(array $args, context $context) {
try {
$user = (new UserService() )->updateName(args['name']);
return [
'user' => $user
];
} catch (UserInputException $exception) {
return [
'error' => $exception,
'user' => null
];
}
}
}
REST API Controller
class UserController {
public function updateUserName(string $name) {
try {
$user = (new UserService() )->updateName($name);
return [
'user' => $user
];
} catch (UserInputException $exception) {
return [
'error' => $exception->message,
];
}
}
}
By using exceptions in the Service class this way, you can also select exceptions you want to be returned in your response(Can be a GraphQL or REST response).
We should only see GraphQL as an access layer. Resolver functions should be as dumb/simple as possible and not contain business logic, validations & capability checks.