#[graphql_object]
Expand description
#[graphql_object]
macro for generating a GraphQL object
implementation for structs with computable field resolvers (declared via
a regular Rust impl
block).
It enables you to write GraphQL field resolvers for a type by declaring a
regular Rust impl
block. Under the hood, the macro implements
GraphQLType
/GraphQLValue
traits.
Specifying multiple #[graphql_object]
attributes on the same definition
is totally okay. They all will be treated as a single attribute.
use juniper::graphql_object;
// We can declare the type as a plain struct without any members.
struct Query;
#[graphql_object]
impl Query {
// WARNING: Only GraphQL fields can be specified in this `impl` block.
// If normal methods are required on the struct, they can be
// defined either in a separate "normal" `impl` block, or
// marked with `#[graphql(ignore)]` attribute.
// This defines a simple, static field which does not require any
// context.
// Such field can return any value that implements `GraphQLType` and
// `GraphQLValue` traits.
//
// NOTICE: By default, field names will be converted to `camelCase`.
// In the generated GraphQL schema this field will be available
// as `apiVersion`.
fn api_version() -> &'static str {
"0.1"
}
// This field takes two arguments.
// GraphQL arguments are just regular function parameters.
//
// NOTICE: In `juniper`, arguments are non-nullable by default. For
// optional arguments, you have to specify them as `Option<_>`.
async fn add(a: f64, b: f64, c: Option<f64>) -> f64 {
a + b + c.unwrap_or(0.0)
}
}
§Accessing self
Fields may also have a self
receiver.
struct Person {
first_name: String,
last_name: String,
}
#[graphql_object]
impl Person {
fn first_name(&self) -> &str {
&self.first_name
}
fn last_name(&self) -> &str {
&self.last_name
}
fn full_name(&self) -> String {
self.build_full_name()
}
// This method is useful only to define GraphQL fields, but is not
// a field itself, so we ignore it in schema.
#[graphql(ignore)] // or `#[graphql(skip)]`, up to your preference
fn build_full_name(&self) -> String {
format!("{} {}", self.first_name, self.last_name)
}
}
§Custom name, description, deprecation and argument defaults
The name of GraphQL object, its field, or a field argument may be
overridden with a name
attribute’s argument. By default, a type name is
used or camelCased
method/argument name.
The description of GraphQL object, its field, or a field argument may
be specified either with a description
/desc
attribute’s argument, or
with a regular Rust doc comment.
A field of GraphQL object may be deprecated by specifying a
deprecated
attribute’s argument, or with regular Rust #[deprecated]
attribute.
The default value of a field argument may be specified with a default
attribute argument (if no exact value is specified then Default::default
is used).
struct HumanWithAttrs;
#[graphql_object]
#[graphql(
// Rename the type for GraphQL by specifying the name here.
name = "Human",
// You may also specify a description here.
// If present, doc comments will be ignored.
desc = "Possible episode human.",
)]
impl HumanWithAttrs {
#[graphql(name = "id", desc = "ID of the human.")]
#[graphql(deprecated = "Don't use it")]
fn some_id(
&self,
#[graphql(name = "number", desc = "Arbitrary number.")]
// You may specify default values.
// A default can be any valid expression that yields the right type.
#[graphql(default = 5)]
num: i32,
) -> &str {
"Don't use me!"
}
}
struct HumanWithDocs;
// Rust docs are used as GraphQL description.
/// Possible episode human.
#[graphql_object]
impl HumanWithDocs {
// Doc comments also work on fields.
/// ID of the human.
#[deprecated]
fn id(
&self,
// If expression is not specified then `Default::default()` is used.
#[graphql(default)] num: i32,
) -> &str {
"Deprecated"
}
}
§Renaming policy
By default, all GraphQL object fields and their arguments are renamed
via camelCase
policy (so fn api_version() -> String
becomes apiVersion
field in GraphQL schema, and so on). This complies with default GraphQL
naming conventions demonstrated in spec.
However, if you need for some reason apply another naming convention, it’s
possible to do by using rename_all
attribute’s argument. At the moment it
supports the following policies only: SCREAMING_SNAKE_CASE
, camelCase
,
none
(disables any renaming).
struct Query;
#[graphql_object]
#[graphql(rename_all = "none")] // disables renaming
impl Query {
// NOTICE: In the generated GraphQL schema this field will be available
// as `api_version`.
fn api_version() -> &'static str {
"0.1"
}
// NOTICE: In the generated GraphQL schema these field arguments will be
// available as `arg_a` and `arg_b`.
async fn add(arg_a: f64, arg_b: f64, c: Option<f64>) -> f64 {
arg_a + arg_b + c.unwrap_or(0.0)
}
}
§Ignoring methods
To omit some method to be assumed as a GraphQL object field and ignore
it, use an ignore
attribute’s argument directly on that method.
struct Human(String);
#[graphql_object]
impl Human {
fn id(&self) -> &str {
&self.0
}
#[graphql(ignore)]
fn kaboom(&mut self) {}
}
§Custom context
By default, the generated implementation tries to infer Context
type
from signatures of impl
block methods, and uses [unit type ()
][4] if
signatures contains no Context
arguments.
If Context
type cannot be inferred or is inferred incorrectly, then
specify it explicitly with context
attribute’s argument.
If method argument is named as context
or ctx
then this argument is
assumed as Context
and will be omitted in GraphQL schema.
Additionally, any argument may be marked as Context
with a context
attribute’s argument.
struct Database {
humans: HashMap<String, Human>,
}
impl juniper::Context for Database {}
struct Human {
id: String,
home_planet: String,
}
#[graphql_object]
#[graphql(context = Database)]
impl Human {
fn id<'db>(&self, context: &'db Database) -> Option<&'db str> {
context.humans.get(&self.id).map(|h| h.id.as_str())
}
fn info<'db>(&self, context: &'db Database) -> Option<&'db str> {
context.humans.get(&self.id).map(|h| h.home_planet.as_str())
}
}
§Using Executor
If an Executor
is required in a method to resolve a GraphQL object
field, specify it as an argument named as executor
or explicitly marked
with an executor
attribute’s argument. Such method argument will be
omitted in GraphQL schema.
However, this requires to explicitly parametrize over ScalarValue
, as
Executor
does so.
struct Human {
name: String,
}
#[graphql_object]
// NOTICE: Specifying `ScalarValue` as custom named type parameter.
// Its name should be similar to the one used in methods.
#[graphql(scalar = S: ScalarValue)]
impl Human {
async fn id<'a, S: ScalarValue>(
&self,
executor: &'a Executor<'_, '_, (), S>,
) -> &'a str {
executor.look_ahead().field_name()
}
fn name<'b, S: ScalarValue>(
&'b self,
#[graphql(executor)] _another: &Executor<'_, '_, (), S>,
) -> &'b str {
&self.name
}
}
§Custom ScalarValue
By default, #[graphql_object]
macro generates code, which is generic over
a ScalarValue
type. This may introduce a problem when at least one of
its fields 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.
struct Human(String);
#[graphql_object]
// NOTICE: Removing `scalar` argument will fail compilation.
#[graphql(scalar = DefaultScalarValue)]
impl Human {
fn id(&self) -> &str {
&self.0
}
fn helper(&self) -> Droid {
Droid {
id: self.0.clone(),
}
}
}
#[derive(GraphQLObject)]
#[graphql(scalar = DefaultScalarValue)]
struct Droid {
id: String,
}