Complex fields
If you've got a struct that can't be mapped directly to GraphQL, that contains
computed fields or circular structures, you have to use a more powerful tool:
the graphql_object!
macro. This macro lets you define GraphQL objects similar
to how you define methods in a Rust impl
block for a type. Continuing with the
example from the last chapter, this is how you would define Person
using the
macro:
#[macro_use] extern crate juniper;
struct Person {
name: String,
age: i32,
}
graphql_object!(Person: () |&self| {
field name() -> &str {
self.name.as_str()
}
field age() -> i32 {
self.age
}
});
While this is a bit more verbose, it lets you write any kind of function in the field resolver. With this syntax, fields can also take arguments:
#[derive(GraphQLObject)]
struct Person {
name: String,
age: i32,
}
struct House {
inhabitants: Vec<Person>,
}
graphql_object!(House: () |&self| {
// Creates the field inhabitantWithName(name), returning a nullable person
field inhabitant_with_name(name: String) -> Option<&Person> {
self.inhabitants.iter().find(|p| p.name == name)
}
});
To access global data such as database connections or authentication information, a context is used. To learn more about this, see the next chapter: Using contexts.
Description, renaming, and deprecation
Like with the derive attribute, field names will be converted from snake_case
to camelCase
. If you need to override the conversion, you can simply rename
the field. Also, the type name can be changed with an alias:
#[macro_use] extern crate juniper;
struct Person {
name: String,
website_url: String,
}
graphql_object!(Person: () as "PersonObject" |&self| {
field name() -> &str {
self.name.as_str()
}
field websiteURL() -> &str {
self.website_url.as_str()
}
});
More features
GraphQL fields expose more features than Rust's standard method syntax gives us:
- Per-field description and deprecation messages
- Per-argument default values
- Per-argument descriptions
These, and more features, are described more thorougly in the reference documentation.