### Install mdBook Source: https://github.com/graphql-rust/juniper/blob/master/book/README.md Install mdBook, the tool used to build the Juniper documentation. This is a prerequisite for local development. ```bash cargo install mdbook ``` -------------------------------- ### Verify Book Code Examples Source: https://github.com/graphql-rust/juniper/blob/master/CONTRIBUTING.md Run this command to verify that the code examples within the Juniper book are correct. This is part of the overall testing process. ```bash make test.book ``` -------------------------------- ### GraphiQL Setup with Juniper Source: https://github.com/graphql-rust/juniper/blob/master/juniper/src/http/graphiql.html This snippet demonstrates the complete client-side setup for GraphiQL, including importing necessary React and GraphiQL components, configuring the fetcher with your GraphQL endpoint, and initializing GraphiQL with plugins like the explorer and history. ```html { "imports": { "react": "https://esm.sh/react@19.2.5", "react/": "https://esm.sh/react@19.2.5/", "react-dom": "https://esm.sh/react-dom@19.2.5", "react-dom/": "https://esm.sh/react-dom@19.2.5/", "graphiql": "https://esm.sh/graphiql@5.2.3?standalone&external=react,react-dom,@graphiql/react,graphql", "graphiql/": "https://esm.sh/graphiql@5.2.3/", "@graphiql/plugin-explorer": "https://esm.sh/@graphiql/plugin-explorer@5.1.2?standalone&external=react,@graphiql/react,graphql", "@graphiql/react": "https://esm.sh/@graphiql/react@0.37.4?standalone&external=react,react-dom,graphql,@graphiql/toolkit,@emotion/is-prop-valid", "@graphiql/toolkit": "https://esm.sh/@graphiql/toolkit@0.12.0?standalone&external=graphql", "graphql": "https://esm.sh/graphql@16.13.2", "@emotion/is-prop-valid": "data:text/javascript," } } import React from 'react'; import ReactDOM from 'react-dom/client'; import { GraphiQL, HISTORY_PLUGIN } from 'graphiql'; import { createGraphiQLFetcher } from '@graphiql/toolkit'; import { explorerPlugin } from '@graphiql/plugin-explorer'; import 'graphiql/setup-workers/esm.sh'; const fetcher = createGraphiQLFetcher({ url: JUNIPER_URL, subscriptionUrl: normalizeSubscriptionEndpoint(JUNIPER_URL, JUNIPER_SUBSCRIPTIONS_URL) }); const plugins = [HISTORY_PLUGIN, explorerPlugin()]; function App() { return React.createElement(GraphiQL, { fetcher, plugins, defaultEditorToolsVisibility: true, }); } const container = document.getElementById('graphiql'); const root = ReactDOM.createRoot(container); root.render(React.createElement(App)); ``` -------------------------------- ### Basic DataLoader Setup in Rust Source: https://github.com/graphql-rust/juniper/blob/master/book/src/advanced/dataloader.md Illustrates the initial setup for using the `dataloader` crate with Juniper. This includes necessary imports and type definitions for entities like Cults and Persons, along with a mock Repository trait. ```rust # extern crate anyhow; # extern crate dataloader; # extern crate juniper; # use std::{collections::HashMap, sync::Arc}; # use anyhow::anyhow; # use dataloader::non_cached::Loader; # use juniper::{GraphQLObject, graphql_object}; # type CultId = i32; type UserId = i32; struct Repository; impl Repository { async fn load_cults_by_ids(&self, cult_ids: &[CultId]) -> anyhow::Result> { unimplemented!() } async fn load_all_persons(&self) -> anyhow::Result> { unimplemented!() } } ``` -------------------------------- ### Install cargo-release Source: https://github.com/graphql-rust/juniper/blob/master/RELEASING.md Install the `cargo-release` tool, which is used to automate crate releases. This is a prerequisite for the release process. ```bash cargo install cargo-release ``` -------------------------------- ### Run All Tests Source: https://github.com/graphql-rust/juniper/blob/master/CONTRIBUTING.md Execute all available tests, including those for code examples in the documentation. Run this in the root directory. ```bash cargo test ``` -------------------------------- ### Test Juniper Book Code Examples Source: https://github.com/graphql-rust/juniper/blob/master/book/README.md Run tests to validate all code examples within the Juniper Book. This ensures the integrity of the documentation's code snippets. ```bash cargo build mdbook test -L target/debug/deps ``` ```bash make test.book ``` -------------------------------- ### Define User and Database Structures Source: https://github.com/graphql-rust/juniper/blob/master/book/src/types/objects/context.md Defines the basic User and Database structures used in the context examples. ```rust # #![expect(dead_code, reason = "example")] # use std::collections::HashMap; # struct Database { users: HashMap, } struct User { id: i32, name: String, friend_ids: Vec, } # # fn main() {} ``` -------------------------------- ### Example GraphQL Query Source: https://github.com/graphql-rust/juniper/blob/master/book/src/advanced/dataloader.md An example GraphQL query that requests persons and their associated cult information. This query structure is designed to benefit from DataLoader batching. ```graphql query { persons { id name cult { id name } } } ``` -------------------------------- ### Defining a Basic GraphQL Interface Source: https://github.com/graphql-rust/juniper/blob/master/book/src/types/interfaces.md This example shows how to define a basic GraphQL interface `Person` with a field `field1` that accepts arguments with default values. ```APIDOC ## `Person` Interface Definition ### Description Defines a GraphQL interface named `Person` with a field `field1`. ### Fields - **field1**(arg1: bool, arg2: i32) -> String - `arg1`: bool (default: true) - `arg2`: i32 (default: `Default::default()`) ### Code Example ```rust #[graphql_interface] trait Person { fn field1( #[graphql(default = true)] arg1: bool, #[graphql(default)] arg2: i32, ) -> String; } ``` ``` -------------------------------- ### Export GraphQL Schema to SDL Source: https://github.com/graphql-rust/juniper/blob/master/book/src/schema/index.md This example shows how to convert a Juniper schema defined in Rust code into its Schema Definition Language (SDL) representation using the `as_sdl()` method. This requires enabling the `schema_language` feature for Juniper. ```rust extern crate juniper; use juniper::{ graphql_object, EmptyMutation, EmptySubscription, FieldResult, RootNode, }; struct Query; #[graphql_object] impl Query { fn hello(&self) -> FieldResult<&str> { Ok("hello world") } } fn main() { // Define our schema in Rust. let schema = RootNode::new( Query, EmptyMutation::<()>::new(), EmptySubscription::<()>::new(), ); // Convert the Rust schema into the GraphQL SDL schema. let result = schema.as_sdl(); let expected = "schema { query: Query } type Query { hello: String! } "; # #[cfg(not(target_os = "windows"))] assert_eq!(result, expected); } ``` -------------------------------- ### Individual GraphQL Query Example Source: https://github.com/graphql-rust/juniper/blob/master/book/src/serve/batching.md This is the JSON data format for a single GraphQL query request. ```graphql { hero { name } } ``` ```json { "query": "{hero{name}}" } ``` -------------------------------- ### Implement GraphQL Query Resolver Source: https://github.com/graphql-rust/juniper/blob/master/book/src/schema/index.md Implement the `graphql_object` macro on a struct to define GraphQL query, mutation, or subscription fields. This example shows a simple query resolver. ```rust struct Query; #[graphql_object] impl Query { fn example(id: String) -> Example { unimplemented!() } } ``` -------------------------------- ### Batched GraphQL Queries Example Source: https://github.com/graphql-rust/juniper/blob/master/book/src/serve/batching.md This is the JSON data format for sending multiple identical GraphQL queries in a single HTTP request. The response will also be an array of results. ```json [ { "query": "{hero{name}}" }, { "query": "{hero{name}}" } ] ``` ```json [ { "data": { "hero": { "name": "R2-D2" } } }, { "data": { "hero": { "name": "R2-D2" } } } ] ``` -------------------------------- ### Execute GraphQL Query with Juniper Source: https://github.com/graphql-rust/juniper/blob/master/book/src/quickstart.md Use `juniper::execute_sync` to run a GraphQL query against a defined schema and context. This is useful for direct query execution without a server setup. ```rust # // Only needed due to 2018 edition because the macro is not accessible. # #[macro_use] extern crate juniper; use juniper::{ EmptyMutation, EmptySubscription, GraphQLEnum, Variables, graphql_object, graphql_value, }; #[derive(GraphQLEnum, Clone, Copy)] enum Episode { // Note, that the enum value will be automatically converted to the // `SCREAMING_SNAKE_CASE` variant, just as GraphQL conventions imply. NewHope, Empire, Jedi, } // Arbitrary context data. struct Ctx(Episode); impl juniper::Context for Ctx {} struct Query; #[graphql_object] #[graphql(context = Ctx)] impl Query { fn favorite_episode(context: &Ctx) -> Episode { context.0 } } type Schema = juniper::RootNode, EmptySubscription>; fn main() { // Create a context. let ctx = Ctx(Episode::NewHope); // Run the execution. let (res, _errors) = juniper::execute_sync( "query { favoriteEpisode }", None, &Schema::new(Query, EmptyMutation::new(), EmptySubscription::new()), &Variables::new(), &ctx, ).unwrap(); assert_eq!( res, graphql_value!({ "favoriteEpisode": "NEW_HOPE", }), ); } ``` -------------------------------- ### Generated SQL Queries Source: https://github.com/graphql-rust/juniper/blob/master/book/src/advanced/dataloader.md Illustrates the efficient SQL queries generated as a result of the DataLoader pattern when executing the example GraphQL query. Shows a query for persons and a batched query for cults. ```sql SELECT id, name, cult_id FROM persons; SELECT id, name FROM cults WHERE id IN (1, 2, 3, 4); ``` -------------------------------- ### Define a GraphQL Subscription Field Source: https://github.com/graphql-rust/juniper/blob/master/book/src/schema/subscriptions.md Subscription fields must be async and return a Stream of GraphQL type values. This example defines a subscription that emits two String values sequentially. ```rust type StringStream = Pin> + Send>>; pub struct Subscription; #[graphql_subscription] #[graphql(context = Database)] impl Subscription { // This subscription operation emits two values sequentially: // the `String`s "Hello" and "World!". async fn hello_world() -> StringStream { let stream = futures::stream::iter([ Ok(String::from("Hello")), Ok(String::from("World!")), ]); Box::pin(stream) } } ``` -------------------------------- ### Serve Juniper Book Locally Source: https://github.com/graphql-rust/juniper/blob/master/book/README.md Launch a local test server for the Juniper Book. This command rebuilds the book and auto-reloads the page upon changes, facilitating local development. ```bash mdbook serve ``` ```bash make book.serve ``` -------------------------------- ### Build Juniper Book Source: https://github.com/graphql-rust/juniper/blob/master/book/README.md Build the Juniper Book into rendered HTML. The output will be placed in the `_rendered/` directory. ```bash mdbook build ``` ```bash make book ``` -------------------------------- ### Execute release process Source: https://github.com/graphql-rust/juniper/blob/master/RELEASING.md Execute the full release process after preparation and checks are complete. This command initiates the release with specified crate, version bump level, and execution enabled. ```bash make release crate=juniper ver=minor exec=yes ``` -------------------------------- ### GraphQL Mutation to Set Favorite Number Source: https://github.com/graphql-rust/juniper/blob/master/book/src/advanced/implicit_and_explicit_null.md Example GraphQL mutation to set a user's favorite number to a specific value. ```graphql mutation { patchUser(patch: { favoriteNumber: 7 }) } ``` -------------------------------- ### GraphQL Mutation to Unset Favorite Number Source: https://github.com/graphql-rust/juniper/blob/master/book/src/advanced/implicit_and_explicit_null.md Example GraphQL mutation to explicitly unset a user's favorite number using `null`. ```graphql mutation { patchUser(patch: { favoriteNumber: null }) } ``` -------------------------------- ### Rust GraphQL Interface Implementation with Subtyping and Nullable Arguments Source: https://github.com/graphql-rust/juniper/blob/master/book/src/types/interfaces.md Illustrates defining GraphQL interfaces (`Node`, `Connection`, `Human`) and implementing them with subtypes and additional nullable arguments. Shows how `HumanConnection` can use `HumanValue` instead of `NodeValue` and how `Luke`'s `home_planet` field accepts an optional language argument. ```rust # extern crate juniper; # use juniper::{graphql_interface, graphql_object, GraphQLInterface, ID}; # #[derive(GraphQLInterface)] #[graphql(for = [HumanValue, Luke])] struct Node { id: ID, } #[derive(GraphQLInterface)] #[graphql(for = HumanConnectionValue)] struct Connection { nodes: Vec, } #[derive(GraphQLInterface)] #[graphql(impl = NodeValue, for = Luke)] struct Human { id: ID, home_planet: String, } #[derive(GraphQLInterface)] #[graphql(impl = ConnectionValue)] struct HumanConnection { nodes: Vec, // ^^^^^^^^^^ notice not `NodeValue` // This can happen, because every `Human` is a `Node` too, so we just // impose additional bounds, which still can be resolved with // `... on Connection { nodes }` syntax. } struct Luke { id: ID, } #[graphql_object] #[graphql(impl = [HumanValue, NodeValue])] impl Luke { fn id(&self) -> &ID { &self.id } fn home_planet(language: Option) -> &'static str { // ^^^^^^^^^^^^^^ // Notice additional `null`able field argument, which is missing on // `Human`. Resolving `...on Human { homePlanet }` will provide `None` // for this argument (default argument value). match language.as_deref() { None | Some("en") => "Tatooine", Some("ko") => "타투인", _ => unimplemented!(), } } } # # fn main() {} ``` -------------------------------- ### GraphQL Mutation to Leave Favorite Number Unchanged Source: https://github.com/graphql-rust/juniper/blob/master/book/src/advanced/implicit_and_explicit_null.md Example GraphQL mutation to leave a user's favorite number unchanged by omitting the field. ```graphql mutation { patchUser(patch: {}) } ``` -------------------------------- ### Juniper Context and Database Connection Source: https://github.com/graphql-rust/juniper/blob/master/book/src/advanced/eager_loading.md Defines a sample `DbConnection` and a `Context` struct that holds the database connection. The `Context` must implement `juniper::Context`. ```rust pub struct DbConnection; impl DbConnection { // Function that will load all the users. fn load_all_users(&self) -> Vec { // ... # unimplemented!() } } // Our Juniper context type which contains a database connection. pub struct Context { db: DbConnection, } impl juniper::Context for Context {} ``` -------------------------------- ### GraphQL Playground Initialization Source: https://github.com/graphql-rust/juniper/blob/master/juniper/src/http/playground.html Initializes the GraphQL Playground in the specified root element. Configure the GraphQL endpoint and subscription endpoint URLs. ```javascript window.addEventListener('load', function (event) { const loadingWrapper = document.getElementById('loading-wrapper'); loadingWrapper.classList.add('fadeOut'); const root = document.getElementById('root'); root.classList.add('playgroundIn'); GraphQLPlayground.init(root, { endpoint: 'JUNIPER_URL', subscriptionEndpoint: 'JUNIPER_SUBSCRIPTIONS_URL' }) }) ``` -------------------------------- ### Run a GraphQL Subscription Request Source: https://github.com/graphql-rust/juniper/blob/master/book/src/schema/subscriptions.md Executes a GraphQL subscription query against the defined schema and database. It establishes a connection to receive subscription events and prints them to the console. ```rust async fn run_subscription() { let schema = schema(); let coordinator = Coordinator::new(schema); let db = Database::new(); let req: GraphQLRequest = serde_json::from_str( r#"{{ "query": "subscription {{ helloWorld }}" }}"#, ).unwrap(); let mut conn = coordinator.subscribe(&req, &db).await.unwrap(); while let Some(result) = conn.next().await { println!("{}", serde_json::to_string(&result).unwrap()); } } ``` -------------------------------- ### GraphQL Query for User Sign-up with Result Handling Source: https://github.com/graphql-rust/juniper/blob/master/book/src/types/objects/error/schema.md This GraphQL query demonstrates how to call a signUp mutation and retrieve either the created user object or a list of validation errors, using fields defined in the SignUpResult enum. ```graphql { mutation { signUp(name: "wrong") { user { name } error { field message } } } } ``` -------------------------------- ### Rust Compile-Time Error: Missing Interface Reference Source: https://github.com/graphql-rust/juniper/blob/master/book/src/types/interfaces.md This example demonstrates a compile-time error that occurs when an interface implementation is missing the `impl` attribute, failing to specify which interface it implements. ```rust # extern crate juniper; # use juniper::{GraphQLInterface, GraphQLObject}; # #[derive(GraphQLObject)] pub struct ObjA { id: String, } #[derive(GraphQLInterface)] #[graphql(for = ObjA)] // ^^^^^^^^^^ the evaluated program panicked at // 'Failed to implement interface `Character` on `ObjA`: missing interface // reference in implementer's `impl` attribute.' struct Character { id: String, } # # fn main() {} ``` -------------------------------- ### Define and Initialize Subscription Schema Source: https://github.com/graphql-rust/juniper/blob/master/book/src/schema/subscriptions.md Defines the GraphQL schema with subscription support and initializes the schema object. This is a foundational step for any GraphQL server. ```rust type Schema = RootNode, Subscription>; fn schema() -> Schema { Schema::new(Query, EmptyMutation::new(), Subscription) } ``` -------------------------------- ### Define GraphQL Schema with Query and Mutation Roots Source: https://github.com/graphql-rust/juniper/blob/master/book/src/schema/index.md This snippet demonstrates defining a GraphQL schema with a User type, a Query root for fetching users, and a Mutation root for signing up users. It uses `#[derive(GraphQLObject)]` and `#[graphql_object]` macros for schema definition. `EmptySubscription` is used when subscriptions are not needed. ```rust extern crate juniper; use juniper::{ EmptySubscription, FieldResult, GraphQLObject, RootNode, graphql_object, }; #[derive(GraphQLObject)] struct User { name: String, } struct Query; #[graphql_object] impl Query { fn user_with_username(username: String) -> FieldResult> { // Look up user in database... # unimplemented!() } } struct Mutation; #[graphql_object] impl Mutation { fn sign_up_user(name: String, email: String) -> FieldResult { // Validate inputs and save user in database... # unimplemented!() } } type Schema = RootNode; # # fn main() {} ``` -------------------------------- ### Add Juniper Dependency Source: https://github.com/graphql-rust/juniper/blob/master/book/src/quickstart.md Add the Juniper crate to your Cargo.toml file to include it in your project. ```toml [dependencies] juniper = "0.17.1" ``` -------------------------------- ### Introspect Selected Fields with Custom ScalarValue Source: https://github.com/graphql-rust/juniper/blob/master/book/src/advanced/lookahead.md Use `executor.look_ahead().children().names()` to iterate over the names of fields selected by the client. This example uses a custom named type parameter for `ScalarValue` for clarity. ```rust extern crate juniper; use juniper::{Executor, GraphQLObject, ScalarValue, graphql_object}; type UserId = i32; #[derive(GraphQLObject)] struct Person { id: UserId, name: String, } struct Query; #[graphql_object] // NOTICE: Specifying `ScalarValue` as custom named type parameter, // so its name is similar to the one used in methods. #[graphql(scalar = S: ScalarValue)] impl Query { fn persons(executor: &Executor<'_, '_, (), S>) -> Vec { // Let's see which `Person`'s fields were selected in the client query. for field_name in executor.look_ahead().children().names() { dbg!(field_name); } // ... unimplemented!() } } ``` -------------------------------- ### Define Relationships Between GraphQL Objects Source: https://github.com/graphql-rust/juniper/blob/master/book/src/types/objects/index.md Objects can have fields that are lists or optional types, which are automatically converted to corresponding GraphQL types. For example, `Vec` becomes `[Person!]!` and `Option` becomes `String` (nullable). ```rust # extern crate juniper; # use juniper::GraphQLObject; # #[derive(GraphQLObject)] struct Person { name: String, age: i32, } #[derive(GraphQLObject)] struct House { address: Option, // converted into `String` (`null`able) inhabitants: Vec, // converted into `[Person!]!` } # # fn main() {} ``` -------------------------------- ### Dry-run release preparation Source: https://github.com/graphql-rust/juniper/blob/master/RELEASING.md Perform a dry-run of the release process to check the generated diffs before making any actual changes. This command uses `make` with specified crate, version bump level, and dry-run options. ```bash make release crate=juniper ver=minor ``` -------------------------------- ### Implement Context for Database Source: https://github.com/graphql-rust/juniper/blob/master/book/src/types/objects/context.md Marks the `Database` struct as a valid context type for Juniper and demonstrates accessing it within a `User` field resolver. ```rust # extern crate juniper; # use std::collections::HashMap; # use juniper::graphql_object; # struct Database { users: HashMap, } // Mark the `Database` as a valid context type for Juniper. impl juniper::Context for Database {} struct User { id: i32, name: String, friend_ids: Vec, } #[graphql_object] #[graphql(context = Database)] // assign `Database` as the context type impl User { // Inject the `Database` context by specifying an argument with the // context type: // - the type must be a reference; // - the name of the argument SHOULD be `context` (or `ctx`). fn friends<'db>(&self, context: &'db Database) -> Vec<&'db User> { // ^^^^^^^ or `ctx`, up to your preference self.friend_ids.iter() .map(|id| { context.users.get(&id).expect("could not find `User` with ID") }) .collect() } fn friend<'db>( &self, id: i32, // Alternatively, the context argument may be marked with an attribute, // and thus, named arbitrary. #[graphql(context)] db: &'db Database, // ^^^^^^^ or `ctx`, up to your preference ) -> Option<&'db User> { self.friend_ids.contains(&id).then(|| { db.users.get(&id).expect("could not find `User` with ID") }) } fn name(&self) -> &str { self.name.as_str() } fn id(&self) -> i32 { self.id } } # # fn main() {} ``` -------------------------------- ### Rust Compile-Time Error: Invalid Argument Nullability Source: https://github.com/graphql-rust/juniper/blob/master/book/src/types/interfaces.md This example shows a compile-time error where an implemented field argument (`is_present`) is non-nullable in the implementation but missing from the interface definition, violating the rule that additional arguments must be nullable. ```rust # extern crate juniper; # use juniper::{graphql_object, GraphQLInterface}; # pub struct ObjA { id: String, } #[graphql_object] #[graphql(impl = CharacterValue)] impl ObjA { fn id(&self, is_present: bool) -> &str { // ^^ the evaluated program panicked at // 'Failed to implement interface `Character` on `ObjA`: Field `id`: Argument // `isPresent` of type `Boolean!` isn't present on the interface and so has // to be nullable.' is_present.then_some(&self.id).unwrap_or("missing") } } #[derive(GraphQLInterface)] #[graphql(for = ObjA)] struct Character { id: String, } # # fn main() {} ``` -------------------------------- ### Rust Compile-Time Error: Invalid Subtype Return Type Source: https://github.com/graphql-rust/juniper/blob/master/book/src/types/interfaces.md This example demonstrates a compile-time error where the return type of a field in the implementation (`Vec!`) is not a valid subtype of the expected return type in the interface (`String!`), violating GraphQL's subtyping rules. ```rust # extern crate juniper; # use juniper::{GraphQLInterface, GraphQLObject}; # #[derive(GraphQLObject)] #[graphql(impl = CharacterValue)] pub struct ObjA { id: Vec, // ^^ the evaluated program panicked at // 'Failed to implement interface `Character` on `ObjA`: Field `id`: // implementer is expected to return a subtype of interface's return // object: `[String!]!` is not a subtype of `String!`.' } #[derive(GraphQLInterface)] #[graphql(for = ObjA)] struct Character { id: String, } # # fn main() {} ``` -------------------------------- ### GraphQL Field Descriptions in Rust Source: https://github.com/graphql-rust/juniper/blob/master/book/src/types/objects/complex_fields.md Demonstrates how to provide descriptions for GraphQL fields and arguments using Rust attributes. Descriptions can be set via doc comments or `#[graphql]` attributes, with the latter taking precedence for GraphQL schema visibility. ```rust struct Person; /// This doc comment is visible only in Rust API docs. #[graphql_object] #[graphql(description = "This description overwrites the one from doc comment.")] impl Person { /// This doc comment is visible only in Rust API docs. #[graphql(description = "This description is visible only in GraphQL schema.")] fn empty() -> &'static str { "" } #[graphql(desc = "This description is visible only in GraphQL schema.")] // ^^^^ shortcut for a `description` argument fn field( #[graphql(desc = "This description is visible only in GraphQL schema.")] arg: bool, ) -> bool { arg } /// This doc comment is visible in both Rust API docs and GraphQL schema /// descriptions. #[graphql(deprecated = "Just because.")] fn deprecated_graphql( // Only `Null`able arguments or non-`Null` arguments with default values // can be deprecated. #[graphql(default, deprecated = "No need.")] arg: bool, ) -> bool { true } // Standard Rust's `#[deprecated]` attribute works too! #[deprecated(note = "Reason is optional, btw!")] fn deprecated_standard( // has no description in GraphQL schema // If no explicit deprecation reason is provided, // then the default "No longer supported" one is used. #[graphql(deprecated)] arg: Option, ) -> bool { false } } # # fn main() {} ``` -------------------------------- ### Adding Documentation and Deprecation to GraphQL Interfaces Source: https://github.com/graphql-rust/juniper/blob/master/book/src/types/interfaces.md Shows how to add descriptions and deprecation notices to GraphQL interfaces, fields, and arguments using `#[graphql(description = "...")]`, `#[graphql(desc = "...")]`, `#[graphql(deprecated = "...")]`, and the standard Rust `#[deprecated]` attribute. ```rust # extern crate juniper; # use juniper::graphql_interface; # /// This doc comment is visible only in Rust API docs. #[graphql_interface] #[graphql(description = "This description overwrites the one from doc comment.")] trait Person { /// This doc comment is visible only in Rust API docs. #[graphql(description = "This description is visible only in GraphQL schema.")] fn empty() -> &'static str; #[graphql(desc = "This description is visible only in GraphQL schema.")] // ^^^^ shortcut for a `description` argument fn field( #[graphql(desc = "This description is visible only in GraphQL schema.")] arg: bool, ) -> bool; /// This doc comment is visible in both Rust API docs and GraphQL schema /// descriptions. #[graphql(deprecated = "Just because.")] fn deprecated_graphql( // Only `Null`able arguments or non-`Null` arguments with default values // can be deprecated. #[graphql(default, deprecated = "No need.")] arg: bool, ) -> bool; // Standard Rust's `#[deprecated]` attribute works too! #[deprecated(note = "Reason is optional, btw!")] fn deprecated_standard( // has no description in GraphQL schema // If no explicit deprecation reason is provided, // then the default "No longer supported" one is used. #[graphql(deprecated)] arg: Option, ) -> bool; } # # fn main() {} ``` -------------------------------- ### GraphQL Object Fields with Descriptions and Deprecations Source: https://github.com/graphql-rust/juniper/blob/master/book/src/types/objects/complex_fields.md Demonstrates how to define fields within a GraphQL object, including custom descriptions that override Rust doc comments and how to mark fields as deprecated. ```APIDOC ## GraphQL Object Fields ### Description Defines fields for a GraphQL object, allowing custom descriptions and deprecations. ### Fields - `empty()`: Returns an empty string. Overwrites Rust doc comment with GraphQL description. - `field(arg: bool)`: Returns a boolean argument. Accepts a description for the argument. - `deprecated_graphql(arg: bool)`: A deprecated field with a custom deprecation reason for the field and its argument. - `deprecated_standard(arg: Option)`: A field deprecated using the standard Rust `#[deprecated]` attribute. ``` -------------------------------- ### Add GraphQL descriptions using Rust doc comments Source: https://github.com/graphql-rust/juniper/blob/master/book/src/types/objects/index.md Juniper automatically uses Rust doc comments to generate GraphQL descriptions for object types and their fields. This allows for self-documenting schemas. ```rust extern crate juniper; use juniper::GraphQLObject; /// Information about a person. #[derive(GraphQLObject)] struct Person { /// The person's full name, including both first and last names. name: String, /// The person's age in years, rounded down. age: i32, } fn main() {} ``` -------------------------------- ### Exposing Generic Results as GraphQL Objects Source: https://github.com/graphql-rust/juniper/blob/master/book/src/types/objects/generics.md Demonstrates how to wrap a Rust `Result` type to expose it as distinct GraphQL objects for different generic instantiations. This approach is necessary due to Rust's orphan rules and GraphQL's naming conventions for types. ```rust extern crate juniper; use juniper::{GraphQLObject, graphql_object}; #[derive(GraphQLObject)] struct User { name: String, } #[derive(GraphQLObject)] struct ForumPost { title: String, } #[derive(GraphQLObject)] struct ValidationError { field: String, message: String, } struct MutationResult(Result>); #[graphql_object] #[graphql(name = "UserResult")] impl MutationResult { fn user(&self) -> Option<&User> { self.0.as_ref().ok() } fn error(&self) -> Option<&[ValidationError]> { self.0.as_ref().err().map(Vec::as_slice) } } #[graphql_object] #[graphql(name = "ForumPostResult")] impl MutationResult { fn forum_post(&self) -> Option<&ForumPost> { self.0.as_ref().ok() } fn error(&self) -> Option<&[ValidationError]> { self.0.as_ref().err().map(Vec::as_slice) } } ``` -------------------------------- ### Adding Documentation and Deprecation Source: https://github.com/graphql-rust/juniper/blob/master/book/src/types/interfaces.md Illustrates how to add descriptions and deprecation notices to GraphQL interfaces, fields, and arguments using attributes. ```APIDOC ## Documenting and Deprecating GraphQL Interfaces ### Description Enhances GraphQL schema clarity by adding descriptions and deprecation markers to interfaces, fields, and arguments. ### Attributes - `#[graphql(description = "...")]`: Adds a description visible in the GraphQL schema. - `#[graphql(deprecated = "...")]` or `#[deprecated]`: Marks an element as deprecated. ### Example ```rust /// This doc comment is visible only in Rust API docs. #[graphql_interface] #[graphql(description = "This description overwrites the one from doc comment.")] trait Person { /// This doc comment is visible only in Rust API docs. #[graphql(description = "This description is visible only in GraphQL schema.")] fn empty() -> &'static str; #[graphql(desc = "This description is visible only in GraphQL schema.")] fn field( #[graphql(desc = "This description is visible only in GraphQL schema.")] arg: bool, ) -> bool; /// This doc comment is visible in both Rust API docs and GraphQL schema /// descriptions. #[graphql(deprecated = "Just because.")] fn deprecated_graphql( #[graphql(default, deprecated = "No need.")] arg: bool, ) -> bool; #[deprecated(note = "Reason is optional, btw!")] fn deprecated_standard( #[graphql(deprecated)] arg: Option, ) -> bool; } ``` **Note**: Only nullable arguments or non-nullable arguments with default values can be deprecated. ``` -------------------------------- ### Defining a GraphQL Interface with Default Arguments Source: https://github.com/graphql-rust/juniper/blob/master/book/src/types/interfaces.md Demonstrates how to define a GraphQL interface in Rust using the `#[graphql_interface]` attribute. It shows how to specify default values for arguments using `#[graphql(default = ...)]` or `#[graphql(default)]`. ```rust #[graphql_interface] trait Person { fn field1( // Default value can be any valid Rust expression, including a function // call, etc. #[graphql(default = true)] arg1: bool, // If default expression is not specified, then the `Default::default()` // value is used. #[graphql(default)] arg2: i32, ) -> String; } # # fn main() {} ``` -------------------------------- ### Rust Implementation for User Sign-up Result Source: https://github.com/graphql-rust/juniper/blob/master/book/src/types/objects/error/schema.md This Rust code defines GraphQL objects for User and ValidationError, and an enum SignUpResult to handle successful user creation or a list of validation errors. The enum is exposed as a GraphQL object with fields for user and error. ```rust # extern crate juniper; # use juniper::{GraphQLObject, graphql_object}; # #[derive(GraphQLObject)] struct User { name: String, } #[derive(GraphQLObject)] struct ValidationError { field: String, message: String, } enum SignUpResult { Ok(User), Error(Vec), } #[graphql_object] impl SignUpResult { fn user(&self) -> Option<&User> { match self { Self::Ok(user) => Some(user), Self::Error(_) => None, } } fn error(&self) -> Option<&[ValidationError]> { match self { Self::Ok(_) => None, Self::Error(errs) => Some(errs.as_slice()) } } } # # fn main() {} ``` -------------------------------- ### Documenting and Deprecating GraphQL Fields Source: https://github.com/graphql-rust/juniper/blob/master/book/src/types/objects/complex_fields.md Add descriptions and deprecation notices to GraphQL fields and arguments using `#[graphql(description = "...")]` and `#[graphql(deprecated = "...")]` or `#[deprecated]` attributes. ```rust extern crate juniper; use juniper::graphql_object; ``` -------------------------------- ### Format Code with Cargo Nightly Source: https://github.com/graphql-rust/juniper/blob/master/CONTRIBUTING.md Run this command in the root directory to ensure consistent code formatting. It requires the nightly Rust compiler. ```bash cargo +nightly fmt --all ``` -------------------------------- ### Database Model Structs for Eager Loading Source: https://github.com/graphql-rust/juniper/blob/master/book/src/advanced/eager_loading.md Defines the underlying Rust structs for database models (`User` and `Country`) and implements the `LoadFrom` trait for `Country` to specify how to load countries based on IDs. ```rust mod models { use std::error::Error; use juniper_eager_loading::LoadFrom; #[derive(Clone)] pub struct User { pub id: i32, pub country_id: i32 } #[derive(Clone)] pub struct Country { pub id: i32, } // This trait is required for eager loading countries. // It defines how to load a list of countries from a list of ids. // Notice that `Context` is generic and can be whatever you want. // It will normally be your Juniper context which would contain // a database connection. impl LoadFrom for Country { type Error = Box; type Context = super::Context; fn load( employments: &[i32], field_args: &(), ctx: &Self::Context, ) -> Result, Self::Error> { // ... # unimplemented!() } } } ``` -------------------------------- ### Resolver for Query.allUsers with Eager Loading Source: https://github.com/graphql-rust/juniper/blob/master/book/src/advanced/eager_loading.md Resolves the `Query.allUsers` field, loading model users, converting them to GraphQL users, and performing eager loading using `User::eager_load_all_children_for_each`. The `QueryTrail` ensures only requested fields are loaded. ```rust // The root query GraphQL type. pub struct Query; impl QueryFields for Query { // The resolver for `Query.allUsers`. fn field_all_users( &self, executor: &Executor<'_, Context>, trail: &QueryTrail<'_, User, Walked>, ) -> FieldResult> { let ctx = executor.context(); // Load the model users. let user_models = ctx.db.load_all_users(); // Turn the model users into GraphQL users. let mut users = User::from_db_models(&user_models); // Perform the eager loading. // `trail` is used to only eager load the fields that are requested. Because // we're using `QueryTrail`s from "juniper_from_schema" it would be a compile // error if we eager loaded associations that aren't requested in the query. User::eager_load_all_children_for_each(&mut users, &user_models, ctx, trail)?; Ok(users) } } ``` -------------------------------- ### Override GraphQL descriptions with #[graphql(description)] Source: https://github.com/graphql-rust/juniper/blob/master/book/src/types/objects/index.md Use the `#[graphql(description = "...")]` attribute to provide GraphQL-specific descriptions that take precedence over Rust doc comments. ```rust extern crate juniper; use juniper::GraphQLObject; /// This doc comment is visible only in Rust API docs. #[derive(GraphQLObject)] #[graphql(description = "This description is visible only in GraphQL schema.")] struct Person { /// This doc comment is visible only in Rust API docs. #[graphql(desc = "This description is visible only in GraphQL schema.")] // ^^^^ shortcut for a `description` argument name: String, /// This doc comment is visible in both Rust API docs and GraphQL schema /// descriptions. age: i32, } fn main() {} ``` -------------------------------- ### Point2D Input Object with Ignore Source: https://github.com/graphql-rust/juniper/blob/master/book/src/types/input_objects.md Illustrates how to ignore fields in an input object using `#[graphql(ignore)]` and provide default values. This is useful for fields that should not be directly settable via GraphQL input. ```APIDOC ## Point2D Input Object with Ignore ### Description Illustrates how to ignore fields in an input object using `#[graphql(ignore)]` and provide default values. This is useful for fields that should not be directly settable via GraphQL input. ### Fields - **x** (f64) - Required - The x-coordinate. - **y** (f64) - Required - The y-coordinate. - **system** (System) - Required - The coordinate system. Ignored in input, defaults to `System::Cartesian`. - **shift** (f64) - Required - An ignored shift value, using the default implementation. ``` -------------------------------- ### GraphQL Schema Definition with Juniper Source: https://github.com/graphql-rust/juniper/blob/master/book/src/advanced/eager_loading.md Defines a GraphQL schema using `graphql_schema!` macro, including a `User` type with an `id` and a `country` field, and a `Country` type. ```rust graphql_schema! { schema { query: Query } type Query { allUsers: [User!]! @juniper(ownership: "owned") } type User { id: Int! country: Country! } type Country { id: Int! } } ``` -------------------------------- ### Create a new CultLoader with custom yield count Source: https://github.com/graphql-rust/juniper/blob/master/book/src/advanced/dataloader.md Instantiates a `CultLoader` with a specified `yield_count`. This controls how many keys are batched before `BatchFn::load` is called, affecting performance and responsiveness. ```rust fn new_cult_loader(repo: Repository) -> CultLoader { CultLoader::new(CultBatcher { repo }) // Usually a `Loader` will coalesce all individual loads which occur // within a single frame of execution before calling a `BatchFn::load()` // with all the collected keys. However, sometimes this behavior is not // desirable or optimal (perhaps, a request is expected to be spread out // over a few subsequent ticks). // A larger yield count will allow more keys to be appended to the batch, // but will wait longer before the actual load. For more details see: // https://github.com/cksac/dataloader-rs/issues/12 // https://github.com/graphql/dataloader#batch-scheduling .with_yield_count(100) } ``` -------------------------------- ### Documenting and Deprecating Input Fields Source: https://github.com/graphql-rust/juniper/blob/master/book/src/types/input_objects.md Add documentation and deprecation information to GraphQL input object fields using `#[graphql(description = "...")]` and `#[graphql(deprecated = "...")]` or `#[deprecated]` attributes. ```rust # extern crate juniper; # use juniper::{GraphQLInputObject, ID}; ``` -------------------------------- ### Generate and Serialize Introspection Query Source: https://github.com/graphql-rust/juniper/blob/master/book/src/schema/index.md This snippet demonstrates how to generate an introspection query for a given schema and serialize the result into a pretty-printed JSON string. It uses `juniper::introspect` and `serde_json::to_string_pretty`. ```rust type Schema = RootNode; fn main() { // Run the built-in introspection query. let (res, _errors) = juniper::introspect( &Schema::new(Query, EmptyMutation::new(), EmptySubscription::new()), &(), IntrospectionFormat::default(), ).unwrap(); // Serialize the introspection result into JSON. let json_result = serde_json::to_string_pretty(&res); assert!(json_result.is_ok()); } ``` -------------------------------- ### Async Context with Mutable References using RwLock Source: https://github.com/graphql-rust/juniper/blob/master/book/src/types/objects/context.md Demonstrates using `tokio::sync::RwLock` for mutable context access in an async resolver. This pattern is necessary when context operations involve asynchronous tasks or require thread-safe mutable access. ```rust # extern crate juniper; # extern crate tokio; # use std::collections::HashMap; # use juniper::graphql_object; use tokio::sync::RwLock; struct Database { requested_count: HashMap, } // Since we cannot directly implement `juniper::Context` // for `RwLock`, we use the newtype idiom. struct DatabaseContext(RwLock); impl juniper::Context for DatabaseContext {} struct User { id: i32, name: String } #[graphql_object] #[graphql(context = DatabaseContext)] impl User { async fn times_requested<'db>(&self, ctx: &'db DatabaseContext) -> i32 { // Acquire a mutable reference and `.await` if async `RwLock` is used, // which is necessary if context consists of async operations like // querying remote databases. // Obtain base type. let DatabaseContext(db) = ctx; // If context is immutable use `.read()` on `RwLock` instead. let mut db = db.write().await; // Perform a mutable operation. db.requested_count .entry(self.id) .and_modify(|e| *e += 1) .or_insert(1) .clone() } fn name(&self) -> &str { self.name.as_str() } fn id(&self) -> i32 { self.id } } # # fn main() {} ```