#[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
Attribute | Description | Type |
---|---|---|
name | The name of the interface | String |
rename_fields | Rename 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_args | Rename 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_name | If true, it allows the user to implement TypeName trait | bool |
register | Register type | Path |
auto_register | Register types for each instance | Path |
§Field Attributes
same as ResolvedObjectFields
fields
Attribute | Description | Type |
---|---|---|
name | The name of the field | String |
skip | Skip this field | bool |
deprecation | Mark this field as a deprecated | bool |
deprecation | Mark this field as deprecated with the reason | String |
rename_args | Rename 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
Attribute | Description | Type |
---|---|---|
name | The name of the argument | String |
ctx | Mark this argument as a context | bool |
§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
}
"#
);