⚙️ Mandatory directives for fields/directives in GraphQL by PoP
— 4 minute read
I just added support for a lovely new feature in GraphQL by PoP: the ability to execute mandatory directives whenever a specific field or directive is present in the GraphQL query. This means that the GraphQL engine will:
Whenever a specific field from some type is present in the query, add a certain directive (or directives) to be executed on the field
Whenever a specific directive is invoked, execute another directive (or directives) before it
A mandatory directive can, itself, have its own set of mandatory directives which are also added to the directive chain up.
This feature is extremely powerful, since it allows to easily configure what directives are added to the query under what circumstances, as to implement any IFTTT strategy. It supports adding the following capabilities to our GraphQL API:
Define the cache control max-age a field by field basis
Attach a @CacheControl directive to all fields, customizing the value of the maxAge parameter: 1 year for the Post's field url, and 1 hour for field title.
By chaining up directives, we can make sure that, whenever validating if the user can access a field/directive, the response will not be cached. For instance:
Attach directive @validateIsUserLoggedIn to field me
Attach directive @CacheControl with maxAge argument value of 0 to directive @validateIsUserLoggedIn.
Beef up security
Attach a @validateIsUserLoggedIn directive to directive @translate, to avoid malicious actors executing queries against the GraphQL service that can bring the server down and spike its bills (in this case, @translate is based on Google Translate and it pays a fee to use this service)
In this schema, the User type has fields roles and capabilities, which I consider to be sensitive information, so it should not be accessible by the random user.
Then, I created package Access Control List for User Roles to attach directive @validateDoesLoggedInUserHaveAnyRole to these two fields, configured to validate that only a user with a given role can access them (code here):