Unions

GraphQL unions represent an object that could be one of a list of GraphQL object types, but provides for no guaranteed fields between those types. They also differ from interfaces in that object types declare what interfaces they implement, but are not aware of what unions contain them.

From the server's point of view, GraphQL unions are somewhat similar to interfaces: the main difference is that they don't contain fields on their own, and so, we only need to represent a value, dispatchable into concrete objects.

Obviously, the most straightforward approach to express GraphQL unions in Rust is to use enums. In Juniper this may be done by using #[derive(GraphQLInterface)] attribute on them:

extern crate derive_more;
extern crate juniper;
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)]
//       ^^^^ only for convenience, and may be omitted
enum Character {
    Human(Human),
    Droid(Droid),
}

fn main() {}

Renaming

Just as with renaming GraphQL objects, we can override the default union name by using the #[graphql(name = "...")] attribute:

extern crate juniper;
use juniper::{GraphQLObject, GraphQLUnion};

#[derive(GraphQLObject)]
struct Human {
    id: String,
    home_planet: String,
}

#[derive(GraphQLObject)]
struct Droid {
    id: String,
    primary_function: String,
}

#[derive(GraphQLUnion)]
#[graphql(name = "CharacterUnion")]
enum Character { // exposed as `CharacterUnion` in GraphQL schema
    Human(Human),
    Droid(Droid),
}

fn main() {}

NOTE: Unlike Rust enum variants, GraphQL union members don't have any special names aside from the ones provided by objects themselves, and so, obviously, cannot be renamed.

Documentation

Similarly to documenting GraphQL objects, we can document a GraphQL union via #[graphql(description = "...")] attribute or Rust doc comments:

extern crate juniper;
use juniper::{GraphQLObject, GraphQLUnion};

#[derive(GraphQLObject)]
struct Human {
    id: String,
    home_planet: String,
}

#[derive(GraphQLObject)]
struct Droid {
    id: String,
    primary_function: String,
}

/// This doc comment is visible in both Rust API docs and GraphQL schema 
/// descriptions.
#[derive(GraphQLUnion)]
enum Character {
    /// This doc comment is visible only in Rust API docs.
    Human(Human),
    /// This doc comment is visible only in Rust API docs.
    Droid(Droid),
}

/// This doc comment is visible only in Rust API docs.
#[derive(GraphQLUnion)]
#[graphql(description = "This description overwrites the one from doc comment.")]
//        ^^^^^^^^^^^ or `desc` shortcut, up to your preference
enum Person {
    /// This doc comment is visible only in Rust API docs.
    Human(Human),
}

fn main() {}

NOTE: Unlike Rust enum variants, GraphQL union members don't have any special constructors aside from the provided objects directly, and so, cannot be documented, but rather reuse object descriptions "as is".

Ignoring

In some rare situations we may want to omit exposing an enum variant in a GraphQL schema. Similarly to GraphQL enums, we can just annotate the variant with the #[graphql(ignore)] attribute.

As an example, let's consider the situation where we need to bind some type parameter T for doing interesting type-level stuff in our resolvers. To achieve this we need to have PhantomData<T>, but we don't want it exposed in the GraphQL schema.

extern crate derive_more;
extern crate juniper;
use std::marker::PhantomData;
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 `skip`, up to your preference
    _State(PhantomData<S>),
}

fn main() {}

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 in runtime.

TIP: See more available features in the API docs of the #[derive(GraphQLUnion)] attribute.