#[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)]
    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(
    // 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(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(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,
}

// NOTICE: Specifying `ScalarValue` as custom named type parameter.
//         Its name should be similar to the one used in methods.
#[graphql_object(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);

// NOTICE: Removing `scalar` argument will fail compilation.
#[graphql_object(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,
}