#[derive(GraphQLUnion)]
{
// Attributes available to this derive:
#[graphql]
}
Expand description
#[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
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
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
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)]
_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)
}
}