Experience Report for Feature Request
I realized there is not any official Feature Request for a pre-hook, so I need to make my case officially.
Originally this was on the 2021 Roadmap, but then later removed when it was mistakenly decided that there would only be post-hooks only.
Note: This is exactly how Custom Mutations work now, and not a good argument. It is also available in neo4j. It should also be up to the app developer if they want to add latency cost.
What you wanted to do
This would be true for Any Validation we want to do that is too complex for the @auth directive. Currently, there is no way to do this. Since the @auth directive does not support auths based on other nodes, this would not work:
type User {
id: ID!
role: String
...
}
type Book {
title: String
content: String
...
}
Let’s say I want to store the role in the database to an admin can change it. It is impossible to change custom claims on JWTs, and even if you can, you have to refresh, logout etc.
If I want to prevent an add or update mutation based on whether or not the user is an admin in the database, there is no way. This is a specific example of @auth rule based on a different type. While this rule is at the top of my list, the validation could be anything. I may want to validate that the title to my book has to contain the word “Harry,” because I am a crazy author.
What you actually did
There is currently no way to do any validation before the mutation other than basic authentication validation with @auth.
If I need to do anything complex, the only other workaround is to create another whole mutation, secure the original, and use DQL in the custom mutation since the original graphql mutation will be secured.
Don’t allow this mutation to be used:
type Book @auth(
add: { rule: "{$DENIED: { eq: \"DENIED\" } }"}
update: { rule: "{$DENIED: { eq: \"DENIED\" } }"}
delete: { rule: "{$DENIED: { eq: \"DENIED\" } }"}
) {
title: String
content: String
...
}
Then I have to create Custom Lambdas for each type:
Type Mutation {
editBook(...) @lambda
newBook(...) @lambda
removeBook(...) @lambda
}
Why that wasn’t great, with examples
Now, not only do I have to write 2x as much code as this:
@lambdaOnPreMutate(add: true, update: true, delete: true)
All of my GraphQL caching is NOT going to be up-to-date. When I create a new mutation, I have now created a new node as far as URQL or APOLLO is concerned.
My reads will not match my mutations.
My app is more bulky, and now slower.
Any external references to support your case
Again, Dgraph team points to Lambdas to solve everything, but they require much more code, are a pain, and hard to keep up with. However if we have to use them for certain cases, we should at least be able to stick with the same node, as this is an app flow and speed issue.
Possible Solutions:
- Pre-hooks - how neo4j does it
- Get Changed Data - how Firebase Functions does it
- Revoke the write - probably impossible
- When calling GraphQL outside lambda, run lambda,
when calling GraphQL inside lambda, call graphql
(this prevents waiting on lambda to finish, as it would be called manually)
J