Directive
Tartiflette bases most of its extensibility on directives. A directive will allow you to execute a behavior at different stages:
on_argument_execution
: allows you to wrap the argument execution. (e.g. access rights, validate the input format...)on_post_input_coercion
: allows you to hook the execution flow right after an input value/variable has beed coerced (appears in version0.10.0
)on_field_execution
: allows you to wrap the field execution. (e.g. resolve the field remotly, apply a specific rate limit on a field...)on_pre_output_coercion
: allows you to mutate the result of the resolved value of a field before returning it (appears in version0.10.0
)on_introspection
: during an introspection query, allows you to wrap the schema fields to add metadata to fields or even to remove objects (e.g. dynamic introspection based on access rights)
How to declare a new directive?
directive @myDirective(
name: String = "Chuck"
) on FIELD_DEFINITION
type Query {
hello: String @myDirective(name: "Norris")
}
from typing import Any, Callable, Dict, Optional, Union
from tartiflette import Directive
@Directive("myDirective")
class MyDirective:
async def on_argument_execution(
self,
directive_args: Dict[str, Any],
next_directive: Callable,
parent_node: Union["FieldNode", "DirectiveNode"],
argument_node: "ArgumentNode",
value: Any,
ctx: Optional[Any],
) -> Any:
######################
# Add your code here #
######################
return await next_directive(parent_node, argument_node, value, ctx)
async def on_post_input_coercion(
self,
directive_args: Dict[str, Any],
next_directive: Callable,
parent_node: Union["VariableDefinitionNode", "InputValueDefinitionNode"],
value: Any,
ctx: Optional[Any],
) -> Any:
######################
# Add your code here #
######################
return await next_directive(parent_node, value, ctx)
async def on_field_execution(
self,
directive_args: Dict[str, Any],
next_resolver: Callable,
parent: Optional[Any],
args: Dict[str, Any],
ctx: Optional[Any],
info: "ResolveInfo",
) -> Any:
######################
# Add your code here #
######################
return await next_resolver(parent, args, ctx, info)
async def on_pre_output_coercion(
self,
directive_args: Dict[str, Any],
next_directive: Callable,
value: Any,
ctx: Optional[Any],
info: "ResolveInfo",
) -> Any:
######################
# Add your code here #
######################
return await next_directive(value, ctx, info)
async def on_introspection(
self,
directive_args: Dict[str, Any],
next_directive: Callable,
introspected_element: Any,
ctx: Optional[Any],
info: "ResolveInfo",
) -> Any:
######################
# Add your code here #
######################
return await next_directive(introspected_element, ctx, info)
Execution flow
Warning: This is valid since
0.10.0
.
Directive hook execution flow is like:
Example
directive @directiveField on FIELD
directive @directiveScalar on SCALAR
directive @directiveEnum on ENUM
directive @directiveEnumValue on ENUM_VALUE
directive @directiveObject on OBJECT
directive @directiveInputObject on INPUT_OBJECT
directive @directiveArgument on ARGUMENT
scalar aScalar @directiveScalar
enum anEnum @directiveEnum {
ONE @directiveEnumValue
TWO
}
input anInputObject @directiveInputObject {
anInputField: aScalar
}
type aType @directiveObject {
aField: aScalar @directiveField
anEnumField: anEnum @directiveField
}
type Query {
field1: aType
field2(anArgument: anInputObject @directiveArgument): aType
field3: anEnum
}
with Resolvers as:
@Resolver("Query.field1")
async def resolve_query_field1(parent, args, ctx, info):
return {"aField": "aValue", "anEnumField":"ONE"}
@Resolver("Query.field2")
async def resolve_query_field2(parent, args, ctx, info):
return {"aField": "aValue", "anEnumField":"TWO"}
@Resolver("Query.field3")
async def resolve_query_field3(parent, args, ctx, info):
return "TWO"
Query 1
query aQuery {
field1 {
aField
anEnumField
}
}
The resolution of Query.field1
will ran:
Query.field1
resolveron_pre_output_coercion
of directivedirectiveObject
of typeaType
output_coercer
forObjectType
Then the resolution of aType.aField
will ran (parrallely ran with aType.anEnumField
):
on_field_execution
of directivedirectiveField
onaType.aField
aType.aField
resolveron_pre_output_coercion
of directivedirectiveScalar
onaScalar
output_coercer
of the scalaraScalar
The resolution of aType.anEnumField
will ran (parrallely ran with aType.aField
):
on_field_execution
of directivedirectiveField
onaType.anEnumField
aType.anEnumField
resolveron_pre_output_coercion
of directivedirectiveEnumValue
causeONE
has a directiveon_pre_output_coercion
of directivedirectiveEnum
of enumanEnum
output_coercer
for enum
Query 2
query aQuery {
field2(anArgument: {anInputField: 3}) {
aField
anEnumField
}
}
The resolution of Query.field2
will ran:
input_coercer
forInputObjectType
input_coercer
for argumentanInputField
of typeaScalar
on_post_input_coercion
of directivedirectiveScalar
of scalaraScalar
on_post_input_coercion
of directivedirectiveInputObject
of typeanInputType
on_argument_execution
of directivedirectiveArgument
for argumentanArgument
Query.field2
resolveron_pre_output_coercion
of directivedirectiveObject
of typeaType
output_coercer
forObjectType
Then the resolution of aType.aField
will ran (parrallely ran with aType.anEnumField
):
on_field_execution
of directivedirectiveField
onaType.aField
aType.aField
resolveron_pre_output_coercion
of directivedirectiveScalar
onaScalar
output_coercer
of the scalaraScalar
The resolution of aType.anEnumField
will ran (parrallely ran with aType.aField
):
on_field_execution
of directivedirectiveField
onaType.anEnumField
aType.anEnumField
resolver (methodatype_anenumfield_resolver
returned "ONE")on_pre_output_coercion
of directivedirectiveEnumValue
causeONE
has a directiveon_pre_output_coercion
of directivedirectiveEnum
of enumanEnum
output_coercer
for enum
Query 3
query aQuery {
field3
}
The resolution of Query.field3
will ran:
Query.field3
resolveron_pre_output_coercion
of directivedirectiveEnum
of enumanEnum
output_coercer
forEnumType