Trait juniper::GraphQLType
source · pub trait GraphQLType<S = DefaultScalarValue>: GraphQLValue<S>where
S: ScalarValue,{
fn name(info: &Self::TypeInfo) -> Option<&str>;
fn meta<'r>(
info: &Self::TypeInfo,
registry: &mut Registry<'r, S>
) -> MetaType<'r, S>
where
S: 'r;
}
Expand description
Primary trait used to expose Rust types in a GraphQL schema.
All of the convenience macros ultimately expand into an implementation of this trait for the given type. This can all be done manually.
Example
Manually deriving an [object][3] is straightforward, but tedious. This is the equivalent of the
User
object as shown in the example in the documentation root:
use juniper::{
meta::MetaType, Arguments, Context, DefaultScalarValue, Executor, ExecutionResult,
FieldResult, GraphQLType, GraphQLValue, Registry,
};
#[derive(Debug)]
struct Database { users: HashMap<String, User> }
impl Context for Database {}
#[derive(Debug)]
struct User { id: String, name: String, friend_ids: Vec<String> }
impl GraphQLType<DefaultScalarValue> for User {
fn name(_: &()) -> Option<&'static str> {
Some("User")
}
fn meta<'r>(_: &(), registry: &mut Registry<'r>) -> MetaType<'r>
where DefaultScalarValue: 'r,
{
// First, we need to define all fields and their types on this type.
//
// If we need arguments, want to implement interfaces, or want to add documentation
// strings, we can do it here.
let fields = &[
registry.field::<&String>("id", &()),
registry.field::<&String>("name", &()),
registry.field::<Vec<&User>>("friends", &()),
];
registry.build_object_type::<User>(&(), fields).into_meta()
}
}
impl GraphQLValue<DefaultScalarValue> for User {
type Context = Database;
type TypeInfo = ();
fn type_name(&self, _: &()) -> Option<&'static str> {
<User as GraphQLType>::name(&())
}
fn resolve_field(
&self,
info: &(),
field_name: &str,
args: &Arguments,
executor: &Executor<Database>
) -> ExecutionResult
{
// Next, we need to match the queried field name. All arms of this match statement
// return `ExecutionResult`, which makes it hard to statically verify that the type you
// pass on to `executor.resolve*` actually matches the one that you defined in `meta()`
// above.
let database = executor.context();
match field_name {
// Because scalars are defined with another `Context` associated type, you must use
// `resolve_with_ctx` here to make the `executor` perform automatic type conversion
// of its argument.
"id" => executor.resolve_with_ctx(info, &self.id),
"name" => executor.resolve_with_ctx(info, &self.name),
// You pass a vector of `User` objects to `executor.resolve`, and it will determine
// which fields of the sub-objects to actually resolve based on the query.
// The `executor` instance keeps track of its current position in the query.
"friends" => executor.resolve(info,
&self.friend_ids.iter()
.filter_map(|id| database.users.get(id))
.collect::<Vec<_>>()
),
// We can only reach this panic in two cases: either a mismatch between the defined
// schema in `meta()` above, or a validation failed because of a this library bug.
//
// In either of those two cases, the only reasonable way out is to panic the thread.
_ => panic!("Field {} not found on type User", field_name),
}
}
}