Derive Macro juniper::GraphQLUnion [−][src]
#[derive(GraphQLUnion)] { // Attributes available to this derive: #[graphql] }
#[derive(GraphQLUnion)]
macro for deriving a GraphQL union implementation for enums and
structs.
The #[graphql]
helper attribute is used for configuring the derived implementation. Specifying
multiple #[graphql]
attributes on the same definition is totally okay. They all will be
treated as a single attribute.
use derive_more::From; use juniper::{GraphQLObject, GraphQLUnion}; #[derive(GraphQLObject)] struct Human { id: String, home_planet: String, } #[derive(GraphQLObject)] struct Droid { id: String, primary_function: String, } #[derive(From, GraphQLUnion)] enum CharacterEnum { Human(Human), Droid(Droid), }
Custom name and description
The name of GraphQL union may be overriden with a name
attribute’s argument. By default,
a type name is used.
The description of GraphQL union may be specified either with a description
/desc
attribute’s argument, or with a regular Rust doc comment.
#[derive(GraphQLUnion)] #[graphql(name = "Character", desc = "Possible episode characters.")] enum Chrctr { Human(Human), Droid(Droid), } // NOTICE: Rust docs are used as GraphQL description. /// Possible episode characters. #[derive(GraphQLUnion)] enum CharacterWithDocs { Human(Human), Droid(Droid), } // NOTICE: `description` argument takes precedence over Rust docs. /// Not a GraphQL description anymore. #[derive(GraphQLUnion)] #[graphql(description = "Possible episode characters.")] enum CharacterWithDescription { Human(Human), Droid(Droid), }
Custom context
By default, the generated implementation uses unit type ()
as Context
. To use a
custom Context
type for GraphQL union variants types or external resolver functions,
specify it with context
/Context
attribute’s argument.
#[derive(GraphQLObject)] #[graphql(Context = CustomContext)] struct Human { id: String, home_planet: String, } #[derive(GraphQLObject)] #[graphql(Context = CustomContext)] struct Droid { id: String, primary_function: String, } pub struct CustomContext; impl juniper::Context for CustomContext {} #[derive(GraphQLUnion)] #[graphql(Context = CustomContext)] enum Character { Human(Human), Droid(Droid), }
Custom ScalarValue
By default, this macro generates code, which is generic over a ScalarValue
type.
This may introduce a problem when at least one of GraphQL union variants is restricted to a
concrete ScalarValue
type in its implementation. To resolve such problem, a concrete
ScalarValue
type should be specified with a scalar
/Scalar
/ScalarValue
attribute’s
argument.
#[derive(GraphQLObject)] #[graphql(Scalar = DefaultScalarValue)] struct Human { id: String, home_planet: String, } #[derive(GraphQLObject)] struct Droid { id: String, primary_function: String, } // NOTICE: Removing `Scalar` argument will fail compilation. #[derive(GraphQLUnion)] #[graphql(Scalar = DefaultScalarValue)] enum Character { Human(Human), Droid(Droid), }
Ignoring enum variants
To omit exposing an enum variant in the GraphQL schema, use an ignore
/skip
attribute’s
argument directly on that variant.
WARNING: It’s the library user’s responsibility to ensure that ignored enum variant is never returned from resolvers, otherwise resolving the GraphQL query will panic at runtime.
use derive_more::From; use juniper::{GraphQLObject, GraphQLUnion}; #[derive(GraphQLObject)] struct Human { id: String, home_planet: String, } #[derive(GraphQLObject)] struct Droid { id: String, primary_function: String, } #[derive(From, GraphQLUnion)] enum Character<S> { Human(Human), Droid(Droid), #[from(ignore)] #[graphql(ignore)] // or `#[graphql(skip)]`, your choice _State(PhantomData<S>), }
External resolver functions
To use a custom logic for resolving a GraphQL union variant, an external resolver function may be specified with:
- either a
with
attribute’s argument on an enum variant; - or an
on
attribute’s argument on an enum/struct itself.
#[derive(GraphQLObject)] #[graphql(Context = CustomContext)] struct Human { id: String, home_planet: String, } #[derive(GraphQLObject)] #[graphql(Context = CustomContext)] struct Droid { id: String, primary_function: String, } pub struct CustomContext { droid: Droid, } impl juniper::Context for CustomContext {} #[derive(GraphQLUnion)] #[graphql(Context = CustomContext)] enum Character { Human(Human), #[graphql(with = Character::droid_from_context)] Droid(Droid), } impl Character { // NOTICE: The function signature must contain `&self` and `&Context`, // and return `Option<&VariantType>`. fn droid_from_context<'c>(&self, ctx: &'c CustomContext) -> Option<&'c Droid> { Some(&ctx.droid) } } #[derive(GraphQLUnion)] #[graphql(Context = CustomContext)] #[graphql(on Droid = CharacterWithoutDroid::droid_from_context)] enum CharacterWithoutDroid { Human(Human), #[graphql(ignore)] Droid, } impl CharacterWithoutDroid { fn droid_from_context<'c>(&self, ctx: &'c CustomContext) -> Option<&'c Droid> { if let Self::Droid = self { Some(&ctx.droid) } else { None } } }
Deriving structs
Specifying external resolver functions is mandatory for using a struct as a GraphQL union, because this is the only way to declare GraphQL union variants in this case.
#[derive(GraphQLObject)] #[graphql(Context = Database)] struct Human { id: String, home_planet: String, } #[derive(GraphQLObject)] #[graphql(Context = Database)] struct Droid { id: String, primary_function: String, } struct Database { humans: HashMap<String, Human>, droids: HashMap<String, Droid>, } impl juniper::Context for Database {} #[derive(GraphQLUnion)] #[graphql( Context = Database, on Human = Character::get_human, on Droid = Character::get_droid, )] struct Character { id: String, } impl Character { fn get_human<'db>(&self, ctx: &'db Database) -> Option<&'db Human>{ ctx.humans.get(&self.id) } fn get_droid<'db>(&self, ctx: &'db Database) -> Option<&'db Droid>{ ctx.droids.get(&self.id) } }