graphql-php
graphql-php copied to clipboard
Examples of custom directives
We need some examples in the documentation on how to implement custom directives both for query resolution and schema definitions.
Here is an example of a @cache(ttl: Int!)
directive.
# Directive definition in GraphQL language
# I am using GraphQL language in this example
# If you are using PHP definition, please refer to \GraphQL\Type\Definition\Directive::getInternalDirectives
directive @cache(
ttl: Int!
) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT
// Define some helper functions to get the directive in the current node
class Util
{
/**
* @param ResolveInfo $info
* @param string $name
* @return DirectiveNode|null
*/
public static function getDirectiveByName(ResolveInfo $info, string $name)
{
$fieldNode = $info->fieldNodes[0];
/** @var NodeList $directives */
$directives = $fieldNode->directives;
if ($directives) {
/** @var DirectiveNode[] $directives */
foreach ($directives as $directive) {
if ($directive->name->value === $name) {
return $directive;
}
}
}
return null;
}
/**
* @param DirectiveNode $directive
* @return ValueNode[]
*/
public static function getDirectiveArguments(DirectiveNode $directive)
{
$args = [];
foreach ($directive->arguments as $arg) {
$args[$arg->name->value] = $arg->value;
}
return $args;
}
}
# Usage
class MyType
{
public static function myNode($parent, array $args, ContextImmutable $context, ResolveInfo $info)
{
$fieldName = $info->fieldName;
$cacheDirective = Util::getDirectiveByName($info, 'cache');
if ($cacheDirective) {
$cacheDirectiveArgs = Util::getDirectiveArguments($cacheDirective);
/** @var \GraphQL\Language\AST\IntValueNode $ttlArg */
// Please node IntValueNode is not the only type, there are StringValueNode, etc
$ttlArg = $cacheDirectiveArgs['ttl'];
$ttl = (int)$ttlArg->value;
}
}
}
I got help from the answer provided by @vladar in https://github.com/webonyx/graphql-php/issues/299#issuecomment-417062970
# GraphQL usage
query {
myType {
myNode @cache(ttl: 60) # cache for 60 seconds
}
}
Here is an example of a
@cache(ttl: Int!)
directive.# Directive definition in GraphQL language # I am using GraphQL language in this example # If you are using PHP definition, please refer to \GraphQL\Type\Definition\Directive::getInternalDirectives directive @cache( ttl: Int! ) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT
// Define some helper functions to get the directive in the current node class Util { /** * @param ResolveInfo $info * @param string $name * @return DirectiveNode|null */ public static function getDirectiveByName(ResolveInfo $info, string $name) { $fieldNode = $info->fieldNodes[0]; /** @var NodeList $directives */ $directives = $fieldNode->directives; if ($directives) { /** @var DirectiveNode[] $directives */ foreach ($directives as $directive) { if ($directive->name->value === $name) { return $directive; } } } return null; } /** * @param DirectiveNode $directive * @return ValueNode[] */ public static function getDirectiveArguments(DirectiveNode $directive) { $args = []; foreach ($directive->arguments as $arg) { $args[$arg->name->value] = $arg->value; } return $args; } }
# Usage class MyType { public static function myNode($parent, array $args, ContextImmutable $context, ResolveInfo $info) { $fieldName = $info->fieldName; $cacheDirective = Util::getDirectiveByName($info, 'cache'); if ($cacheDirective) { $cacheDirectiveArgs = Util::getDirectiveArguments($cacheDirective); /** @var \GraphQL\Language\AST\IntValueNode $ttlArg */ // Please node IntValueNode is not the only type, there are StringValueNode, etc $ttlArg = $cacheDirectiveArgs['ttl']; $ttl = (int)$ttlArg->value; } } }
I got help from the answer provided by @vladar in #299 (comment)
# GraphQL usage query { myType { myNode @cache(ttl: 60) # cache for 60 seconds } }
your solution is not perfect. If I define @date
direct to transform timestamp to date, And I must to process the same directive in every field resolver?Why not process in custom type?
Here is an example of a
@cache(ttl: Int!)
directive.# Directive definition in GraphQL language # I am using GraphQL language in this example # If you are using PHP definition, please refer to \GraphQL\Type\Definition\Directive::getInternalDirectives directive @cache( ttl: Int! ) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT
// Define some helper functions to get the directive in the current node class Util { /** * @param ResolveInfo $info * @param string $name * @return DirectiveNode|null */ public static function getDirectiveByName(ResolveInfo $info, string $name) { $fieldNode = $info->fieldNodes[0]; /** @var NodeList $directives */ $directives = $fieldNode->directives; if ($directives) { /** @var DirectiveNode[] $directives */ foreach ($directives as $directive) { if ($directive->name->value === $name) { return $directive; } } } return null; } /** * @param DirectiveNode $directive * @return ValueNode[] */ public static function getDirectiveArguments(DirectiveNode $directive) { $args = []; foreach ($directive->arguments as $arg) { $args[$arg->name->value] = $arg->value; } return $args; } }
# Usage class MyType { public static function myNode($parent, array $args, ContextImmutable $context, ResolveInfo $info) { $fieldName = $info->fieldName; $cacheDirective = Util::getDirectiveByName($info, 'cache'); if ($cacheDirective) { $cacheDirectiveArgs = Util::getDirectiveArguments($cacheDirective); /** @var \GraphQL\Language\AST\IntValueNode $ttlArg */ // Please node IntValueNode is not the only type, there are StringValueNode, etc $ttlArg = $cacheDirectiveArgs['ttl']; $ttl = (int)$ttlArg->value; } } }
I got help from the answer provided by @vladar in #299 (comment)
# GraphQL usage query { myType { myNode @cache(ttl: 60) # cache for 60 seconds } }
your solution is not perfect. If I define
@date
direct to transform timestamp to date, And I must to process the same directive in every field resolver?Why not process in custom type?
Why would you do that with a directive? Your use case sounds like a scalar type use case. But anyway my example is very likely not perfect. It just did what I need and is just use as an example here. Feel free to create a better example. And it's probably out-dated already.
I would love to see more examples of custom directives, the documentation still has me scratching my head.
Looking at the $trackDirective
, I don't know where it is to be placed in my schema. I don't understand how to use a @skip
directive for example when my schema is entirely defined in PHP; I have only seen examples of directive when it is expressive in the type language (GSL?).
What is the FieldArgument
role? Is it something found in the target field.
Directives seem powerful, but I'd love to see the doc expanded, or a directive used in the Blog
example, from which I learned a lot.
Thanks
Working on custom directive integration in a project, I found the GraphQL\Executor\Values able to perform directive detection and argument type casting:
$cacheDirective= Values::getDirectiveValues(
$resolveInfo->schema->getDirective('cache'),
$resolveInfo->fieldNodes[0],
$resolveInfo->variableValues
);
if ($cacheDirective) {
$ttl = $cacheDirective['ttl'];
}
It will convert the TTL argument value to match the schema definition type and return null if it cannot find the directive.