Attribute Macro Interface

Source
#[Interface]
Expand description

Define a new GraphQL interface type.

To define an interface, you need to define a rust trait and mark it with the #[Interface] attribute. then you can mark object types with #[graphql(mark(TraitName))] or #[graphql(implements(TraitName))] attribute to implement the interface in the GraphQL schema.

If you mark the object type with #[graphql(mark(TraitName))], you need to implement fields yourself.

If you mark the object type with #[graphql(implements(TraitName))], you should implement the trait for the object type and fields will be resolved automatically.

you can use Instance<dyn TraitName> as the return type of the field to set the interface as the output type in the GraphQL schema.

§Macro Attributes

AttributeDescriptionType
nameThe name of the interfaceString
rename_fieldsRename all the fields according to the given case convention. The possible values are lowercase, UPPERCASE, PascalCase, camelCase, snake_case, and SCREAMING_SNAKE_CASE.String
rename_argsRename all the arguments according to the given case convention. The possible values are lowercase, UPPERCASE, PascalCase, camelCase, snake_case, and SCREAMING_SNAKE_CASE.String
get_type_nameIf true, it allows the user to implement TypeName traitbool
registerRegister typePath
auto_registerRegister types for each instancePath

§Field Attributes

same as ResolvedObjectFields fields

AttributeDescriptionType
nameThe name of the fieldString
skipSkip this fieldbool
deprecationMark this field as a deprecatedbool
deprecationMark this field as deprecated with the reasonString
rename_argsRename all the arguments according to the given case convention. The possible values are lowercase, UPPERCASE, PascalCase, camelCase, snake_case, and SCREAMING_SNAKE_CASE.String

§Argument Attributes

same as ResolvedObjectFields arguments

AttributeDescriptionType
nameThe name of the argumentString
ctxMark this argument as a contextbool

§Accepted Output and Arguments Types

Same as ResolvedObjectFields

§Example

§Basic

use dynamic_graphql::Interface;
use dynamic_graphql::SimpleObject;
use dynamic_graphql::ResolvedObject;
use dynamic_graphql::ResolvedObjectFields;
use dynamic_graphql::Instance;
use dynamic_graphql::App;


#[Interface]
trait Character {
    fn id(&self) -> String;
    fn name(&self) -> String;
}

// Character trait is not implemented for the Human struct, so it can be marked with
// `#[graphql(mark(Character))]` and interface fields should be resolved manually.

#[derive(SimpleObject)]
#[graphql(mark(Character))]
struct Human {
    // id and name are defined here, they should be exactly the same as the fields
    // defined in the Character interface.
    id: String,
    name: String,
    age: i32,
}

// Character trait is implemented for Droid, so it can be marked with
// `#[graphql(implements(Character))]` and interface fields will be resolved automatically.

#[derive(ResolvedObject)]
#[graphql(implements(Character))]
struct Droid {
    id: String,
    name: String,
    power: i32,
}

#[ResolvedObjectFields]
impl Droid {
    fn power(&self) -> i32 {
        self.power
    }
}

impl Character for Droid {
    fn id(&self) -> String {
        self.id.clone()
    }

    fn name(&self) -> String {
        self.name.clone()
    }
}

#[derive(ResolvedObject)]
#[graphql(root)]
struct Query;

#[ResolvedObjectFields]
impl Query {
    async fn character(&self, id: String) -> Option<Instance<dyn Character>> {
        if id == "1" {
            Some(Instance::new_owned(Human {
                id: "1".to_string(),
                name: "Luke".to_string(),
                age: 20,
            }))
        } else if id == "2" {
            Some(Instance::new_owned(Droid {
                id: "2".to_string(),
                name: "R2-D2".to_string(),
                power: 100,
            }))
        } else {
            None
        }
    }
}


// Human and Droid types never appeared in the Query type signature, so they should be
// registered manually.
#[derive(App)]
struct App(Query, Human, Droid);

let schema = App::create_schema().finish().unwrap();

assert_eq!(
    normalize_schema(&schema.sdl()),
    r#"
interface Character {
  id: String!
  name: String!
}

type Droid implements Character {
  power: Int!
  id: String!
  name: String!
}

type Human implements Character {
  id: String!
  name: String!
  age: Int!
}

type Query {
  character(id: String!): Character
}

directive @include(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT

directive @skip(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT

schema {
  query: Query
}
"#
);