From 4a057ebf09701e62469725fbc8c22d0f66791f40 Mon Sep 17 00:00:00 2001
From: Juniper Bot
Juniper provides a convenience function to introspect the entire schema. The result can then be converted to JSON for use with tools and libraries such as graphql-client:
-use juniper::{EmptyMutation, EmptySubscription, FieldResult, IntrospectionFormat}; +
# extern crate juniper; +# extern crate serde_json; +use juniper::{EmptyMutation, EmptySubscription, FieldResult, IntrospectionFormat}; // Define our schema. diff --git a/master/advanced/non_struct_objects.html b/master/advanced/non_struct_objects.html index 9b56739c..335abf3f 100644 --- a/master/advanced/non_struct_objects.html +++ b/master/advanced/non_struct_objects.html @@ -143,7 +143,8 @@ at enums, but traits will work too - they don't have to be mapped into interfaces.
Using
-Result
-like enums can be a useful way of reporting e.g. validation errors from a mutation:# #[derive(juniper::GraphQLObject)] struct User { name: String } +
# extern crate juniper; +# #[derive(juniper::GraphQLObject)] struct User { name: String } #[derive(juniper::GraphQLObject)] struct ValidationError { diff --git a/master/advanced/objects_and_generics.html b/master/advanced/objects_and_generics.html index a5614e99..0a07be9d 100644 --- a/master/advanced/objects_and_generics.html +++ b/master/advanced/objects_and_generics.html @@ -146,7 +146,8 @@ not make e.g.
Result<T, E>
into a GraphQL type, but you cResult<User, String>
into a GraphQL type.Let's make a slightly more compact but generic implementation of the last chapter:
-# #[derive(juniper::GraphQLObject)] struct User { name: String } +
# extern crate juniper; +# #[derive(juniper::GraphQLObject)] struct User { name: String } # #[derive(juniper::GraphQLObject)] struct ForumPost { title: String } #[derive(juniper::GraphQLObject)] diff --git a/master/advanced/subscriptions.html b/master/advanced/subscriptions.html index f68ad49c..1e57fbdb 100644 --- a/master/advanced/subscriptions.html +++ b/master/advanced/subscriptions.html @@ -155,19 +155,18 @@ juniper_subscriptions = { git = "https://github.com/graphql-rust/juniper&qu operations in your [Schema][Schema]. For subscriptions all fields/operations should be async and should return a Stream.
This example shows a subscription operation that returns two events, the strings
-Hello
andWorld!
sequentially:# use juniper::http::GraphQLRequest; -# use juniper::{DefaultScalarValue, FieldError, SubscriptionCoordinator}; -# use juniper_subscriptions::Coordinator; -# use futures::{Stream, StreamExt}; +
# extern crate futures; +# extern crate juniper; +# extern crate juniper_subscriptions; +# extern crate tokio; +# use juniper::FieldError; +# use futures::Stream; # use std::pin::Pin; +# # #[derive(Clone)] # pub struct Database; # impl juniper::Context for Database {} -# impl Database { -# fn new() -> Self { -# Self {} -# } -# } + # pub struct Query; # #[juniper::graphql_object(Context = Database)] # impl Query { @@ -200,7 +199,12 @@ and shutdown logic.
While you can implement [
-SubscriptionCoordinator
][SubscriptionCoordinator] yourself, Juniper contains a simple and generic implementation called [Coordinator
][Coordinator]. Thesubscribe
operation returns a [Future
][Future] with anItem
value of aResult<Connection, GraphQLError>
, where [Connection
][Connection] is aStream
of values returned by the operation and [GraphQLError
][GraphQLError] is the error when the subscription fails.# use juniper::http::GraphQLRequest; +
# extern crate futures; +# extern crate juniper; +# extern crate juniper_subscriptions; +# extern crate serde_json; +# extern crate tokio; +# use juniper::http::GraphQLRequest; # use juniper::{DefaultScalarValue, EmptyMutation, FieldError, RootNode, SubscriptionCoordinator}; # use juniper_subscriptions::Coordinator; # use futures::{Stream, StreamExt}; diff --git a/master/print.html b/master/print.html index 444deb93..2d3c4479 100644 --- a/master/print.html +++ b/master/print.html @@ -198,7 +198,8 @@ naturally map to GraphQL features, such as
Option<T>
,V types to a GraphQL schema. The most important one is the graphql_object procedural macro that is used for declaring an object with resolvers, which you will use for the
Query
andMutation
roots. -use juniper::{FieldResult, EmptySubscription}; +
# extern crate juniper; +use juniper::{FieldResult, EmptySubscription}; # struct DatabasePool; # impl DatabasePool { @@ -291,7 +292,7 @@ impl Mutation { } } -// A root schema consists of a query and a mutation. +// A root schema consists of a query, a mutation, and a subscription. // Request queries can be executed against a RootNode. type Schema = juniper::RootNode<'static, Query, Mutation, EmptySubscription<Context>>; @@ -333,7 +334,7 @@ impl Query { } -// A root schema consists of a query and a mutation. +// A root schema consists of a query, a mutation, and a subscription. // Request queries can be executed against a RootNode. type Schema = juniper::RootNode<'static, Query, EmptyMutation<Ctx>, EmptySubscription<Ctx>>; @@ -390,7 +391,8 @@ is a struct. struct you want to expose, the easiest way is to use the custom derive attribute. The other way is described in the Complex fields chapter. -
#[derive(juniper::GraphQLObject)] +
# extern crate juniper; +#[derive(juniper::GraphQLObject)] struct Person { name: String, age: i32, @@ -407,7 +409,8 @@ fact that GraphQL is self-documenting and add descriptions to the type and fields. Juniper will automatically use associated doc comments as GraphQL descriptions:
!FILENAME GraphQL descriptions via Rust doc comments
-#[derive(juniper::GraphQLObject)] +
# extern crate juniper; +#[derive(juniper::GraphQLObject)] /// Information about a person struct Person { /// The person's full name, including both first and last names @@ -421,7 +424,8 @@ struct Person {
Objects and fields without doc comments can instead set a
description
via thegraphql
attribute. The following example is equivalent to the above:!FILENAME GraphQL descriptions via attribute
-#[derive(juniper::GraphQLObject)] +
# extern crate juniper; +#[derive(juniper::GraphQLObject)] #[graphql(description="Information about a person")] struct Person { #[graphql(description="The person's full name, including both first and last names")] @@ -435,7 +439,8 @@ struct Person {
Descriptions set via the
-graphql
attribute take precedence over Rust doc comments. This enables internal Rust documentation and external GraphQL documentation to differ:#[derive(juniper::GraphQLObject)] +
# extern crate juniper; +#[derive(juniper::GraphQLObject)] #[graphql(description="This description shows up in GraphQL")] /// This description shows up in RustDoc struct Person { @@ -463,7 +468,8 @@ or
Let's see what that means for building relationships between objects:
-#[derive(juniper::GraphQLObject)] +
# extern crate juniper; +#[derive(juniper::GraphQLObject)] struct Person { name: String, age: i32, @@ -483,7 +489,8 @@ objects.
Renaming fields
By default, struct fields are converted from Rust's standard
-snake_case
naming convention into GraphQL'scamelCase
convention:#[derive(juniper::GraphQLObject)] +
# extern crate juniper; +#[derive(juniper::GraphQLObject)] struct Person { first_name: String, // Would be exposed as firstName in the GraphQL schema last_name: String, // Exposed as lastName @@ -493,7 +500,8 @@ struct Person {
You can override the name by using the
-graphql
attribute on individual struct fields:#[derive(juniper::GraphQLObject)] +
# extern crate juniper; +#[derive(juniper::GraphQLObject)] struct Person { name: String, age: i32, @@ -506,7 +514,8 @@ struct Person {
Deprecating fields
To deprecate a field, you specify a deprecation reason using the
-graphql
attribute:#[derive(juniper::GraphQLObject)] +
# extern crate juniper; +#[derive(juniper::GraphQLObject)] struct Person { name: String, age: i32, @@ -521,7 +530,8 @@ combined. Some restrictions from the GraphQL spec still applies though; you can only deprecate object fields and enum values.
Skipping fields
By default all fields in a
-GraphQLObject
are included in the generated GraphQL type. To prevent including a specific field, annotate the field with#[graphql(skip)]
:#[derive(juniper::GraphQLObject)] +
# extern crate juniper; +#[derive(juniper::GraphQLObject)] struct Person { name: String, age: i32, @@ -541,7 +551,8 @@ can be specified in this
impl
block. If you want to define normal m you have to do so in a separate, normalimpl
block. Continuing with the example from the last chapter, this is how you would definePerson
using the macro: -+
# extern crate juniper; + struct Person { name: String, age: i32, @@ -570,7 +581,8 @@ impl Person {
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(juniper::GraphQLObject)] +
# extern crate juniper; +#[derive(juniper::GraphQLObject)] struct Person { name: String, age: i32, @@ -597,7 +609,8 @@ chapter: Using contexts.
Like with the derive attribute, field names will be converted from
-snake_case
tocamelCase
. If you need to override the conversion, you can simply rename the field. Also, the type name can be changed with an alias:+
# extern crate juniper; + struct Person { } @@ -648,7 +661,8 @@ impl Person {
They can have custom descriptions and default values.
Note: The syntax for this is currently a little awkward. This will become better once the Rust RFC 2565 is implemented.
-+
# extern crate juniper; + struct Person {} #[juniper::graphql_object] @@ -935,7 +949,8 @@ types. Strings are used to identify the problematic field name. Errors for a particular field are also returned as a string. In this example the string contains a server-side localized error message. However, it is also possible to return a unique string identifier and have the client present a localized string to the user. -
#[derive(juniper::GraphQLObject)] +
# extern crate juniper; +#[derive(juniper::GraphQLObject)] pub struct Item { name: String, quantity: i32, @@ -1021,7 +1036,8 @@ GraphQL's type system to describe the errors more precisely. field is set if the validation for that particular field fails. You will likely want some kind of code generation to reduce repetition as the number of types required is significantly larger than before. Each resolver function has a custom
ValidationResult
which contains only fields provided by the function. -#[derive(juniper::GraphQLObject)] +
# extern crate juniper; +#[derive(juniper::GraphQLObject)] pub struct Item { name: String, quantity: i32, @@ -1090,7 +1106,8 @@ errors when they occur.
In the following example, a theoretical database could fail and would generate errors. Since it is not common for the database to fail, the corresponding error is returned as a critical error:
-# #[macro_use] extern crate juniper; +
# // Only needed due to 2018 edition because the macro is not accessible. +# #[macro_use] extern crate juniper; #[derive(juniper::GraphQLObject)] pub struct Item { @@ -1176,7 +1193,8 @@ explore this approach in a real world application.
Enums in GraphQL are string constants grouped together to represent a set of possible values. Simple Rust enums can be converted to GraphQL enums by using a custom derive attribute:
-#[derive(juniper::GraphQLEnum)] +
# extern crate juniper; +#[derive(juniper::GraphQLEnum)] enum Episode { NewHope, Empire, @@ -1189,7 +1207,8 @@ enum Episode { values for these variants are
NEWHOPE
,EMPIRE
, andJEDI
, respectively. If you want to override this, you can use thegraphql
attribute, similar to how it works when defining objects: -#[derive(juniper::GraphQLEnum)] +
# extern crate juniper; +#[derive(juniper::GraphQLEnum)] enum Episode { #[graphql(name="NEW_HOPE")] NewHope, @@ -1202,7 +1221,8 @@ enum Episode {
Documentation and deprecation
Just like when defining objects, the type itself can be renamed and documented, while individual enum variants can be renamed, documented, and deprecated:
-#[derive(juniper::GraphQLEnum)] +
# extern crate juniper; +#[derive(juniper::GraphQLEnum)] #[graphql(name="Episode", description="An episode of Star Wars")] enum StarWarsEpisode { #[graphql(deprecated="We don't really talk about this one")] @@ -1426,7 +1446,8 @@ juniper::graphql_interface!(Character: () where Scalar = <S> |&self| {
Input objects are complex data structures that can be used as arguments to GraphQL fields. In Juniper, you can define input objects using a custom derive attribute, similar to simple objects and enums:
-#[derive(juniper::GraphQLInputObject)] +
# extern crate juniper; +#[derive(juniper::GraphQLInputObject)] struct Coordinate { latitude: f64, longitude: f64 @@ -1449,7 +1470,8 @@ impl Root {
Documentation and renaming
Just like the other derives, you can rename and add documentation to both the type and the fields:
-#[derive(juniper::GraphQLInputObject)] +
# extern crate juniper; +#[derive(juniper::GraphQLInputObject)] #[graphql(name="Coordinate", description="A position on the globe")] struct WorldCoordinate { #[graphql(name="lat", description="The latitude")] @@ -1512,7 +1534,8 @@ crates. They are enabled via features that are on by default.
Often, you might need a custom scalar that just wraps an existing type.
This can be done with the newtype pattern and a custom derive, similar to how serde supports this pattern with
-#[serde(transparent)]
.#[derive(juniper::GraphQLScalarValue)] +
# extern crate juniper; +#[derive(juniper::GraphQLScalarValue)] pub struct UserId(i32); #[derive(juniper::GraphQLObject)] @@ -1524,7 +1547,8 @@ struct User {
That's it, you can now user
UserId
in your schema.The macro also allows for more customization:
-/// You can use a doc comment to specify a description. +
# extern crate juniper; +/// You can use a doc comment to specify a description. #[derive(juniper::GraphQLScalarValue)] #[graphql( transparent, @@ -1549,7 +1573,8 @@ purpose.
The example below is used just for illustration.
Note: the example assumes that the
-Date
type implementsstd::fmt::Display
andstd::str::FromStr
.# mod date { +
# extern crate juniper; +# mod date { # pub struct Date; # impl std::str::FromStr for Date{ # type Err = String; fn from_str(_value: &str) -> Result<Self, Self::Err> { unimplemented!() } @@ -1599,7 +1624,8 @@ where
Enums
Most of the time, we just need a trivial and straightforward Rust enum to represent a GraphQL union.
-# #![allow(dead_code)] +
# extern crate juniper; +# #[macro_use] extern crate derive_more; use derive_more::From; use juniper::{GraphQLObject, GraphQLUnion}; @@ -1630,7 +1656,9 @@ enum Character {
WARNING:
-
It's the library user's responsibility to ensure that ignored enum variant is never returned from resolvers, otherwise resolving the GraphQL query will panic at runtime.# use std::marker::PhantomData; +
# extern crate juniper; +# #[macro_use] extern crate derive_more; +# use std::marker::PhantomData; use derive_more::From; use juniper::{GraphQLObject, GraphQLUnion}; @@ -1659,7 +1687,7 @@ enum Character<S> {
External resolver functions
If some custom logic is needed to resolve a GraphQL union variant, you may specify an external function to do so:
-# #![allow(dead_code)] +
# extern crate juniper; use juniper::{GraphQLObject, GraphQLUnion}; #[derive(GraphQLObject)] @@ -1700,7 +1728,7 @@ impl Character { # fn main() {}
With an external resolver function we can even declare a new GraphQL union variant where the Rust type is absent in the initial enum definition. The attribute syntax
-#[graphql(on VariantType = resolver_fn)]
follows the GraphQL syntax for dispatching union variants.# #![allow(dead_code)] +
# extern crate juniper; use juniper::{GraphQLObject, GraphQLUnion}; #[derive(GraphQLObject)] @@ -1753,7 +1781,8 @@ impl Character {
Structs
Using Rust structs as GraphQL unions is very similar to using enums, with the nuance that specifying an external resolver function is the only way to declare a GraphQL union variant.
-# use std::collections::HashMap; +
# extern crate juniper; +# use std::collections::HashMap; use juniper::{GraphQLObject, GraphQLUnion}; #[derive(GraphQLObject)] @@ -1804,7 +1833,8 @@ impl Character {
NOTICE:
-
A trait has to be object safe, because schema resolvers will need to return a trait object to specify a GraphQL union behind it.use juniper::{graphql_union, GraphQLObject}; +
# extern crate juniper; +use juniper::{graphql_union, GraphQLObject}; #[derive(GraphQLObject)] struct Human { @@ -1837,7 +1867,7 @@ impl Character for Droid {
Custom context
If a context is required in a trait method to resolve a GraphQL union variant, specify it as an argument.
-# #![allow(unused_variables)] +
# extern crate juniper; # use std::collections::HashMap; use juniper::{graphql_union, GraphQLObject}; @@ -1884,7 +1914,8 @@ impl Character for Droid {
Ignoring trait methods
As with enums, we may want to omit some trait methods to be assumed as GraphQL union variants and ignore them.
-use juniper::{graphql_union, GraphQLObject}; +
# extern crate juniper; +use juniper::{graphql_union, GraphQLObject}; #[derive(GraphQLObject)] struct Human { @@ -1920,7 +1951,8 @@ impl Character for Droid {
External resolver functions
Similarly to enums and structs, it's not mandatory to use trait methods as GraphQL union variant resolvers. Instead, custom functions may be specified:
-# use std::collections::HashMap; +
# extern crate juniper; +# use std::collections::HashMap; use juniper::{graphql_union, GraphQLObject}; #[derive(GraphQLObject)] @@ -1980,7 +2012,7 @@ fn get_droid<'db>(ch: &DynCharacter<'_>, ctx: &'db Database)
ScalarValue
considerationsBy default,
-#[derive(GraphQLUnion)]
and#[graphql_union]
macros generate code, which is generic over aScalarValue
type. This may introduce a problem when at least one of GraphQL union variants is restricted to a concreteScalarValue
type in its implementation. To resolve such problem, a concreteScalarValue
type should be specified:# #![allow(dead_code)] +
# extern crate juniper; use juniper::{DefaultScalarValue, GraphQLObject, GraphQLUnion}; #[derive(GraphQLObject)] @@ -2020,7 +2052,8 @@ object somewhere but never reference it, it will not be exposed in a schema.
The query root
The query root is just a GraphQL object. You define it like any other GraphQL object in Juniper, most commonly using the
-graphql_object
proc macro:# use juniper::FieldResult; +
# extern crate juniper; +# use juniper::FieldResult; # #[derive(juniper::GraphQLObject)] struct User { name: String } struct Root; @@ -2037,7 +2070,8 @@ impl Root {
Mutations
Mutations are also just GraphQL objects. Each mutation is a single field that performs some mutating side-effect such as updating a database.
-# use juniper::FieldResult; +
# extern crate juniper; +# use juniper::FieldResult; # #[derive(juniper::GraphQLObject)] struct User { name: String } struct Mutations; @@ -2053,8 +2087,7 @@ impl Mutations {
Converting a Rust schema to the GraphQL Schema Language
Many tools in the GraphQL ecosystem require the schema to be defined in the GraphQL Schema Language. You can generate a GraphQL Schema Language representation of your schema defined in Rust using the
-schema-language
feature (on by default):# // Only needed due to 2018 edition because the macro is not accessible. -# #[macro_use] extern crate juniper; +
# extern crate juniper; use juniper::{FieldResult, EmptyMutation, EmptySubscription, RootNode}; struct Query; @@ -2303,7 +2336,9 @@ produced by issuing a specially crafted introspection query.
Juniper provides a convenience function to introspect the entire schema. The result can then be converted to JSON for use with tools and libraries such as graphql-client:
-use juniper::{EmptyMutation, EmptySubscription, FieldResult, IntrospectionFormat}; +
# extern crate juniper; +# extern crate serde_json; +use juniper::{EmptyMutation, EmptySubscription, FieldResult, IntrospectionFormat}; // Define our schema. @@ -2356,7 +2391,8 @@ at enums, but traits will work too - they don't have to be mapped into interfaces.
Using
-Result
-like enums can be a useful way of reporting e.g. validation errors from a mutation:# #[derive(juniper::GraphQLObject)] struct User { name: String } +
# extern crate juniper; +# #[derive(juniper::GraphQLObject)] struct User { name: String } #[derive(juniper::GraphQLObject)] struct ValidationError { @@ -2409,7 +2445,8 @@ not make e.g.
Result<T, E>
into a GraphQL type, but you cResult<User, String>
into a GraphQL type.Let's make a slightly more compact but generic implementation of the last chapter:
-# #[derive(juniper::GraphQLObject)] struct User { name: String } +
# extern crate juniper; +# #[derive(juniper::GraphQLObject)] struct User { name: String } # #[derive(juniper::GraphQLObject)] struct ForumPost { title: String } #[derive(juniper::GraphQLObject)] @@ -2696,19 +2733,18 @@ juniper_subscriptions = { git = "https://github.com/graphql-rust/juniper&qu operations in your [Schema][Schema]. For subscriptions all fields/operations should be async and should return a Stream.
This example shows a subscription operation that returns two events, the strings
-Hello
andWorld!
sequentially:# use juniper::http::GraphQLRequest; -# use juniper::{DefaultScalarValue, FieldError, SubscriptionCoordinator}; -# use juniper_subscriptions::Coordinator; -# use futures::{Stream, StreamExt}; +
# extern crate futures; +# extern crate juniper; +# extern crate juniper_subscriptions; +# extern crate tokio; +# use juniper::FieldError; +# use futures::Stream; # use std::pin::Pin; +# # #[derive(Clone)] # pub struct Database; # impl juniper::Context for Database {} -# impl Database { -# fn new() -> Self { -# Self {} -# } -# } + # pub struct Query; # #[juniper::graphql_object(Context = Database)] # impl Query { @@ -2741,7 +2777,12 @@ and shutdown logic.
While you can implement [
-SubscriptionCoordinator
][SubscriptionCoordinator] yourself, Juniper contains a simple and generic implementation called [Coordinator
][Coordinator]. Thesubscribe
operation returns a [Future
][Future] with anItem
value of aResult<Connection, GraphQLError>
, where [Connection
][Connection] is aStream
of values returned by the operation and [GraphQLError
][GraphQLError] is the error when the subscription fails.# use juniper::http::GraphQLRequest; +