Custom directive

There are two types of directives in GraphQL: executable and type system. Executable directives are used by the client within an operation to modify the behavior (like the built-in @include and @skip directives). Type system directives provide additional information about the types, potentially modifying how the server behaves (like @deprecated and @oneOf). async-graphql allows you to declare both types of custom directives, with different limitations on each.

Executable directives

To create a custom executable directive, you need to implement the CustomDirective trait, and then use the Directive macro to generate a factory function that receives the parameters of the directive and returns an instance of the directive.

Currently async-graphql only supports custom executable directives located at FIELD.

#![allow(unused)]
fn main() {
extern crate async_graphql;
use async_graphql::*;
struct ConcatDirective {
    value: String,
}

#[async_trait::async_trait]
impl CustomDirective for ConcatDirective {
    async fn resolve_field(&self, _ctx: &Context<'_>, resolve: ResolveFut<'_>) -> ServerResult<Option<Value>> {
        resolve.await.map(|value| {
            value.map(|value| match value {
                Value::String(str) => Value::String(str + &self.value),
                _ => value,
            })
        })
    }
}

#[Directive(location = "Field")]
fn concat(value: String) -> impl CustomDirective {
    ConcatDirective { value }
}
}

Register the directive when building the schema:

#![allow(unused)]
fn main() {
extern crate async_graphql;
use async_graphql::*;
struct Query;
#[Object]
impl Query { async fn version(&self) -> &str { "1.0" } }
struct ConcatDirective { value: String, }
#[async_trait::async_trait]
impl CustomDirective for ConcatDirective {
  async fn resolve_field(&self, _ctx: &Context<'_>, resolve: ResolveFut<'_>) -> ServerResult<Option<Value>> { todo!() }
}
#[Directive(location = "Field")]
fn concat(value: String) -> impl CustomDirective { ConcatDirective { value } }
let schema = Schema::build(Query, EmptyMutation, EmptySubscription)
    .directive(concat)
    .finish();
}

Type system directives

To create a custom type system directive, you can use the #[TypeDirective] macro on a function:

#![allow(unused)]
fn main() {
extern crate async_graphql;
use async_graphql::*;
#[TypeDirective(
    location = "FieldDefinition",
    location = "Object",
)]
fn testDirective(scope: String, input: u32, opt: Option<u64>) {}
}

Current only the FieldDefinition and Object locations are supported, you can select one or both. After declaring the directive, you can apply it to a relevant location (after importing the function) like this:

#![allow(unused)]
fn main() {
extern crate async_graphql;
use async_graphql::*;
#[TypeDirective(
location = "FieldDefinition",
location = "Object",
)]
fn testDirective(scope: String, input: u32, opt: Option<u64>) {}
#[derive(SimpleObject)]
#[graphql(
    directive = testDirective::apply("simple object type".to_string(), 1, Some(3))
)]
struct SimpleValue {
    #[graphql(
        directive = testDirective::apply("field and param with \" symbol".to_string(), 2, Some(3))
    )]
    some_data: String,
}
}

This example produces a schema like this:

type SimpleValue @testDirective(scope: "simple object type", input: 1, opt: 3) {
	someData: String! @testDirective(scope: "field and param with \" symbol", input: 2, opt: 3)
}

directive @testDirective(scope: String!, input: Int!, opt: Int) on FIELD_DEFINITION | OBJECT

Note: To use a type-system directive with Apollo Federation's @composeDirective, see the federation docs