Updated book for master ***NO_CI***
This commit is contained in:
parent
f42054f407
commit
3fda1ed1ed
12 changed files with 350 additions and 292 deletions
|
@ -162,11 +162,14 @@ result can then be converted to JSON for use with tools and libraries such as
|
||||||
<pre><pre class="playpen"><code class="language-rust"># #![allow(unused_variables)]
|
<pre><pre class="playpen"><code class="language-rust"># #![allow(unused_variables)]
|
||||||
# extern crate juniper;
|
# extern crate juniper;
|
||||||
# extern crate serde_json;
|
# extern crate serde_json;
|
||||||
use juniper::{EmptyMutation, EmptySubscription, FieldResult, IntrospectionFormat};
|
use juniper::{
|
||||||
|
graphql_object, EmptyMutation, EmptySubscription, FieldResult,
|
||||||
|
GraphQLObject, IntrospectionFormat,
|
||||||
|
};
|
||||||
|
|
||||||
// Define our schema.
|
// Define our schema.
|
||||||
|
|
||||||
#[derive(juniper::GraphQLObject)]
|
#[derive(GraphQLObject)]
|
||||||
struct Example {
|
struct Example {
|
||||||
id: String,
|
id: String,
|
||||||
}
|
}
|
||||||
|
@ -176,9 +179,7 @@ impl juniper::Context for Context {}
|
||||||
|
|
||||||
struct Query;
|
struct Query;
|
||||||
|
|
||||||
#[juniper::graphql_object(
|
#[graphql_object(context = Context)]
|
||||||
Context = Context,
|
|
||||||
)]
|
|
||||||
impl Query {
|
impl Query {
|
||||||
fn example(id: String) -> FieldResult<Example> {
|
fn example(id: String) -> FieldResult<Example> {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
|
|
|
@ -144,9 +144,10 @@ interfaces.</p>
|
||||||
<p>Using <code>Result</code>-like enums can be a useful way of reporting e.g. validation
|
<p>Using <code>Result</code>-like enums can be a useful way of reporting e.g. validation
|
||||||
errors from a mutation:</p>
|
errors from a mutation:</p>
|
||||||
<pre><pre class="playpen"><code class="language-rust"># extern crate juniper;
|
<pre><pre class="playpen"><code class="language-rust"># extern crate juniper;
|
||||||
|
# use juniper::{graphql_object, GraphQLObject};
|
||||||
# #[derive(juniper::GraphQLObject)] struct User { name: String }
|
# #[derive(juniper::GraphQLObject)] struct User { name: String }
|
||||||
|
#
|
||||||
#[derive(juniper::GraphQLObject)]
|
#[derive(GraphQLObject)]
|
||||||
struct ValidationError {
|
struct ValidationError {
|
||||||
field: String,
|
field: String,
|
||||||
message: String,
|
message: String,
|
||||||
|
@ -158,7 +159,7 @@ enum SignUpResult {
|
||||||
Error(Vec<ValidationError>),
|
Error(Vec<ValidationError>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[juniper::graphql_object]
|
#[graphql_object]
|
||||||
impl SignUpResult {
|
impl SignUpResult {
|
||||||
fn user(&self) -> Option<&User> {
|
fn user(&self) -> Option<&User> {
|
||||||
match *self {
|
match *self {
|
||||||
|
@ -174,7 +175,7 @@ impl SignUpResult {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#
|
||||||
# fn main() {}
|
# fn main() {}
|
||||||
</code></pre></pre>
|
</code></pre></pre>
|
||||||
<p>Here, we use an enum to decide whether a user's input data was valid or not, and
|
<p>Here, we use an enum to decide whether a user's input data was valid or not, and
|
||||||
|
|
|
@ -159,7 +159,7 @@ sequentially:</p>
|
||||||
# extern crate juniper;
|
# extern crate juniper;
|
||||||
# extern crate juniper_subscriptions;
|
# extern crate juniper_subscriptions;
|
||||||
# extern crate tokio;
|
# extern crate tokio;
|
||||||
# use juniper::FieldError;
|
# use juniper::{graphql_object, graphql_subscription, FieldError};
|
||||||
# use futures::Stream;
|
# use futures::Stream;
|
||||||
# use std::pin::Pin;
|
# use std::pin::Pin;
|
||||||
#
|
#
|
||||||
|
@ -168,7 +168,7 @@ sequentially:</p>
|
||||||
# impl juniper::Context for Database {}
|
# impl juniper::Context for Database {}
|
||||||
|
|
||||||
# pub struct Query;
|
# pub struct Query;
|
||||||
# #[juniper::graphql_object(Context = Database)]
|
# #[graphql_object(context = Database)]
|
||||||
# impl Query {
|
# impl Query {
|
||||||
# fn hello_world() -> &str {
|
# fn hello_world() -> &str {
|
||||||
# "Hello World!"
|
# "Hello World!"
|
||||||
|
@ -178,7 +178,7 @@ pub struct Subscription;
|
||||||
|
|
||||||
type StringStream = Pin<Box<dyn Stream<Item = Result<String, FieldError>> + Send>>;
|
type StringStream = Pin<Box<dyn Stream<Item = Result<String, FieldError>> + Send>>;
|
||||||
|
|
||||||
#[juniper::graphql_subscription(Context = Database)]
|
#[graphql_subscription(context = Database)]
|
||||||
impl Subscription {
|
impl Subscription {
|
||||||
async fn hello_world() -> StringStream {
|
async fn hello_world() -> StringStream {
|
||||||
let stream = tokio::stream::iter(vec![
|
let stream = tokio::stream::iter(vec![
|
||||||
|
@ -188,6 +188,7 @@ impl Subscription {
|
||||||
Box::pin(stream)
|
Box::pin(stream)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#
|
||||||
# fn main () {}
|
# fn main () {}
|
||||||
</code></pre></pre>
|
</code></pre></pre>
|
||||||
<a class="header" href="#coordinator" id="coordinator"><h3>Coordinator</h3></a>
|
<a class="header" href="#coordinator" id="coordinator"><h3>Coordinator</h3></a>
|
||||||
|
@ -205,8 +206,12 @@ where [<code>Connection</code>][Connection] is a <code>Stream</code> of values r
|
||||||
# extern crate juniper_subscriptions;
|
# extern crate juniper_subscriptions;
|
||||||
# extern crate serde_json;
|
# extern crate serde_json;
|
||||||
# extern crate tokio;
|
# extern crate tokio;
|
||||||
# use juniper::http::GraphQLRequest;
|
# use juniper::{
|
||||||
# use juniper::{DefaultScalarValue, EmptyMutation, FieldError, RootNode, SubscriptionCoordinator};
|
# http::GraphQLRequest,
|
||||||
|
# graphql_object, graphql_subscription,
|
||||||
|
# DefaultScalarValue, EmptyMutation, FieldError,
|
||||||
|
# RootNode, SubscriptionCoordinator,
|
||||||
|
# };
|
||||||
# use juniper_subscriptions::Coordinator;
|
# use juniper_subscriptions::Coordinator;
|
||||||
# use futures::{Stream, StreamExt};
|
# use futures::{Stream, StreamExt};
|
||||||
# use std::pin::Pin;
|
# use std::pin::Pin;
|
||||||
|
@ -224,7 +229,7 @@ where [<code>Connection</code>][Connection] is a <code>Stream</code> of values r
|
||||||
#
|
#
|
||||||
# pub struct Query;
|
# pub struct Query;
|
||||||
#
|
#
|
||||||
# #[juniper::graphql_object(Context = Database)]
|
# #[graphql_object(context = Database)]
|
||||||
# impl Query {
|
# impl Query {
|
||||||
# fn hello_world() -> &str {
|
# fn hello_world() -> &str {
|
||||||
# "Hello World!"
|
# "Hello World!"
|
||||||
|
@ -235,7 +240,7 @@ where [<code>Connection</code>][Connection] is a <code>Stream</code> of values r
|
||||||
#
|
#
|
||||||
# type StringStream = Pin<Box<dyn Stream<Item = Result<String, FieldError>> + Send>>;
|
# type StringStream = Pin<Box<dyn Stream<Item = Result<String, FieldError>> + Send>>;
|
||||||
#
|
#
|
||||||
# #[juniper::graphql_subscription(Context = Database)]
|
# #[graphql_subscription(context = Database)]
|
||||||
# impl Subscription {
|
# impl Subscription {
|
||||||
# async fn hello_world() -> StringStream {
|
# async fn hello_world() -> StringStream {
|
||||||
# let stream =
|
# let stream =
|
||||||
|
@ -253,11 +258,9 @@ async fn run_subscription() {
|
||||||
let schema = schema();
|
let schema = schema();
|
||||||
let coordinator = Coordinator::new(schema);
|
let coordinator = Coordinator::new(schema);
|
||||||
let req: GraphQLRequest<DefaultScalarValue> = serde_json::from_str(
|
let req: GraphQLRequest<DefaultScalarValue> = serde_json::from_str(
|
||||||
r#"
|
r#"{
|
||||||
{
|
|
||||||
"query": "subscription { helloWorld }"
|
"query": "subscription { helloWorld }"
|
||||||
}
|
}"#,
|
||||||
"#,
|
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let ctx = Database::new();
|
let ctx = Database::new();
|
||||||
|
@ -266,7 +269,7 @@ async fn run_subscription() {
|
||||||
println!("{}", serde_json::to_string(&result).unwrap());
|
println!("{}", serde_json::to_string(&result).unwrap());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#
|
||||||
# fn main() { }
|
# fn main() { }
|
||||||
</code></pre></pre>
|
</code></pre></pre>
|
||||||
<a class="header" href="#web-integration-and-examples" id="web-integration-and-examples"><h3>Web Integration and Examples</h3></a>
|
<a class="header" href="#web-integration-and-examples" id="web-integration-and-examples"><h3>Web Integration and Examples</h3></a>
|
||||||
|
|
|
@ -200,8 +200,12 @@ types to a GraphQL schema. The most important one is the
|
||||||
resolvers, which you will use for the <code>Query</code> and <code>Mutation</code> roots.</p>
|
resolvers, which you will use for the <code>Query</code> and <code>Mutation</code> roots.</p>
|
||||||
<pre><pre class="playpen"><code class="language-rust"># #![allow(unused_variables)]
|
<pre><pre class="playpen"><code class="language-rust"># #![allow(unused_variables)]
|
||||||
# extern crate juniper;
|
# extern crate juniper;
|
||||||
use juniper::{FieldResult, EmptySubscription};
|
# use std::fmt::Display;
|
||||||
|
use juniper::{
|
||||||
|
graphql_object, EmptySubscription, FieldResult, GraphQLEnum,
|
||||||
|
GraphQLInputObject, GraphQLObject, ScalarValue,
|
||||||
|
};
|
||||||
|
#
|
||||||
# struct DatabasePool;
|
# struct DatabasePool;
|
||||||
# impl DatabasePool {
|
# impl DatabasePool {
|
||||||
# fn get_connection(&self) -> FieldResult<DatabasePool> { Ok(DatabasePool) }
|
# fn get_connection(&self) -> FieldResult<DatabasePool> { Ok(DatabasePool) }
|
||||||
|
@ -209,14 +213,14 @@ use juniper::{FieldResult, EmptySubscription};
|
||||||
# fn insert_human(&self, _human: &NewHuman) -> FieldResult<Human> { Err("")? }
|
# fn insert_human(&self, _human: &NewHuman) -> FieldResult<Human> { Err("")? }
|
||||||
# }
|
# }
|
||||||
|
|
||||||
#[derive(juniper::GraphQLEnum)]
|
#[derive(GraphQLEnum)]
|
||||||
enum Episode {
|
enum Episode {
|
||||||
NewHope,
|
NewHope,
|
||||||
Empire,
|
Empire,
|
||||||
Jedi,
|
Jedi,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(juniper::GraphQLObject)]
|
#[derive(GraphQLObject)]
|
||||||
#[graphql(description = "A humanoid creature in the Star Wars universe")]
|
#[graphql(description = "A humanoid creature in the Star Wars universe")]
|
||||||
struct Human {
|
struct Human {
|
||||||
id: String,
|
id: String,
|
||||||
|
@ -227,7 +231,7 @@ struct Human {
|
||||||
|
|
||||||
// There is also a custom derive for mapping GraphQL input objects.
|
// There is also a custom derive for mapping GraphQL input objects.
|
||||||
|
|
||||||
#[derive(juniper::GraphQLInputObject)]
|
#[derive(GraphQLInputObject)]
|
||||||
#[graphql(description = "A humanoid creature in the Star Wars universe")]
|
#[graphql(description = "A humanoid creature in the Star Wars universe")]
|
||||||
struct NewHuman {
|
struct NewHuman {
|
||||||
name: String,
|
name: String,
|
||||||
|
@ -250,14 +254,13 @@ impl juniper::Context for Context {}
|
||||||
|
|
||||||
struct Query;
|
struct Query;
|
||||||
|
|
||||||
#[juniper::graphql_object(
|
#[graphql_object(
|
||||||
// Here we specify the context type for the object.
|
// Here we specify the context type for the object.
|
||||||
// We need to do this in every type that
|
// We need to do this in every type that
|
||||||
// needs access to the context.
|
// needs access to the context.
|
||||||
Context = Context,
|
context = Context,
|
||||||
)]
|
)]
|
||||||
impl Query {
|
impl Query {
|
||||||
|
|
||||||
fn apiVersion() -> &str {
|
fn apiVersion() -> &str {
|
||||||
"1.0"
|
"1.0"
|
||||||
}
|
}
|
||||||
|
@ -281,14 +284,18 @@ impl Query {
|
||||||
|
|
||||||
struct Mutation;
|
struct Mutation;
|
||||||
|
|
||||||
#[juniper::graphql_object(
|
#[graphql_object(
|
||||||
Context = Context,
|
context = Context,
|
||||||
)]
|
|
||||||
impl Mutation {
|
|
||||||
|
|
||||||
fn createHuman(context: &Context, new_human: NewHuman) -> FieldResult<Human> {
|
// If we need to use `ScalarValue` parametrization explicitly somewhere
|
||||||
let db = context.pool.get_connection()?;
|
// in the object definition (like here in `FieldResult`), we should
|
||||||
let human: Human = db.insert_human(&new_human)?;
|
// declare an explicit type parameter for that, and specify it.
|
||||||
|
scalar = S,
|
||||||
|
)]
|
||||||
|
impl<S: ScalarValue + Display> Mutation {
|
||||||
|
fn createHuman(context: &Context, new_human: NewHuman) -> FieldResult<Human, S> {
|
||||||
|
let db = context.pool.get_connection().map_err(|e| e.map_scalar_value())?;
|
||||||
|
let human: Human = db.insert_human(&new_human).map_err(|e| e.map_scalar_value())?;
|
||||||
Ok(human)
|
Ok(human)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -296,7 +303,7 @@ impl Mutation {
|
||||||
// A root schema consists of a query, a mutation, and a subscription.
|
// A root schema consists of a query, a mutation, and a subscription.
|
||||||
// Request queries can be executed against a RootNode.
|
// Request queries can be executed against a RootNode.
|
||||||
type Schema = juniper::RootNode<'static, Query, Mutation, EmptySubscription<Context>>;
|
type Schema = juniper::RootNode<'static, Query, Mutation, EmptySubscription<Context>>;
|
||||||
|
#
|
||||||
# fn main() {
|
# fn main() {
|
||||||
# let _ = Schema::new(Query, Mutation{}, EmptySubscription::new());
|
# let _ = Schema::new(Query, Mutation{}, EmptySubscription::new());
|
||||||
# }
|
# }
|
||||||
|
@ -308,10 +315,12 @@ type Schema = juniper::RootNode<'static, Query, Mutation, EmptySubscription&l
|
||||||
<p>You can invoke <code>juniper::execute</code> directly to run a GraphQL query:</p>
|
<p>You can invoke <code>juniper::execute</code> directly to run a GraphQL query:</p>
|
||||||
<pre><pre class="playpen"><code class="language-rust"># // Only needed due to 2018 edition because the macro is not accessible.
|
<pre><pre class="playpen"><code class="language-rust"># // Only needed due to 2018 edition because the macro is not accessible.
|
||||||
# #[macro_use] extern crate juniper;
|
# #[macro_use] extern crate juniper;
|
||||||
use juniper::{FieldResult, Variables, EmptyMutation, EmptySubscription};
|
use juniper::{
|
||||||
|
graphql_object, EmptyMutation, EmptySubscription, FieldResult,
|
||||||
|
GraphQLEnum, Variables,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(GraphQLEnum, Clone, Copy)]
|
||||||
#[derive(juniper::GraphQLEnum, Clone, Copy)]
|
|
||||||
enum Episode {
|
enum Episode {
|
||||||
NewHope,
|
NewHope,
|
||||||
Empire,
|
Empire,
|
||||||
|
@ -325,16 +334,13 @@ impl juniper::Context for Ctx {}
|
||||||
|
|
||||||
struct Query;
|
struct Query;
|
||||||
|
|
||||||
#[juniper::graphql_object(
|
#[graphql_object(context = Ctx)]
|
||||||
Context = Ctx,
|
|
||||||
)]
|
|
||||||
impl Query {
|
impl Query {
|
||||||
fn favoriteEpisode(context: &Ctx) -> FieldResult<Episode> {
|
fn favoriteEpisode(context: &Ctx) -> FieldResult<Episode> {
|
||||||
Ok(context.0)
|
Ok(context.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// A root schema consists of a query, a mutation, and a subscription.
|
// A root schema consists of a query, a mutation, and a subscription.
|
||||||
// Request queries can be executed against a RootNode.
|
// Request queries can be executed against a RootNode.
|
||||||
type Schema = juniper::RootNode<'static, Query, EmptyMutation<Ctx>, EmptySubscription<Ctx>>;
|
type Schema = juniper::RootNode<'static, Query, EmptyMutation<Ctx>, EmptySubscription<Ctx>>;
|
||||||
|
@ -393,12 +399,13 @@ struct you want to expose, the easiest way is to use the custom derive
|
||||||
attribute. The other way is described in the <a href="complex_fields.html">Complex fields</a>
|
attribute. The other way is described in the <a href="complex_fields.html">Complex fields</a>
|
||||||
chapter.</p>
|
chapter.</p>
|
||||||
<pre><pre class="playpen"><code class="language-rust"># extern crate juniper;
|
<pre><pre class="playpen"><code class="language-rust"># extern crate juniper;
|
||||||
#[derive(juniper::GraphQLObject)]
|
# use juniper::GraphQLObject;
|
||||||
|
#[derive(GraphQLObject)]
|
||||||
struct Person {
|
struct Person {
|
||||||
name: String,
|
name: String,
|
||||||
age: i32,
|
age: i32,
|
||||||
}
|
}
|
||||||
|
#
|
||||||
# fn main() {}
|
# fn main() {}
|
||||||
</code></pre></pre>
|
</code></pre></pre>
|
||||||
<p>This will create a GraphQL object type called <code>Person</code>, with two fields: <code>name</code>
|
<p>This will create a GraphQL object type called <code>Person</code>, with two fields: <code>name</code>
|
||||||
|
@ -411,7 +418,8 @@ fields. Juniper will automatically use associated doc comments as GraphQL
|
||||||
descriptions:</p>
|
descriptions:</p>
|
||||||
<p>!FILENAME GraphQL descriptions via Rust doc comments</p>
|
<p>!FILENAME GraphQL descriptions via Rust doc comments</p>
|
||||||
<pre><pre class="playpen"><code class="language-rust"># extern crate juniper;
|
<pre><pre class="playpen"><code class="language-rust"># extern crate juniper;
|
||||||
#[derive(juniper::GraphQLObject)]
|
# use juniper::GraphQLObject;
|
||||||
|
#[derive(GraphQLObject)]
|
||||||
/// Information about a person
|
/// Information about a person
|
||||||
struct Person {
|
struct Person {
|
||||||
/// The person's full name, including both first and last names
|
/// The person's full name, including both first and last names
|
||||||
|
@ -419,14 +427,15 @@ struct Person {
|
||||||
/// The person's age in years, rounded down
|
/// The person's age in years, rounded down
|
||||||
age: i32,
|
age: i32,
|
||||||
}
|
}
|
||||||
|
#
|
||||||
# fn main() {}
|
# fn main() {}
|
||||||
</code></pre></pre>
|
</code></pre></pre>
|
||||||
<p>Objects and fields without doc comments can instead set a <code>description</code>
|
<p>Objects and fields without doc comments can instead set a <code>description</code>
|
||||||
via the <code>graphql</code> attribute. The following example is equivalent to the above:</p>
|
via the <code>graphql</code> attribute. The following example is equivalent to the above:</p>
|
||||||
<p>!FILENAME GraphQL descriptions via attribute</p>
|
<p>!FILENAME GraphQL descriptions via attribute</p>
|
||||||
<pre><pre class="playpen"><code class="language-rust"># extern crate juniper;
|
<pre><pre class="playpen"><code class="language-rust"># extern crate juniper;
|
||||||
#[derive(juniper::GraphQLObject)]
|
# use juniper::GraphQLObject;
|
||||||
|
#[derive(GraphQLObject)]
|
||||||
#[graphql(description = "Information about a person")]
|
#[graphql(description = "Information about a person")]
|
||||||
struct Person {
|
struct Person {
|
||||||
#[graphql(description = "The person's full name, including both first and last names")]
|
#[graphql(description = "The person's full name, including both first and last names")]
|
||||||
|
@ -434,14 +443,15 @@ struct Person {
|
||||||
#[graphql(description = "The person's age in years, rounded down")]
|
#[graphql(description = "The person's age in years, rounded down")]
|
||||||
age: i32,
|
age: i32,
|
||||||
}
|
}
|
||||||
|
#
|
||||||
# fn main() {}
|
# fn main() {}
|
||||||
</code></pre></pre>
|
</code></pre></pre>
|
||||||
<p>Descriptions set via the <code>graphql</code> attribute take precedence over Rust
|
<p>Descriptions set via the <code>graphql</code> attribute take precedence over Rust
|
||||||
doc comments. This enables internal Rust documentation and external GraphQL
|
doc comments. This enables internal Rust documentation and external GraphQL
|
||||||
documentation to differ:</p>
|
documentation to differ:</p>
|
||||||
<pre><pre class="playpen"><code class="language-rust"># extern crate juniper;
|
<pre><pre class="playpen"><code class="language-rust"># extern crate juniper;
|
||||||
#[derive(juniper::GraphQLObject)]
|
# use juniper::GraphQLObject;
|
||||||
|
#[derive(GraphQLObject)]
|
||||||
#[graphql(description = "This description shows up in GraphQL")]
|
#[graphql(description = "This description shows up in GraphQL")]
|
||||||
/// This description shows up in RustDoc
|
/// This description shows up in RustDoc
|
||||||
struct Person {
|
struct Person {
|
||||||
|
@ -451,7 +461,7 @@ struct Person {
|
||||||
/// This description shows up in both RustDoc and GraphQL
|
/// This description shows up in both RustDoc and GraphQL
|
||||||
age: i32,
|
age: i32,
|
||||||
}
|
}
|
||||||
|
#
|
||||||
# fn main() {}
|
# fn main() {}
|
||||||
</code></pre></pre>
|
</code></pre></pre>
|
||||||
<a class="header" href="#relationships" id="relationships"><h2>Relationships</h2></a>
|
<a class="header" href="#relationships" id="relationships"><h2>Relationships</h2></a>
|
||||||
|
@ -470,18 +480,19 @@ or</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>Let's see what that means for building relationships between objects:</p>
|
<p>Let's see what that means for building relationships between objects:</p>
|
||||||
<pre><pre class="playpen"><code class="language-rust"># extern crate juniper;
|
<pre><pre class="playpen"><code class="language-rust"># extern crate juniper;
|
||||||
#[derive(juniper::GraphQLObject)]
|
# use juniper::GraphQLObject;
|
||||||
|
#[derive(GraphQLObject)]
|
||||||
struct Person {
|
struct Person {
|
||||||
name: String,
|
name: String,
|
||||||
age: i32,
|
age: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(juniper::GraphQLObject)]
|
#[derive(GraphQLObject)]
|
||||||
struct House {
|
struct House {
|
||||||
address: Option<String>, // Converted into String (nullable)
|
address: Option<String>, // Converted into String (nullable)
|
||||||
inhabitants: Vec<Person>, // Converted into [Person!]!
|
inhabitants: Vec<Person>, // Converted into [Person!]!
|
||||||
}
|
}
|
||||||
|
#
|
||||||
# fn main() {}
|
# fn main() {}
|
||||||
</code></pre></pre>
|
</code></pre></pre>
|
||||||
<p>Because <code>Person</code> is a valid GraphQL type, you can have a <code>Vec<Person></code> in a
|
<p>Because <code>Person</code> is a valid GraphQL type, you can have a <code>Vec<Person></code> in a
|
||||||
|
@ -491,39 +502,42 @@ objects.</p>
|
||||||
<p>By default, struct fields are converted from Rust's standard <code>snake_case</code> naming
|
<p>By default, struct fields are converted from Rust's standard <code>snake_case</code> naming
|
||||||
convention into GraphQL's <code>camelCase</code> convention:</p>
|
convention into GraphQL's <code>camelCase</code> convention:</p>
|
||||||
<pre><pre class="playpen"><code class="language-rust"># extern crate juniper;
|
<pre><pre class="playpen"><code class="language-rust"># extern crate juniper;
|
||||||
#[derive(juniper::GraphQLObject)]
|
# use juniper::GraphQLObject;
|
||||||
|
#[derive(GraphQLObject)]
|
||||||
struct Person {
|
struct Person {
|
||||||
first_name: String, // Would be exposed as firstName in the GraphQL schema
|
first_name: String, // Would be exposed as firstName in the GraphQL schema
|
||||||
last_name: String, // Exposed as lastName
|
last_name: String, // Exposed as lastName
|
||||||
}
|
}
|
||||||
|
#
|
||||||
# fn main() {}
|
# fn main() {}
|
||||||
</code></pre></pre>
|
</code></pre></pre>
|
||||||
<p>You can override the name by using the <code>graphql</code> attribute on individual struct
|
<p>You can override the name by using the <code>graphql</code> attribute on individual struct
|
||||||
fields:</p>
|
fields:</p>
|
||||||
<pre><pre class="playpen"><code class="language-rust"># extern crate juniper;
|
<pre><pre class="playpen"><code class="language-rust"># extern crate juniper;
|
||||||
#[derive(juniper::GraphQLObject)]
|
# use juniper::GraphQLObject;
|
||||||
|
#[derive(GraphQLObject)]
|
||||||
struct Person {
|
struct Person {
|
||||||
name: String,
|
name: String,
|
||||||
age: i32,
|
age: i32,
|
||||||
#[graphql(name = "websiteURL")]
|
#[graphql(name = "websiteURL")]
|
||||||
website_url: Option<String>, // Now exposed as websiteURL in the schema
|
website_url: Option<String>, // Now exposed as websiteURL in the schema
|
||||||
}
|
}
|
||||||
|
#
|
||||||
# fn main() {}
|
# fn main() {}
|
||||||
</code></pre></pre>
|
</code></pre></pre>
|
||||||
<a class="header" href="#deprecating-fields" id="deprecating-fields"><h2>Deprecating fields</h2></a>
|
<a class="header" href="#deprecating-fields" id="deprecating-fields"><h2>Deprecating fields</h2></a>
|
||||||
<p>To deprecate a field, you specify a deprecation reason using the <code>graphql</code>
|
<p>To deprecate a field, you specify a deprecation reason using the <code>graphql</code>
|
||||||
attribute:</p>
|
attribute:</p>
|
||||||
<pre><pre class="playpen"><code class="language-rust"># extern crate juniper;
|
<pre><pre class="playpen"><code class="language-rust"># extern crate juniper;
|
||||||
#[derive(juniper::GraphQLObject)]
|
# use juniper::GraphQLObject;
|
||||||
|
#[derive(GraphQLObject)]
|
||||||
struct Person {
|
struct Person {
|
||||||
name: String,
|
name: String,
|
||||||
age: i32,
|
age: i32,
|
||||||
#[graphql(deprecated = "Please use the name field instead")]
|
#[graphql(deprecated = "Please use the name field instead")]
|
||||||
first_name: String,
|
first_name: String,
|
||||||
}
|
}
|
||||||
|
#
|
||||||
# fn main() {}
|
# fn main() {}
|
||||||
</code></pre></pre>
|
</code></pre></pre>
|
||||||
<p>The <code>name</code>, <code>description</code>, and <code>deprecation</code> arguments can of course be
|
<p>The <code>name</code>, <code>description</code>, and <code>deprecation</code> arguments can of course be
|
||||||
|
@ -532,7 +546,8 @@ only deprecate object fields and enum values.</p>
|
||||||
<a class="header" href="#skipping-fields" id="skipping-fields"><h2>Skipping fields</h2></a>
|
<a class="header" href="#skipping-fields" id="skipping-fields"><h2>Skipping fields</h2></a>
|
||||||
<p>By default all fields in a <code>GraphQLObject</code> are included in the generated GraphQL type. To prevent including a specific field, annotate the field with <code>#[graphql(skip)]</code>:</p>
|
<p>By default all fields in a <code>GraphQLObject</code> are included in the generated GraphQL type. To prevent including a specific field, annotate the field with <code>#[graphql(skip)]</code>:</p>
|
||||||
<pre><pre class="playpen"><code class="language-rust"># extern crate juniper;
|
<pre><pre class="playpen"><code class="language-rust"># extern crate juniper;
|
||||||
#[derive(juniper::GraphQLObject)]
|
# use juniper::GraphQLObject;
|
||||||
|
#[derive(GraphQLObject)]
|
||||||
struct Person {
|
struct Person {
|
||||||
name: String,
|
name: String,
|
||||||
age: i32,
|
age: i32,
|
||||||
|
@ -540,13 +555,13 @@ struct Person {
|
||||||
# #[allow(dead_code)]
|
# #[allow(dead_code)]
|
||||||
password_hash: String, // This cannot be queried or modified from GraphQL
|
password_hash: String, // This cannot be queried or modified from GraphQL
|
||||||
}
|
}
|
||||||
|
#
|
||||||
# fn main() {}
|
# fn main() {}
|
||||||
</code></pre></pre>
|
</code></pre></pre>
|
||||||
<a class="header" href="#complex-fields" id="complex-fields"><h1>Complex fields</h1></a>
|
<a class="header" href="#complex-fields" id="complex-fields"><h1>Complex fields</h1></a>
|
||||||
<p>If you've got a struct that can't be mapped directly to GraphQL, that contains
|
<p>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:
|
computed fields or circular structures, you have to use a more powerful tool:
|
||||||
the <code>object</code> procedural macro. This macro lets you define GraphQL object
|
the <code>#[graphql_object]</code> procedural macro. This macro lets you define GraphQL object
|
||||||
fields in a Rust <code>impl</code> block for a type. Note that only GraphQL fields
|
fields in a Rust <code>impl</code> block for a type. Note that only GraphQL fields
|
||||||
can be specified in this <code>impl</code> block. If you want to define normal methods on the struct,
|
can be specified in this <code>impl</code> block. If you want to define normal methods on the struct,
|
||||||
you have to do so in a separate, normal <code>impl</code> block. Continuing with the
|
you have to do so in a separate, normal <code>impl</code> block. Continuing with the
|
||||||
|
@ -554,13 +569,14 @@ example from the last chapter, this is how you would define <code>Person</code>
|
||||||
macro:</p>
|
macro:</p>
|
||||||
<pre><pre class="playpen"><code class="language-rust"># #![allow(dead_code)]
|
<pre><pre class="playpen"><code class="language-rust"># #![allow(dead_code)]
|
||||||
# extern crate juniper;
|
# extern crate juniper;
|
||||||
|
# use juniper::graphql_object;
|
||||||
#
|
#
|
||||||
struct Person {
|
struct Person {
|
||||||
name: String,
|
name: String,
|
||||||
age: i32,
|
age: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[juniper::graphql_object]
|
#[graphql_object]
|
||||||
impl Person {
|
impl Person {
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
self.name.as_str()
|
self.name.as_str()
|
||||||
|
@ -578,13 +594,15 @@ impl Person {
|
||||||
// [...]
|
// [...]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#
|
||||||
# fn main() { }
|
# fn main() { }
|
||||||
</code></pre></pre>
|
</code></pre></pre>
|
||||||
<p>While this is a bit more verbose, it lets you write any kind of function in the
|
<p>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:</p>
|
field resolver. With this syntax, fields can also take arguments:</p>
|
||||||
<pre><pre class="playpen"><code class="language-rust"># extern crate juniper;
|
<pre><pre class="playpen"><code class="language-rust"># extern crate juniper;
|
||||||
#[derive(juniper::GraphQLObject)]
|
# use juniper::{graphql_object, GraphQLObject};
|
||||||
|
#
|
||||||
|
#[derive(GraphQLObject)]
|
||||||
struct Person {
|
struct Person {
|
||||||
name: String,
|
name: String,
|
||||||
age: i32,
|
age: i32,
|
||||||
|
@ -594,14 +612,14 @@ struct House {
|
||||||
inhabitants: Vec<Person>,
|
inhabitants: Vec<Person>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[juniper::graphql_object]
|
#[graphql_object]
|
||||||
impl House {
|
impl House {
|
||||||
// Creates the field inhabitantWithName(name), returning a nullable person
|
// Creates the field inhabitantWithName(name), returning a nullable person
|
||||||
fn inhabitant_with_name(&self, name: String) -> Option<&Person> {
|
fn inhabitant_with_name(&self, name: String) -> Option<&Person> {
|
||||||
self.inhabitants.iter().find(|p| p.name == name)
|
self.inhabitants.iter().find(|p| p.name == name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#
|
||||||
# fn main() {}
|
# fn main() {}
|
||||||
</code></pre></pre>
|
</code></pre></pre>
|
||||||
<p>To access global data such as database connections or authentication
|
<p>To access global data such as database connections or authentication
|
||||||
|
@ -612,20 +630,20 @@ chapter: <a href="using_contexts.html">Using contexts</a>.</p>
|
||||||
to <code>camelCase</code>. If you need to override the conversion, you can simply rename
|
to <code>camelCase</code>. If you need to override the conversion, you can simply rename
|
||||||
the field. Also, the type name can be changed with an alias:</p>
|
the field. Also, the type name can be changed with an alias:</p>
|
||||||
<pre><pre class="playpen"><code class="language-rust"># extern crate juniper;
|
<pre><pre class="playpen"><code class="language-rust"># extern crate juniper;
|
||||||
|
# use juniper::graphql_object;
|
||||||
struct Person {
|
#
|
||||||
}
|
struct Person;
|
||||||
|
|
||||||
/// Doc comments are used as descriptions for GraphQL.
|
/// Doc comments are used as descriptions for GraphQL.
|
||||||
#[juniper::graphql_object(
|
#[graphql_object(
|
||||||
// With this attribute you can change the public GraphQL name of the type.
|
// With this attribute you can change the public GraphQL name of the type.
|
||||||
name = "PersonObject",
|
name = "PersonObject",
|
||||||
|
|
||||||
// You can also specify a description here, which will overwrite
|
// You can also specify a description here, which will overwrite
|
||||||
// a doc comment description.
|
// a doc comment description.
|
||||||
description = "...",
|
description = "...",
|
||||||
)]
|
)]
|
||||||
impl Person {
|
impl Person {
|
||||||
|
|
||||||
/// A doc comment on the field will also be used for GraphQL.
|
/// A doc comment on the field will also be used for GraphQL.
|
||||||
#[graphql(
|
#[graphql(
|
||||||
// Or provide a description here.
|
// Or provide a description here.
|
||||||
|
@ -636,9 +654,7 @@ impl Person {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fields can also be renamed if required.
|
// Fields can also be renamed if required.
|
||||||
#[graphql(
|
#[graphql(name = "myCustomFieldName")]
|
||||||
name = "myCustomFieldName",
|
|
||||||
)]
|
|
||||||
fn renamed_field() -> bool {
|
fn renamed_field() -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
@ -655,7 +671,7 @@ impl Person {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#
|
||||||
# fn main() { }
|
# fn main() { }
|
||||||
</code></pre></pre>
|
</code></pre></pre>
|
||||||
<a class="header" href="#customizing-arguments" id="customizing-arguments"><h2>Customizing arguments</h2></a>
|
<a class="header" href="#customizing-arguments" id="customizing-arguments"><h2>Customizing arguments</h2></a>
|
||||||
|
@ -664,10 +680,11 @@ impl Person {
|
||||||
<p><strong>Note</strong>: The syntax for this is currently a little awkward.
|
<p><strong>Note</strong>: The syntax for this is currently a little awkward.
|
||||||
This will become better once the <a href="https://github.com/rust-lang/rust/issues/60406">Rust RFC 2565</a> is implemented.</p>
|
This will become better once the <a href="https://github.com/rust-lang/rust/issues/60406">Rust RFC 2565</a> is implemented.</p>
|
||||||
<pre><pre class="playpen"><code class="language-rust"># extern crate juniper;
|
<pre><pre class="playpen"><code class="language-rust"># extern crate juniper;
|
||||||
|
# use juniper::graphql_object;
|
||||||
|
#
|
||||||
struct Person {}
|
struct Person {}
|
||||||
|
|
||||||
#[juniper::graphql_object]
|
#[graphql_object]
|
||||||
impl Person {
|
impl Person {
|
||||||
#[graphql(
|
#[graphql(
|
||||||
arguments(
|
arguments(
|
||||||
|
@ -687,7 +704,7 @@ impl Person {
|
||||||
format!("{} {}", arg1, arg2)
|
format!("{} {}", arg1, arg2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#
|
||||||
# fn main() { }
|
# fn main() { }
|
||||||
</code></pre></pre>
|
</code></pre></pre>
|
||||||
<a class="header" href="#more-features" id="more-features"><h2>More features</h2></a>
|
<a class="header" href="#more-features" id="more-features"><h2>More features</h2></a>
|
||||||
|
@ -697,7 +714,7 @@ impl Person {
|
||||||
<li>Per-argument default values</li>
|
<li>Per-argument default values</li>
|
||||||
<li>Per-argument descriptions</li>
|
<li>Per-argument descriptions</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>These, and more features, are described more thorougly in <a href="https://docs.rs/juniper/latest/juniper/macro.object.html">the reference
|
<p>These, and more features, are described more thoroughly in <a href="https://docs.rs/juniper/latest/juniper/macro.object.html">the reference
|
||||||
documentation</a>.</p>
|
documentation</a>.</p>
|
||||||
<a class="header" href="#using-contexts" id="using-contexts"><h1>Using contexts</h1></a>
|
<a class="header" href="#using-contexts" id="using-contexts"><h1>Using contexts</h1></a>
|
||||||
<p>The context type is a feature in Juniper that lets field resolvers access global
|
<p>The context type is a feature in Juniper that lets field resolvers access global
|
||||||
|
@ -710,7 +727,7 @@ integration.</p>
|
||||||
resolvers. Let's say that we have a simple user database in a <code>HashMap</code>:</p>
|
resolvers. Let's say that we have a simple user database in a <code>HashMap</code>:</p>
|
||||||
<pre><pre class="playpen"><code class="language-rust"># #![allow(dead_code)]
|
<pre><pre class="playpen"><code class="language-rust"># #![allow(dead_code)]
|
||||||
# use std::collections::HashMap;
|
# use std::collections::HashMap;
|
||||||
|
#
|
||||||
struct Database {
|
struct Database {
|
||||||
users: HashMap<i32, User>,
|
users: HashMap<i32, User>,
|
||||||
}
|
}
|
||||||
|
@ -720,7 +737,7 @@ struct User {
|
||||||
name: String,
|
name: String,
|
||||||
friend_ids: Vec<i32>,
|
friend_ids: Vec<i32>,
|
||||||
}
|
}
|
||||||
|
#
|
||||||
# fn main() { }
|
# fn main() { }
|
||||||
</code></pre></pre>
|
</code></pre></pre>
|
||||||
<p>We would like a <code>friends</code> field on <code>User</code> that returns a list of <code>User</code> objects.
|
<p>We would like a <code>friends</code> field on <code>User</code> that returns a list of <code>User</code> objects.
|
||||||
|
@ -729,9 +746,10 @@ In order to write such a field though, the database must be queried.</p>
|
||||||
the user object.</p>
|
the user object.</p>
|
||||||
<p>To gain access to the context, we need to specify an argument with the same
|
<p>To gain access to the context, we need to specify an argument with the same
|
||||||
type as the specified <code>Context</code> for the type:</p>
|
type as the specified <code>Context</code> for the type:</p>
|
||||||
<pre><pre class="playpen"><code class="language-rust"># use std::collections::HashMap;
|
<pre><pre class="playpen"><code class="language-rust"># extern crate juniper;
|
||||||
extern crate juniper;
|
# use std::collections::HashMap;
|
||||||
|
# use juniper::graphql_object;
|
||||||
|
#
|
||||||
// This struct represents our context.
|
// This struct represents our context.
|
||||||
struct Database {
|
struct Database {
|
||||||
users: HashMap<i32, User>,
|
users: HashMap<i32, User>,
|
||||||
|
@ -746,11 +764,8 @@ struct User {
|
||||||
friend_ids: Vec<i32>,
|
friend_ids: Vec<i32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Assign Database as the context type for User
|
// Assign Database as the context type for User
|
||||||
#[juniper::graphql_object(
|
#[graphql_object(context = Database)]
|
||||||
Context = Database,
|
|
||||||
)]
|
|
||||||
impl User {
|
impl User {
|
||||||
// 3. Inject the context by specifying an argument
|
// 3. Inject the context by specifying an argument
|
||||||
// with the context type.
|
// with the context type.
|
||||||
|
@ -773,7 +788,7 @@ impl User {
|
||||||
self.id
|
self.id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#
|
||||||
# fn main() { }
|
# fn main() { }
|
||||||
</code></pre></pre>
|
</code></pre></pre>
|
||||||
<p>You only get an immutable reference to the context, so if you want to affect
|
<p>You only get an immutable reference to the context, so if you want to affect
|
||||||
|
@ -804,13 +819,13 @@ use std::{
|
||||||
fs::{File},
|
fs::{File},
|
||||||
io::{Read},
|
io::{Read},
|
||||||
};
|
};
|
||||||
use juniper::FieldResult;
|
use juniper::{graphql_object, FieldResult};
|
||||||
|
|
||||||
struct Example {
|
struct Example {
|
||||||
filename: PathBuf,
|
filename: PathBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[juniper::graphql_object]
|
#[graphql_object]
|
||||||
impl Example {
|
impl Example {
|
||||||
fn contents() -> FieldResult<String> {
|
fn contents() -> FieldResult<String> {
|
||||||
let mut file = File::open(&self.filename)?;
|
let mut file = File::open(&self.filename)?;
|
||||||
|
@ -829,7 +844,7 @@ impl Example {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#
|
||||||
# fn main() {}
|
# fn main() {}
|
||||||
</code></pre></pre>
|
</code></pre></pre>
|
||||||
<p><code>FieldResult<T></code> is an alias for <code>Result<T, FieldError></code>, which is the error
|
<p><code>FieldResult<T></code> is an alias for <code>Result<T, FieldError></code>, which is the error
|
||||||
|
@ -889,14 +904,16 @@ following would be returned:</p>
|
||||||
<p>Sometimes it is desirable to return additional structured error information
|
<p>Sometimes it is desirable to return additional structured error information
|
||||||
to clients. This can be accomplished by implementing <a href="https://docs.rs/juniper/latest/juniper/trait.IntoFieldError.html"><code>IntoFieldError</code></a>:</p>
|
to clients. This can be accomplished by implementing <a href="https://docs.rs/juniper/latest/juniper/trait.IntoFieldError.html"><code>IntoFieldError</code></a>:</p>
|
||||||
<pre><pre class="playpen"><code class="language-rust"># #[macro_use] extern crate juniper;
|
<pre><pre class="playpen"><code class="language-rust"># #[macro_use] extern crate juniper;
|
||||||
|
# use juniper::{graphql_object, FieldError, IntoFieldError, ScalarValue};
|
||||||
|
#
|
||||||
enum CustomError {
|
enum CustomError {
|
||||||
WhateverNotSet,
|
WhateverNotSet,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl juniper::IntoFieldError for CustomError {
|
impl<S: ScalarValue> IntoFieldError<S> for CustomError {
|
||||||
fn into_field_error(self) -> juniper::FieldError {
|
fn into_field_error(self) -> FieldError<S> {
|
||||||
match self {
|
match self {
|
||||||
CustomError::WhateverNotSet => juniper::FieldError::new(
|
CustomError::WhateverNotSet => FieldError::new(
|
||||||
"Whatever does not exist",
|
"Whatever does not exist",
|
||||||
graphql_value!({
|
graphql_value!({
|
||||||
"type": "NO_WHATEVER"
|
"type": "NO_WHATEVER"
|
||||||
|
@ -910,7 +927,7 @@ struct Example {
|
||||||
whatever: Option<bool>,
|
whatever: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[juniper::graphql_object]
|
#[graphql_object]
|
||||||
impl Example {
|
impl Example {
|
||||||
fn whatever() -> Result<bool, CustomError> {
|
fn whatever() -> Result<bool, CustomError> {
|
||||||
if let Some(value) = self.whatever {
|
if let Some(value) = self.whatever {
|
||||||
|
@ -919,18 +936,18 @@ impl Example {
|
||||||
Err(CustomError::WhateverNotSet)
|
Err(CustomError::WhateverNotSet)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#
|
||||||
# fn main() {}
|
# fn main() {}
|
||||||
</code></pre></pre>
|
</code></pre></pre>
|
||||||
<p>The specified structured error information is included in the <a href="https://facebook.github.io/graphql/June2018/#sec-Errors"><code>extensions</code></a> key:</p>
|
<p>The specified structured error information is included in the <a href="https://facebook.github.io/graphql/June2018/#sec-Errors"><code>extensions</code></a> key:</p>
|
||||||
<pre><code class="language-js">{
|
<pre><code class="language-json">{
|
||||||
"errors": [
|
"errors": [{
|
||||||
"message": "Whatever does not exist",
|
"message": "Whatever does not exist",
|
||||||
"locations": [{ "line": 2, "column": 4 }]),
|
"locations": [{"line": 2, "column": 4}],
|
||||||
"extensions": {
|
"extensions": {
|
||||||
"type": "NO_WHATEVER"
|
"type": "NO_WHATEVER"
|
||||||
}
|
}
|
||||||
]
|
}]
|
||||||
}
|
}
|
||||||
</code></pre>
|
</code></pre>
|
||||||
<a class="header" href="#errors-backed-by-graphqls-schema" id="errors-backed-by-graphqls-schema"><h2>Errors Backed by GraphQL's Schema</h2></a>
|
<a class="header" href="#errors-backed-by-graphqls-schema" id="errors-backed-by-graphqls-schema"><h2>Errors Backed by GraphQL's Schema</h2></a>
|
||||||
|
@ -952,24 +969,26 @@ 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
|
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.</p>
|
possible to return a unique string identifier and have the client present a localized string to the user.</p>
|
||||||
<pre><pre class="playpen"><code class="language-rust"># extern crate juniper;
|
<pre><pre class="playpen"><code class="language-rust"># extern crate juniper;
|
||||||
#[derive(juniper::GraphQLObject)]
|
# use juniper::{graphql_object, GraphQLObject, GraphQLUnion};
|
||||||
|
#
|
||||||
|
#[derive(GraphQLObject)]
|
||||||
pub struct Item {
|
pub struct Item {
|
||||||
name: String,
|
name: String,
|
||||||
quantity: i32,
|
quantity: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(juniper::GraphQLObject)]
|
#[derive(GraphQLObject)]
|
||||||
pub struct ValidationError {
|
pub struct ValidationError {
|
||||||
field: String,
|
field: String,
|
||||||
message: String,
|
message: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(juniper::GraphQLObject)]
|
#[derive(GraphQLObject)]
|
||||||
pub struct ValidationErrors {
|
pub struct ValidationErrors {
|
||||||
errors: Vec<ValidationError>,
|
errors: Vec<ValidationError>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(juniper::GraphQLUnion)]
|
#[derive(GraphQLUnion)]
|
||||||
pub enum GraphQLResult {
|
pub enum GraphQLResult {
|
||||||
Ok(Item),
|
Ok(Item),
|
||||||
Err(ValidationErrors),
|
Err(ValidationErrors),
|
||||||
|
@ -977,7 +996,7 @@ pub enum GraphQLResult {
|
||||||
|
|
||||||
pub struct Mutation;
|
pub struct Mutation;
|
||||||
|
|
||||||
#[juniper::graphql_object]
|
#[graphql_object]
|
||||||
impl Mutation {
|
impl Mutation {
|
||||||
fn addItem(&self, name: String, quantity: i32) -> GraphQLResult {
|
fn addItem(&self, name: String, quantity: i32) -> GraphQLResult {
|
||||||
let mut errors = Vec::new();
|
let mut errors = Vec::new();
|
||||||
|
@ -1003,7 +1022,7 @@ impl Mutation {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#
|
||||||
# fn main() {}
|
# fn main() {}
|
||||||
</code></pre></pre>
|
</code></pre></pre>
|
||||||
<p>Each function may have a different return type and depending on the input
|
<p>Each function may have a different return type and depending on the input
|
||||||
|
@ -1039,19 +1058,21 @@ field is set if the validation for that particular field fails. You will likely
|
||||||
before. Each resolver function has a custom <code>ValidationResult</code> which
|
before. Each resolver function has a custom <code>ValidationResult</code> which
|
||||||
contains only fields provided by the function.</p>
|
contains only fields provided by the function.</p>
|
||||||
<pre><pre class="playpen"><code class="language-rust"># extern crate juniper;
|
<pre><pre class="playpen"><code class="language-rust"># extern crate juniper;
|
||||||
#[derive(juniper::GraphQLObject)]
|
# use juniper::{graphql_object, GraphQLObject, GraphQLUnion};
|
||||||
|
#
|
||||||
|
#[derive(GraphQLObject)]
|
||||||
pub struct Item {
|
pub struct Item {
|
||||||
name: String,
|
name: String,
|
||||||
quantity: i32,
|
quantity: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(juniper::GraphQLObject)]
|
#[derive(GraphQLObject)]
|
||||||
pub struct ValidationError {
|
pub struct ValidationError {
|
||||||
name: Option<String>,
|
name: Option<String>,
|
||||||
quantity: Option<String>,
|
quantity: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(juniper::GraphQLUnion)]
|
#[derive(GraphQLUnion)]
|
||||||
pub enum GraphQLResult {
|
pub enum GraphQLResult {
|
||||||
Ok(Item),
|
Ok(Item),
|
||||||
Err(ValidationError),
|
Err(ValidationError),
|
||||||
|
@ -1059,7 +1080,7 @@ pub enum GraphQLResult {
|
||||||
|
|
||||||
pub struct Mutation;
|
pub struct Mutation;
|
||||||
|
|
||||||
#[juniper::graphql_object]
|
#[graphql_object]
|
||||||
impl Mutation {
|
impl Mutation {
|
||||||
fn addItem(&self, name: String, quantity: i32) -> GraphQLResult {
|
fn addItem(&self, name: String, quantity: i32) -> GraphQLResult {
|
||||||
let mut error = ValidationError {
|
let mut error = ValidationError {
|
||||||
|
@ -1082,7 +1103,7 @@ impl Mutation {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#
|
||||||
# fn main() {}
|
# fn main() {}
|
||||||
</code></pre></pre>
|
</code></pre></pre>
|
||||||
<pre><code class="language-graphql">{
|
<pre><code class="language-graphql">{
|
||||||
|
@ -1108,22 +1129,23 @@ errors when they occur.</p>
|
||||||
<p>In the following example, a theoretical database could fail
|
<p>In the following example, a theoretical database could fail
|
||||||
and would generate errors. Since it is not common for the database to
|
and would generate errors. Since it is not common for the database to
|
||||||
fail, the corresponding error is returned as a critical error:</p>
|
fail, the corresponding error is returned as a critical error:</p>
|
||||||
<pre><pre class="playpen"><code class="language-rust"># // Only needed due to 2018 edition because the macro is not accessible.
|
<pre><pre class="playpen"><code class="language-rust"># extern crate juniper;
|
||||||
# #[macro_use] extern crate juniper;
|
#
|
||||||
|
use juniper::{graphql_object, graphql_value, FieldError, GraphQLObject, GraphQLUnion, ScalarValue};
|
||||||
|
|
||||||
#[derive(juniper::GraphQLObject)]
|
#[derive(GraphQLObject)]
|
||||||
pub struct Item {
|
pub struct Item {
|
||||||
name: String,
|
name: String,
|
||||||
quantity: i32,
|
quantity: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(juniper::GraphQLObject)]
|
#[derive(GraphQLObject)]
|
||||||
pub struct ValidationErrorItem {
|
pub struct ValidationErrorItem {
|
||||||
name: Option<String>,
|
name: Option<String>,
|
||||||
quantity: Option<String>,
|
quantity: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(juniper::GraphQLUnion)]
|
#[derive(GraphQLUnion)]
|
||||||
pub enum GraphQLResult {
|
pub enum GraphQLResult {
|
||||||
Ok(Item),
|
Ok(Item),
|
||||||
Err(ValidationErrorItem),
|
Err(ValidationErrorItem),
|
||||||
|
@ -1133,10 +1155,10 @@ pub enum ApiError {
|
||||||
Database,
|
Database,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl juniper::IntoFieldError for ApiError {
|
impl<S: ScalarValue> juniper::IntoFieldError<S> for ApiError {
|
||||||
fn into_field_error(self) -> juniper::FieldError {
|
fn into_field_error(self) -> FieldError<S> {
|
||||||
match self {
|
match self {
|
||||||
ApiError::Database => juniper::FieldError::new(
|
ApiError::Database => FieldError::new(
|
||||||
"Internal database error",
|
"Internal database error",
|
||||||
graphql_value!({
|
graphql_value!({
|
||||||
"type": "DATABASE"
|
"type": "DATABASE"
|
||||||
|
@ -1148,7 +1170,7 @@ impl juniper::IntoFieldError for ApiError {
|
||||||
|
|
||||||
pub struct Mutation;
|
pub struct Mutation;
|
||||||
|
|
||||||
#[juniper::graphql_object]
|
#[graphql_object]
|
||||||
impl Mutation {
|
impl Mutation {
|
||||||
fn addItem(&self, name: String, quantity: i32) -> Result<GraphQLResult, ApiError> {
|
fn addItem(&self, name: String, quantity: i32) -> Result<GraphQLResult, ApiError> {
|
||||||
let mut error = ValidationErrorItem {
|
let mut error = ValidationErrorItem {
|
||||||
|
@ -1171,7 +1193,7 @@ impl Mutation {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#
|
||||||
# fn main() {}
|
# fn main() {}
|
||||||
</code></pre></pre>
|
</code></pre></pre>
|
||||||
<a class="header" href="#additional-material" id="additional-material"><h2>Additional Material</h2></a>
|
<a class="header" href="#additional-material" id="additional-material"><h2>Additional Material</h2></a>
|
||||||
|
@ -2265,18 +2287,18 @@ object somewhere but never reference it, it will not be exposed in a schema.</p>
|
||||||
object in Juniper, most commonly using the <code>graphql_object</code> proc macro:</p>
|
object in Juniper, most commonly using the <code>graphql_object</code> proc macro:</p>
|
||||||
<pre><pre class="playpen"><code class="language-rust"># #![allow(unused_variables)]
|
<pre><pre class="playpen"><code class="language-rust"># #![allow(unused_variables)]
|
||||||
# extern crate juniper;
|
# extern crate juniper;
|
||||||
# use juniper::FieldResult;
|
# use juniper::{graphql_object, FieldResult, GraphQLObject};
|
||||||
# #[derive(juniper::GraphQLObject)] struct User { name: String }
|
# #[derive(GraphQLObject)] struct User { name: String }
|
||||||
struct Root;
|
struct Root;
|
||||||
|
|
||||||
#[juniper::graphql_object]
|
#[graphql_object]
|
||||||
impl Root {
|
impl Root {
|
||||||
fn userWithUsername(username: String) -> FieldResult<Option<User>> {
|
fn userWithUsername(username: String) -> FieldResult<Option<User>> {
|
||||||
// Look up user in database...
|
// Look up user in database...
|
||||||
# unimplemented!()
|
# unimplemented!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#
|
||||||
# fn main() { }
|
# fn main() { }
|
||||||
</code></pre></pre>
|
</code></pre></pre>
|
||||||
<a class="header" href="#mutations" id="mutations"><h2>Mutations</h2></a>
|
<a class="header" href="#mutations" id="mutations"><h2>Mutations</h2></a>
|
||||||
|
@ -2284,28 +2306,30 @@ impl Root {
|
||||||
that performs some mutating side-effect such as updating a database.</p>
|
that performs some mutating side-effect such as updating a database.</p>
|
||||||
<pre><pre class="playpen"><code class="language-rust"># #![allow(unused_variables)]
|
<pre><pre class="playpen"><code class="language-rust"># #![allow(unused_variables)]
|
||||||
# extern crate juniper;
|
# extern crate juniper;
|
||||||
# use juniper::FieldResult;
|
# use juniper::{graphql_object, FieldResult, GraphQLObject};
|
||||||
# #[derive(juniper::GraphQLObject)] struct User { name: String }
|
# #[derive(GraphQLObject)] struct User { name: String }
|
||||||
struct Mutations;
|
struct Mutations;
|
||||||
|
|
||||||
#[juniper::graphql_object]
|
#[graphql_object]
|
||||||
impl Mutations {
|
impl Mutations {
|
||||||
fn signUpUser(name: String, email: String) -> FieldResult<User> {
|
fn signUpUser(name: String, email: String) -> FieldResult<User> {
|
||||||
// Validate inputs and save user in database...
|
// Validate inputs and save user in database...
|
||||||
# unimplemented!()
|
# unimplemented!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#
|
||||||
# fn main() { }
|
# fn main() { }
|
||||||
</code></pre></pre>
|
</code></pre></pre>
|
||||||
<a class="header" href="#converting-a-rust-schema-to-the-a-hrefhttpsgraphqlorglearnschematype-languagegraphql-schema-languagea" id="converting-a-rust-schema-to-the-a-hrefhttpsgraphqlorglearnschematype-languagegraphql-schema-languagea"><h1>Converting a Rust schema to the <a href="https://graphql.org/learn/schema/#type-language">GraphQL Schema Language</a></h1></a>
|
<a class="header" href="#converting-a-rust-schema-to-the-a-hrefhttpsgraphqlorglearnschematype-languagegraphql-schema-languagea" id="converting-a-rust-schema-to-the-a-hrefhttpsgraphqlorglearnschematype-languagegraphql-schema-languagea"><h1>Converting a Rust schema to the <a href="https://graphql.org/learn/schema/#type-language">GraphQL Schema Language</a></h1></a>
|
||||||
<p>Many tools in the GraphQL ecosystem require the schema to be defined in the <a href="https://graphql.org/learn/schema/#type-language">GraphQL Schema Language</a>. You can generate a <a href="https://graphql.org/learn/schema/#type-language">GraphQL Schema Language</a> representation of your schema defined in Rust using the <code>schema-language</code> feature (on by default):</p>
|
<p>Many tools in the GraphQL ecosystem require the schema to be defined in the <a href="https://graphql.org/learn/schema/#type-language">GraphQL Schema Language</a>. You can generate a <a href="https://graphql.org/learn/schema/#type-language">GraphQL Schema Language</a> representation of your schema defined in Rust using the <code>schema-language</code> feature (on by default):</p>
|
||||||
<pre><pre class="playpen"><code class="language-rust"># extern crate juniper;
|
<pre><pre class="playpen"><code class="language-rust"># extern crate juniper;
|
||||||
use juniper::{FieldResult, EmptyMutation, EmptySubscription, RootNode};
|
use juniper::{
|
||||||
|
graphql_object, EmptyMutation, EmptySubscription, FieldResult, RootNode,
|
||||||
|
};
|
||||||
|
|
||||||
struct Query;
|
struct Query;
|
||||||
|
|
||||||
#[juniper::graphql_object]
|
#[graphql_object]
|
||||||
impl Query {
|
impl Query {
|
||||||
fn hello(&self) -> FieldResult<&str> {
|
fn hello(&self) -> FieldResult<&str> {
|
||||||
Ok("hello world")
|
Ok("hello world")
|
||||||
|
@ -2553,11 +2577,14 @@ result can then be converted to JSON for use with tools and libraries such as
|
||||||
<pre><pre class="playpen"><code class="language-rust"># #![allow(unused_variables)]
|
<pre><pre class="playpen"><code class="language-rust"># #![allow(unused_variables)]
|
||||||
# extern crate juniper;
|
# extern crate juniper;
|
||||||
# extern crate serde_json;
|
# extern crate serde_json;
|
||||||
use juniper::{EmptyMutation, EmptySubscription, FieldResult, IntrospectionFormat};
|
use juniper::{
|
||||||
|
graphql_object, EmptyMutation, EmptySubscription, FieldResult,
|
||||||
|
GraphQLObject, IntrospectionFormat,
|
||||||
|
};
|
||||||
|
|
||||||
// Define our schema.
|
// Define our schema.
|
||||||
|
|
||||||
#[derive(juniper::GraphQLObject)]
|
#[derive(GraphQLObject)]
|
||||||
struct Example {
|
struct Example {
|
||||||
id: String,
|
id: String,
|
||||||
}
|
}
|
||||||
|
@ -2567,9 +2594,7 @@ impl juniper::Context for Context {}
|
||||||
|
|
||||||
struct Query;
|
struct Query;
|
||||||
|
|
||||||
#[juniper::graphql_object(
|
#[graphql_object(context = Context)]
|
||||||
Context = Context,
|
|
||||||
)]
|
|
||||||
impl Query {
|
impl Query {
|
||||||
fn example(id: String) -> FieldResult<Example> {
|
fn example(id: String) -> FieldResult<Example> {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
|
@ -2607,9 +2632,10 @@ interfaces.</p>
|
||||||
<p>Using <code>Result</code>-like enums can be a useful way of reporting e.g. validation
|
<p>Using <code>Result</code>-like enums can be a useful way of reporting e.g. validation
|
||||||
errors from a mutation:</p>
|
errors from a mutation:</p>
|
||||||
<pre><pre class="playpen"><code class="language-rust"># extern crate juniper;
|
<pre><pre class="playpen"><code class="language-rust"># extern crate juniper;
|
||||||
|
# use juniper::{graphql_object, GraphQLObject};
|
||||||
# #[derive(juniper::GraphQLObject)] struct User { name: String }
|
# #[derive(juniper::GraphQLObject)] struct User { name: String }
|
||||||
|
#
|
||||||
#[derive(juniper::GraphQLObject)]
|
#[derive(GraphQLObject)]
|
||||||
struct ValidationError {
|
struct ValidationError {
|
||||||
field: String,
|
field: String,
|
||||||
message: String,
|
message: String,
|
||||||
|
@ -2621,7 +2647,7 @@ enum SignUpResult {
|
||||||
Error(Vec<ValidationError>),
|
Error(Vec<ValidationError>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[juniper::graphql_object]
|
#[graphql_object]
|
||||||
impl SignUpResult {
|
impl SignUpResult {
|
||||||
fn user(&self) -> Option<&User> {
|
fn user(&self) -> Option<&User> {
|
||||||
match *self {
|
match *self {
|
||||||
|
@ -2637,7 +2663,7 @@ impl SignUpResult {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#
|
||||||
# fn main() {}
|
# fn main() {}
|
||||||
</code></pre></pre>
|
</code></pre></pre>
|
||||||
<p>Here, we use an enum to decide whether a user's input data was valid or not, and
|
<p>Here, we use an enum to decide whether a user's input data was valid or not, and
|
||||||
|
@ -3042,7 +3068,7 @@ sequentially:</p>
|
||||||
# extern crate juniper;
|
# extern crate juniper;
|
||||||
# extern crate juniper_subscriptions;
|
# extern crate juniper_subscriptions;
|
||||||
# extern crate tokio;
|
# extern crate tokio;
|
||||||
# use juniper::FieldError;
|
# use juniper::{graphql_object, graphql_subscription, FieldError};
|
||||||
# use futures::Stream;
|
# use futures::Stream;
|
||||||
# use std::pin::Pin;
|
# use std::pin::Pin;
|
||||||
#
|
#
|
||||||
|
@ -3051,7 +3077,7 @@ sequentially:</p>
|
||||||
# impl juniper::Context for Database {}
|
# impl juniper::Context for Database {}
|
||||||
|
|
||||||
# pub struct Query;
|
# pub struct Query;
|
||||||
# #[juniper::graphql_object(Context = Database)]
|
# #[graphql_object(context = Database)]
|
||||||
# impl Query {
|
# impl Query {
|
||||||
# fn hello_world() -> &str {
|
# fn hello_world() -> &str {
|
||||||
# "Hello World!"
|
# "Hello World!"
|
||||||
|
@ -3061,7 +3087,7 @@ pub struct Subscription;
|
||||||
|
|
||||||
type StringStream = Pin<Box<dyn Stream<Item = Result<String, FieldError>> + Send>>;
|
type StringStream = Pin<Box<dyn Stream<Item = Result<String, FieldError>> + Send>>;
|
||||||
|
|
||||||
#[juniper::graphql_subscription(Context = Database)]
|
#[graphql_subscription(context = Database)]
|
||||||
impl Subscription {
|
impl Subscription {
|
||||||
async fn hello_world() -> StringStream {
|
async fn hello_world() -> StringStream {
|
||||||
let stream = tokio::stream::iter(vec![
|
let stream = tokio::stream::iter(vec![
|
||||||
|
@ -3071,6 +3097,7 @@ impl Subscription {
|
||||||
Box::pin(stream)
|
Box::pin(stream)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#
|
||||||
# fn main () {}
|
# fn main () {}
|
||||||
</code></pre></pre>
|
</code></pre></pre>
|
||||||
<a class="header" href="#coordinator" id="coordinator"><h3>Coordinator</h3></a>
|
<a class="header" href="#coordinator" id="coordinator"><h3>Coordinator</h3></a>
|
||||||
|
@ -3088,8 +3115,12 @@ where [<code>Connection</code>][Connection] is a <code>Stream</code> of values r
|
||||||
# extern crate juniper_subscriptions;
|
# extern crate juniper_subscriptions;
|
||||||
# extern crate serde_json;
|
# extern crate serde_json;
|
||||||
# extern crate tokio;
|
# extern crate tokio;
|
||||||
# use juniper::http::GraphQLRequest;
|
# use juniper::{
|
||||||
# use juniper::{DefaultScalarValue, EmptyMutation, FieldError, RootNode, SubscriptionCoordinator};
|
# http::GraphQLRequest,
|
||||||
|
# graphql_object, graphql_subscription,
|
||||||
|
# DefaultScalarValue, EmptyMutation, FieldError,
|
||||||
|
# RootNode, SubscriptionCoordinator,
|
||||||
|
# };
|
||||||
# use juniper_subscriptions::Coordinator;
|
# use juniper_subscriptions::Coordinator;
|
||||||
# use futures::{Stream, StreamExt};
|
# use futures::{Stream, StreamExt};
|
||||||
# use std::pin::Pin;
|
# use std::pin::Pin;
|
||||||
|
@ -3107,7 +3138,7 @@ where [<code>Connection</code>][Connection] is a <code>Stream</code> of values r
|
||||||
#
|
#
|
||||||
# pub struct Query;
|
# pub struct Query;
|
||||||
#
|
#
|
||||||
# #[juniper::graphql_object(Context = Database)]
|
# #[graphql_object(context = Database)]
|
||||||
# impl Query {
|
# impl Query {
|
||||||
# fn hello_world() -> &str {
|
# fn hello_world() -> &str {
|
||||||
# "Hello World!"
|
# "Hello World!"
|
||||||
|
@ -3118,7 +3149,7 @@ where [<code>Connection</code>][Connection] is a <code>Stream</code> of values r
|
||||||
#
|
#
|
||||||
# type StringStream = Pin<Box<dyn Stream<Item = Result<String, FieldError>> + Send>>;
|
# type StringStream = Pin<Box<dyn Stream<Item = Result<String, FieldError>> + Send>>;
|
||||||
#
|
#
|
||||||
# #[juniper::graphql_subscription(Context = Database)]
|
# #[graphql_subscription(context = Database)]
|
||||||
# impl Subscription {
|
# impl Subscription {
|
||||||
# async fn hello_world() -> StringStream {
|
# async fn hello_world() -> StringStream {
|
||||||
# let stream =
|
# let stream =
|
||||||
|
@ -3136,11 +3167,9 @@ async fn run_subscription() {
|
||||||
let schema = schema();
|
let schema = schema();
|
||||||
let coordinator = Coordinator::new(schema);
|
let coordinator = Coordinator::new(schema);
|
||||||
let req: GraphQLRequest<DefaultScalarValue> = serde_json::from_str(
|
let req: GraphQLRequest<DefaultScalarValue> = serde_json::from_str(
|
||||||
r#"
|
r#"{
|
||||||
{
|
|
||||||
"query": "subscription { helloWorld }"
|
"query": "subscription { helloWorld }"
|
||||||
}
|
}"#,
|
||||||
"#,
|
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let ctx = Database::new();
|
let ctx = Database::new();
|
||||||
|
@ -3149,7 +3178,7 @@ async fn run_subscription() {
|
||||||
println!("{}", serde_json::to_string(&result).unwrap());
|
println!("{}", serde_json::to_string(&result).unwrap());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#
|
||||||
# fn main() { }
|
# fn main() { }
|
||||||
</code></pre></pre>
|
</code></pre></pre>
|
||||||
<a class="header" href="#web-integration-and-examples" id="web-integration-and-examples"><h3>Web Integration and Examples</h3></a>
|
<a class="header" href="#web-integration-and-examples" id="web-integration-and-examples"><h3>Web Integration and Examples</h3></a>
|
||||||
|
|
|
@ -155,8 +155,12 @@ types to a GraphQL schema. The most important one is the
|
||||||
resolvers, which you will use for the <code>Query</code> and <code>Mutation</code> roots.</p>
|
resolvers, which you will use for the <code>Query</code> and <code>Mutation</code> roots.</p>
|
||||||
<pre><pre class="playpen"><code class="language-rust"># #![allow(unused_variables)]
|
<pre><pre class="playpen"><code class="language-rust"># #![allow(unused_variables)]
|
||||||
# extern crate juniper;
|
# extern crate juniper;
|
||||||
use juniper::{FieldResult, EmptySubscription};
|
# use std::fmt::Display;
|
||||||
|
use juniper::{
|
||||||
|
graphql_object, EmptySubscription, FieldResult, GraphQLEnum,
|
||||||
|
GraphQLInputObject, GraphQLObject, ScalarValue,
|
||||||
|
};
|
||||||
|
#
|
||||||
# struct DatabasePool;
|
# struct DatabasePool;
|
||||||
# impl DatabasePool {
|
# impl DatabasePool {
|
||||||
# fn get_connection(&self) -> FieldResult<DatabasePool> { Ok(DatabasePool) }
|
# fn get_connection(&self) -> FieldResult<DatabasePool> { Ok(DatabasePool) }
|
||||||
|
@ -164,14 +168,14 @@ use juniper::{FieldResult, EmptySubscription};
|
||||||
# fn insert_human(&self, _human: &NewHuman) -> FieldResult<Human> { Err("")? }
|
# fn insert_human(&self, _human: &NewHuman) -> FieldResult<Human> { Err("")? }
|
||||||
# }
|
# }
|
||||||
|
|
||||||
#[derive(juniper::GraphQLEnum)]
|
#[derive(GraphQLEnum)]
|
||||||
enum Episode {
|
enum Episode {
|
||||||
NewHope,
|
NewHope,
|
||||||
Empire,
|
Empire,
|
||||||
Jedi,
|
Jedi,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(juniper::GraphQLObject)]
|
#[derive(GraphQLObject)]
|
||||||
#[graphql(description = "A humanoid creature in the Star Wars universe")]
|
#[graphql(description = "A humanoid creature in the Star Wars universe")]
|
||||||
struct Human {
|
struct Human {
|
||||||
id: String,
|
id: String,
|
||||||
|
@ -182,7 +186,7 @@ struct Human {
|
||||||
|
|
||||||
// There is also a custom derive for mapping GraphQL input objects.
|
// There is also a custom derive for mapping GraphQL input objects.
|
||||||
|
|
||||||
#[derive(juniper::GraphQLInputObject)]
|
#[derive(GraphQLInputObject)]
|
||||||
#[graphql(description = "A humanoid creature in the Star Wars universe")]
|
#[graphql(description = "A humanoid creature in the Star Wars universe")]
|
||||||
struct NewHuman {
|
struct NewHuman {
|
||||||
name: String,
|
name: String,
|
||||||
|
@ -205,14 +209,13 @@ impl juniper::Context for Context {}
|
||||||
|
|
||||||
struct Query;
|
struct Query;
|
||||||
|
|
||||||
#[juniper::graphql_object(
|
#[graphql_object(
|
||||||
// Here we specify the context type for the object.
|
// Here we specify the context type for the object.
|
||||||
// We need to do this in every type that
|
// We need to do this in every type that
|
||||||
// needs access to the context.
|
// needs access to the context.
|
||||||
Context = Context,
|
context = Context,
|
||||||
)]
|
)]
|
||||||
impl Query {
|
impl Query {
|
||||||
|
|
||||||
fn apiVersion() -> &str {
|
fn apiVersion() -> &str {
|
||||||
"1.0"
|
"1.0"
|
||||||
}
|
}
|
||||||
|
@ -236,14 +239,18 @@ impl Query {
|
||||||
|
|
||||||
struct Mutation;
|
struct Mutation;
|
||||||
|
|
||||||
#[juniper::graphql_object(
|
#[graphql_object(
|
||||||
Context = Context,
|
context = Context,
|
||||||
)]
|
|
||||||
impl Mutation {
|
|
||||||
|
|
||||||
fn createHuman(context: &Context, new_human: NewHuman) -> FieldResult<Human> {
|
// If we need to use `ScalarValue` parametrization explicitly somewhere
|
||||||
let db = context.pool.get_connection()?;
|
// in the object definition (like here in `FieldResult`), we should
|
||||||
let human: Human = db.insert_human(&new_human)?;
|
// declare an explicit type parameter for that, and specify it.
|
||||||
|
scalar = S,
|
||||||
|
)]
|
||||||
|
impl<S: ScalarValue + Display> Mutation {
|
||||||
|
fn createHuman(context: &Context, new_human: NewHuman) -> FieldResult<Human, S> {
|
||||||
|
let db = context.pool.get_connection().map_err(|e| e.map_scalar_value())?;
|
||||||
|
let human: Human = db.insert_human(&new_human).map_err(|e| e.map_scalar_value())?;
|
||||||
Ok(human)
|
Ok(human)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -251,7 +258,7 @@ impl Mutation {
|
||||||
// A root schema consists of a query, a mutation, and a subscription.
|
// A root schema consists of a query, a mutation, and a subscription.
|
||||||
// Request queries can be executed against a RootNode.
|
// Request queries can be executed against a RootNode.
|
||||||
type Schema = juniper::RootNode<'static, Query, Mutation, EmptySubscription<Context>>;
|
type Schema = juniper::RootNode<'static, Query, Mutation, EmptySubscription<Context>>;
|
||||||
|
#
|
||||||
# fn main() {
|
# fn main() {
|
||||||
# let _ = Schema::new(Query, Mutation{}, EmptySubscription::new());
|
# let _ = Schema::new(Query, Mutation{}, EmptySubscription::new());
|
||||||
# }
|
# }
|
||||||
|
@ -263,10 +270,12 @@ type Schema = juniper::RootNode<'static, Query, Mutation, EmptySubscription&l
|
||||||
<p>You can invoke <code>juniper::execute</code> directly to run a GraphQL query:</p>
|
<p>You can invoke <code>juniper::execute</code> directly to run a GraphQL query:</p>
|
||||||
<pre><pre class="playpen"><code class="language-rust"># // Only needed due to 2018 edition because the macro is not accessible.
|
<pre><pre class="playpen"><code class="language-rust"># // Only needed due to 2018 edition because the macro is not accessible.
|
||||||
# #[macro_use] extern crate juniper;
|
# #[macro_use] extern crate juniper;
|
||||||
use juniper::{FieldResult, Variables, EmptyMutation, EmptySubscription};
|
use juniper::{
|
||||||
|
graphql_object, EmptyMutation, EmptySubscription, FieldResult,
|
||||||
|
GraphQLEnum, Variables,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(GraphQLEnum, Clone, Copy)]
|
||||||
#[derive(juniper::GraphQLEnum, Clone, Copy)]
|
|
||||||
enum Episode {
|
enum Episode {
|
||||||
NewHope,
|
NewHope,
|
||||||
Empire,
|
Empire,
|
||||||
|
@ -280,16 +289,13 @@ impl juniper::Context for Ctx {}
|
||||||
|
|
||||||
struct Query;
|
struct Query;
|
||||||
|
|
||||||
#[juniper::graphql_object(
|
#[graphql_object(context = Ctx)]
|
||||||
Context = Ctx,
|
|
||||||
)]
|
|
||||||
impl Query {
|
impl Query {
|
||||||
fn favoriteEpisode(context: &Ctx) -> FieldResult<Episode> {
|
fn favoriteEpisode(context: &Ctx) -> FieldResult<Episode> {
|
||||||
Ok(context.0)
|
Ok(context.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// A root schema consists of a query, a mutation, and a subscription.
|
// A root schema consists of a query, a mutation, and a subscription.
|
||||||
// Request queries can be executed against a RootNode.
|
// Request queries can be executed against a RootNode.
|
||||||
type Schema = juniper::RootNode<'static, Query, EmptyMutation<Ctx>, EmptySubscription<Ctx>>;
|
type Schema = juniper::RootNode<'static, Query, EmptyMutation<Ctx>, EmptySubscription<Ctx>>;
|
||||||
|
|
|
@ -153,18 +153,18 @@ object somewhere but never reference it, it will not be exposed in a schema.</p>
|
||||||
object in Juniper, most commonly using the <code>graphql_object</code> proc macro:</p>
|
object in Juniper, most commonly using the <code>graphql_object</code> proc macro:</p>
|
||||||
<pre><pre class="playpen"><code class="language-rust"># #![allow(unused_variables)]
|
<pre><pre class="playpen"><code class="language-rust"># #![allow(unused_variables)]
|
||||||
# extern crate juniper;
|
# extern crate juniper;
|
||||||
# use juniper::FieldResult;
|
# use juniper::{graphql_object, FieldResult, GraphQLObject};
|
||||||
# #[derive(juniper::GraphQLObject)] struct User { name: String }
|
# #[derive(GraphQLObject)] struct User { name: String }
|
||||||
struct Root;
|
struct Root;
|
||||||
|
|
||||||
#[juniper::graphql_object]
|
#[graphql_object]
|
||||||
impl Root {
|
impl Root {
|
||||||
fn userWithUsername(username: String) -> FieldResult<Option<User>> {
|
fn userWithUsername(username: String) -> FieldResult<Option<User>> {
|
||||||
// Look up user in database...
|
// Look up user in database...
|
||||||
# unimplemented!()
|
# unimplemented!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#
|
||||||
# fn main() { }
|
# fn main() { }
|
||||||
</code></pre></pre>
|
</code></pre></pre>
|
||||||
<a class="header" href="#mutations" id="mutations"><h2>Mutations</h2></a>
|
<a class="header" href="#mutations" id="mutations"><h2>Mutations</h2></a>
|
||||||
|
@ -172,28 +172,30 @@ impl Root {
|
||||||
that performs some mutating side-effect such as updating a database.</p>
|
that performs some mutating side-effect such as updating a database.</p>
|
||||||
<pre><pre class="playpen"><code class="language-rust"># #![allow(unused_variables)]
|
<pre><pre class="playpen"><code class="language-rust"># #![allow(unused_variables)]
|
||||||
# extern crate juniper;
|
# extern crate juniper;
|
||||||
# use juniper::FieldResult;
|
# use juniper::{graphql_object, FieldResult, GraphQLObject};
|
||||||
# #[derive(juniper::GraphQLObject)] struct User { name: String }
|
# #[derive(GraphQLObject)] struct User { name: String }
|
||||||
struct Mutations;
|
struct Mutations;
|
||||||
|
|
||||||
#[juniper::graphql_object]
|
#[graphql_object]
|
||||||
impl Mutations {
|
impl Mutations {
|
||||||
fn signUpUser(name: String, email: String) -> FieldResult<User> {
|
fn signUpUser(name: String, email: String) -> FieldResult<User> {
|
||||||
// Validate inputs and save user in database...
|
// Validate inputs and save user in database...
|
||||||
# unimplemented!()
|
# unimplemented!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#
|
||||||
# fn main() { }
|
# fn main() { }
|
||||||
</code></pre></pre>
|
</code></pre></pre>
|
||||||
<a class="header" href="#converting-a-rust-schema-to-the-a-hrefhttpsgraphqlorglearnschematype-languagegraphql-schema-languagea" id="converting-a-rust-schema-to-the-a-hrefhttpsgraphqlorglearnschematype-languagegraphql-schema-languagea"><h1>Converting a Rust schema to the <a href="https://graphql.org/learn/schema/#type-language">GraphQL Schema Language</a></h1></a>
|
<a class="header" href="#converting-a-rust-schema-to-the-a-hrefhttpsgraphqlorglearnschematype-languagegraphql-schema-languagea" id="converting-a-rust-schema-to-the-a-hrefhttpsgraphqlorglearnschematype-languagegraphql-schema-languagea"><h1>Converting a Rust schema to the <a href="https://graphql.org/learn/schema/#type-language">GraphQL Schema Language</a></h1></a>
|
||||||
<p>Many tools in the GraphQL ecosystem require the schema to be defined in the <a href="https://graphql.org/learn/schema/#type-language">GraphQL Schema Language</a>. You can generate a <a href="https://graphql.org/learn/schema/#type-language">GraphQL Schema Language</a> representation of your schema defined in Rust using the <code>schema-language</code> feature (on by default):</p>
|
<p>Many tools in the GraphQL ecosystem require the schema to be defined in the <a href="https://graphql.org/learn/schema/#type-language">GraphQL Schema Language</a>. You can generate a <a href="https://graphql.org/learn/schema/#type-language">GraphQL Schema Language</a> representation of your schema defined in Rust using the <code>schema-language</code> feature (on by default):</p>
|
||||||
<pre><pre class="playpen"><code class="language-rust"># extern crate juniper;
|
<pre><pre class="playpen"><code class="language-rust"># extern crate juniper;
|
||||||
use juniper::{FieldResult, EmptyMutation, EmptySubscription, RootNode};
|
use juniper::{
|
||||||
|
graphql_object, EmptyMutation, EmptySubscription, FieldResult, RootNode,
|
||||||
|
};
|
||||||
|
|
||||||
struct Query;
|
struct Query;
|
||||||
|
|
||||||
#[juniper::graphql_object]
|
#[graphql_object]
|
||||||
impl Query {
|
impl Query {
|
||||||
fn hello(&self) -> FieldResult<&str> {
|
fn hello(&self) -> FieldResult<&str> {
|
||||||
Ok("hello world")
|
Ok("hello world")
|
||||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -139,7 +139,7 @@
|
||||||
<a class="header" href="#complex-fields" id="complex-fields"><h1>Complex fields</h1></a>
|
<a class="header" href="#complex-fields" id="complex-fields"><h1>Complex fields</h1></a>
|
||||||
<p>If you've got a struct that can't be mapped directly to GraphQL, that contains
|
<p>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:
|
computed fields or circular structures, you have to use a more powerful tool:
|
||||||
the <code>object</code> procedural macro. This macro lets you define GraphQL object
|
the <code>#[graphql_object]</code> procedural macro. This macro lets you define GraphQL object
|
||||||
fields in a Rust <code>impl</code> block for a type. Note that only GraphQL fields
|
fields in a Rust <code>impl</code> block for a type. Note that only GraphQL fields
|
||||||
can be specified in this <code>impl</code> block. If you want to define normal methods on the struct,
|
can be specified in this <code>impl</code> block. If you want to define normal methods on the struct,
|
||||||
you have to do so in a separate, normal <code>impl</code> block. Continuing with the
|
you have to do so in a separate, normal <code>impl</code> block. Continuing with the
|
||||||
|
@ -147,13 +147,14 @@ example from the last chapter, this is how you would define <code>Person</code>
|
||||||
macro:</p>
|
macro:</p>
|
||||||
<pre><pre class="playpen"><code class="language-rust"># #![allow(dead_code)]
|
<pre><pre class="playpen"><code class="language-rust"># #![allow(dead_code)]
|
||||||
# extern crate juniper;
|
# extern crate juniper;
|
||||||
|
# use juniper::graphql_object;
|
||||||
#
|
#
|
||||||
struct Person {
|
struct Person {
|
||||||
name: String,
|
name: String,
|
||||||
age: i32,
|
age: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[juniper::graphql_object]
|
#[graphql_object]
|
||||||
impl Person {
|
impl Person {
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
self.name.as_str()
|
self.name.as_str()
|
||||||
|
@ -171,13 +172,15 @@ impl Person {
|
||||||
// [...]
|
// [...]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#
|
||||||
# fn main() { }
|
# fn main() { }
|
||||||
</code></pre></pre>
|
</code></pre></pre>
|
||||||
<p>While this is a bit more verbose, it lets you write any kind of function in the
|
<p>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:</p>
|
field resolver. With this syntax, fields can also take arguments:</p>
|
||||||
<pre><pre class="playpen"><code class="language-rust"># extern crate juniper;
|
<pre><pre class="playpen"><code class="language-rust"># extern crate juniper;
|
||||||
#[derive(juniper::GraphQLObject)]
|
# use juniper::{graphql_object, GraphQLObject};
|
||||||
|
#
|
||||||
|
#[derive(GraphQLObject)]
|
||||||
struct Person {
|
struct Person {
|
||||||
name: String,
|
name: String,
|
||||||
age: i32,
|
age: i32,
|
||||||
|
@ -187,14 +190,14 @@ struct House {
|
||||||
inhabitants: Vec<Person>,
|
inhabitants: Vec<Person>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[juniper::graphql_object]
|
#[graphql_object]
|
||||||
impl House {
|
impl House {
|
||||||
// Creates the field inhabitantWithName(name), returning a nullable person
|
// Creates the field inhabitantWithName(name), returning a nullable person
|
||||||
fn inhabitant_with_name(&self, name: String) -> Option<&Person> {
|
fn inhabitant_with_name(&self, name: String) -> Option<&Person> {
|
||||||
self.inhabitants.iter().find(|p| p.name == name)
|
self.inhabitants.iter().find(|p| p.name == name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#
|
||||||
# fn main() {}
|
# fn main() {}
|
||||||
</code></pre></pre>
|
</code></pre></pre>
|
||||||
<p>To access global data such as database connections or authentication
|
<p>To access global data such as database connections or authentication
|
||||||
|
@ -205,20 +208,20 @@ chapter: <a href="using_contexts.html">Using contexts</a>.</p>
|
||||||
to <code>camelCase</code>. If you need to override the conversion, you can simply rename
|
to <code>camelCase</code>. If you need to override the conversion, you can simply rename
|
||||||
the field. Also, the type name can be changed with an alias:</p>
|
the field. Also, the type name can be changed with an alias:</p>
|
||||||
<pre><pre class="playpen"><code class="language-rust"># extern crate juniper;
|
<pre><pre class="playpen"><code class="language-rust"># extern crate juniper;
|
||||||
|
# use juniper::graphql_object;
|
||||||
struct Person {
|
#
|
||||||
}
|
struct Person;
|
||||||
|
|
||||||
/// Doc comments are used as descriptions for GraphQL.
|
/// Doc comments are used as descriptions for GraphQL.
|
||||||
#[juniper::graphql_object(
|
#[graphql_object(
|
||||||
// With this attribute you can change the public GraphQL name of the type.
|
// With this attribute you can change the public GraphQL name of the type.
|
||||||
name = "PersonObject",
|
name = "PersonObject",
|
||||||
|
|
||||||
// You can also specify a description here, which will overwrite
|
// You can also specify a description here, which will overwrite
|
||||||
// a doc comment description.
|
// a doc comment description.
|
||||||
description = "...",
|
description = "...",
|
||||||
)]
|
)]
|
||||||
impl Person {
|
impl Person {
|
||||||
|
|
||||||
/// A doc comment on the field will also be used for GraphQL.
|
/// A doc comment on the field will also be used for GraphQL.
|
||||||
#[graphql(
|
#[graphql(
|
||||||
// Or provide a description here.
|
// Or provide a description here.
|
||||||
|
@ -229,9 +232,7 @@ impl Person {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fields can also be renamed if required.
|
// Fields can also be renamed if required.
|
||||||
#[graphql(
|
#[graphql(name = "myCustomFieldName")]
|
||||||
name = "myCustomFieldName",
|
|
||||||
)]
|
|
||||||
fn renamed_field() -> bool {
|
fn renamed_field() -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
@ -248,7 +249,7 @@ impl Person {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#
|
||||||
# fn main() { }
|
# fn main() { }
|
||||||
</code></pre></pre>
|
</code></pre></pre>
|
||||||
<a class="header" href="#customizing-arguments" id="customizing-arguments"><h2>Customizing arguments</h2></a>
|
<a class="header" href="#customizing-arguments" id="customizing-arguments"><h2>Customizing arguments</h2></a>
|
||||||
|
@ -257,10 +258,11 @@ impl Person {
|
||||||
<p><strong>Note</strong>: The syntax for this is currently a little awkward.
|
<p><strong>Note</strong>: The syntax for this is currently a little awkward.
|
||||||
This will become better once the <a href="https://github.com/rust-lang/rust/issues/60406">Rust RFC 2565</a> is implemented.</p>
|
This will become better once the <a href="https://github.com/rust-lang/rust/issues/60406">Rust RFC 2565</a> is implemented.</p>
|
||||||
<pre><pre class="playpen"><code class="language-rust"># extern crate juniper;
|
<pre><pre class="playpen"><code class="language-rust"># extern crate juniper;
|
||||||
|
# use juniper::graphql_object;
|
||||||
|
#
|
||||||
struct Person {}
|
struct Person {}
|
||||||
|
|
||||||
#[juniper::graphql_object]
|
#[graphql_object]
|
||||||
impl Person {
|
impl Person {
|
||||||
#[graphql(
|
#[graphql(
|
||||||
arguments(
|
arguments(
|
||||||
|
@ -280,7 +282,7 @@ impl Person {
|
||||||
format!("{} {}", arg1, arg2)
|
format!("{} {}", arg1, arg2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#
|
||||||
# fn main() { }
|
# fn main() { }
|
||||||
</code></pre></pre>
|
</code></pre></pre>
|
||||||
<a class="header" href="#more-features" id="more-features"><h2>More features</h2></a>
|
<a class="header" href="#more-features" id="more-features"><h2>More features</h2></a>
|
||||||
|
@ -290,7 +292,7 @@ impl Person {
|
||||||
<li>Per-argument default values</li>
|
<li>Per-argument default values</li>
|
||||||
<li>Per-argument descriptions</li>
|
<li>Per-argument descriptions</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>These, and more features, are described more thorougly in <a href="https://docs.rs/juniper/latest/juniper/macro.object.html">the reference
|
<p>These, and more features, are described more thoroughly in <a href="https://docs.rs/juniper/latest/juniper/macro.object.html">the reference
|
||||||
documentation</a>.</p>
|
documentation</a>.</p>
|
||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
|
@ -144,12 +144,13 @@ struct you want to expose, the easiest way is to use the custom derive
|
||||||
attribute. The other way is described in the <a href="complex_fields.html">Complex fields</a>
|
attribute. The other way is described in the <a href="complex_fields.html">Complex fields</a>
|
||||||
chapter.</p>
|
chapter.</p>
|
||||||
<pre><pre class="playpen"><code class="language-rust"># extern crate juniper;
|
<pre><pre class="playpen"><code class="language-rust"># extern crate juniper;
|
||||||
#[derive(juniper::GraphQLObject)]
|
# use juniper::GraphQLObject;
|
||||||
|
#[derive(GraphQLObject)]
|
||||||
struct Person {
|
struct Person {
|
||||||
name: String,
|
name: String,
|
||||||
age: i32,
|
age: i32,
|
||||||
}
|
}
|
||||||
|
#
|
||||||
# fn main() {}
|
# fn main() {}
|
||||||
</code></pre></pre>
|
</code></pre></pre>
|
||||||
<p>This will create a GraphQL object type called <code>Person</code>, with two fields: <code>name</code>
|
<p>This will create a GraphQL object type called <code>Person</code>, with two fields: <code>name</code>
|
||||||
|
@ -162,7 +163,8 @@ fields. Juniper will automatically use associated doc comments as GraphQL
|
||||||
descriptions:</p>
|
descriptions:</p>
|
||||||
<p>!FILENAME GraphQL descriptions via Rust doc comments</p>
|
<p>!FILENAME GraphQL descriptions via Rust doc comments</p>
|
||||||
<pre><pre class="playpen"><code class="language-rust"># extern crate juniper;
|
<pre><pre class="playpen"><code class="language-rust"># extern crate juniper;
|
||||||
#[derive(juniper::GraphQLObject)]
|
# use juniper::GraphQLObject;
|
||||||
|
#[derive(GraphQLObject)]
|
||||||
/// Information about a person
|
/// Information about a person
|
||||||
struct Person {
|
struct Person {
|
||||||
/// The person's full name, including both first and last names
|
/// The person's full name, including both first and last names
|
||||||
|
@ -170,14 +172,15 @@ struct Person {
|
||||||
/// The person's age in years, rounded down
|
/// The person's age in years, rounded down
|
||||||
age: i32,
|
age: i32,
|
||||||
}
|
}
|
||||||
|
#
|
||||||
# fn main() {}
|
# fn main() {}
|
||||||
</code></pre></pre>
|
</code></pre></pre>
|
||||||
<p>Objects and fields without doc comments can instead set a <code>description</code>
|
<p>Objects and fields without doc comments can instead set a <code>description</code>
|
||||||
via the <code>graphql</code> attribute. The following example is equivalent to the above:</p>
|
via the <code>graphql</code> attribute. The following example is equivalent to the above:</p>
|
||||||
<p>!FILENAME GraphQL descriptions via attribute</p>
|
<p>!FILENAME GraphQL descriptions via attribute</p>
|
||||||
<pre><pre class="playpen"><code class="language-rust"># extern crate juniper;
|
<pre><pre class="playpen"><code class="language-rust"># extern crate juniper;
|
||||||
#[derive(juniper::GraphQLObject)]
|
# use juniper::GraphQLObject;
|
||||||
|
#[derive(GraphQLObject)]
|
||||||
#[graphql(description = "Information about a person")]
|
#[graphql(description = "Information about a person")]
|
||||||
struct Person {
|
struct Person {
|
||||||
#[graphql(description = "The person's full name, including both first and last names")]
|
#[graphql(description = "The person's full name, including both first and last names")]
|
||||||
|
@ -185,14 +188,15 @@ struct Person {
|
||||||
#[graphql(description = "The person's age in years, rounded down")]
|
#[graphql(description = "The person's age in years, rounded down")]
|
||||||
age: i32,
|
age: i32,
|
||||||
}
|
}
|
||||||
|
#
|
||||||
# fn main() {}
|
# fn main() {}
|
||||||
</code></pre></pre>
|
</code></pre></pre>
|
||||||
<p>Descriptions set via the <code>graphql</code> attribute take precedence over Rust
|
<p>Descriptions set via the <code>graphql</code> attribute take precedence over Rust
|
||||||
doc comments. This enables internal Rust documentation and external GraphQL
|
doc comments. This enables internal Rust documentation and external GraphQL
|
||||||
documentation to differ:</p>
|
documentation to differ:</p>
|
||||||
<pre><pre class="playpen"><code class="language-rust"># extern crate juniper;
|
<pre><pre class="playpen"><code class="language-rust"># extern crate juniper;
|
||||||
#[derive(juniper::GraphQLObject)]
|
# use juniper::GraphQLObject;
|
||||||
|
#[derive(GraphQLObject)]
|
||||||
#[graphql(description = "This description shows up in GraphQL")]
|
#[graphql(description = "This description shows up in GraphQL")]
|
||||||
/// This description shows up in RustDoc
|
/// This description shows up in RustDoc
|
||||||
struct Person {
|
struct Person {
|
||||||
|
@ -202,7 +206,7 @@ struct Person {
|
||||||
/// This description shows up in both RustDoc and GraphQL
|
/// This description shows up in both RustDoc and GraphQL
|
||||||
age: i32,
|
age: i32,
|
||||||
}
|
}
|
||||||
|
#
|
||||||
# fn main() {}
|
# fn main() {}
|
||||||
</code></pre></pre>
|
</code></pre></pre>
|
||||||
<a class="header" href="#relationships" id="relationships"><h2>Relationships</h2></a>
|
<a class="header" href="#relationships" id="relationships"><h2>Relationships</h2></a>
|
||||||
|
@ -221,18 +225,19 @@ or</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>Let's see what that means for building relationships between objects:</p>
|
<p>Let's see what that means for building relationships between objects:</p>
|
||||||
<pre><pre class="playpen"><code class="language-rust"># extern crate juniper;
|
<pre><pre class="playpen"><code class="language-rust"># extern crate juniper;
|
||||||
#[derive(juniper::GraphQLObject)]
|
# use juniper::GraphQLObject;
|
||||||
|
#[derive(GraphQLObject)]
|
||||||
struct Person {
|
struct Person {
|
||||||
name: String,
|
name: String,
|
||||||
age: i32,
|
age: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(juniper::GraphQLObject)]
|
#[derive(GraphQLObject)]
|
||||||
struct House {
|
struct House {
|
||||||
address: Option<String>, // Converted into String (nullable)
|
address: Option<String>, // Converted into String (nullable)
|
||||||
inhabitants: Vec<Person>, // Converted into [Person!]!
|
inhabitants: Vec<Person>, // Converted into [Person!]!
|
||||||
}
|
}
|
||||||
|
#
|
||||||
# fn main() {}
|
# fn main() {}
|
||||||
</code></pre></pre>
|
</code></pre></pre>
|
||||||
<p>Because <code>Person</code> is a valid GraphQL type, you can have a <code>Vec<Person></code> in a
|
<p>Because <code>Person</code> is a valid GraphQL type, you can have a <code>Vec<Person></code> in a
|
||||||
|
@ -242,39 +247,42 @@ objects.</p>
|
||||||
<p>By default, struct fields are converted from Rust's standard <code>snake_case</code> naming
|
<p>By default, struct fields are converted from Rust's standard <code>snake_case</code> naming
|
||||||
convention into GraphQL's <code>camelCase</code> convention:</p>
|
convention into GraphQL's <code>camelCase</code> convention:</p>
|
||||||
<pre><pre class="playpen"><code class="language-rust"># extern crate juniper;
|
<pre><pre class="playpen"><code class="language-rust"># extern crate juniper;
|
||||||
#[derive(juniper::GraphQLObject)]
|
# use juniper::GraphQLObject;
|
||||||
|
#[derive(GraphQLObject)]
|
||||||
struct Person {
|
struct Person {
|
||||||
first_name: String, // Would be exposed as firstName in the GraphQL schema
|
first_name: String, // Would be exposed as firstName in the GraphQL schema
|
||||||
last_name: String, // Exposed as lastName
|
last_name: String, // Exposed as lastName
|
||||||
}
|
}
|
||||||
|
#
|
||||||
# fn main() {}
|
# fn main() {}
|
||||||
</code></pre></pre>
|
</code></pre></pre>
|
||||||
<p>You can override the name by using the <code>graphql</code> attribute on individual struct
|
<p>You can override the name by using the <code>graphql</code> attribute on individual struct
|
||||||
fields:</p>
|
fields:</p>
|
||||||
<pre><pre class="playpen"><code class="language-rust"># extern crate juniper;
|
<pre><pre class="playpen"><code class="language-rust"># extern crate juniper;
|
||||||
#[derive(juniper::GraphQLObject)]
|
# use juniper::GraphQLObject;
|
||||||
|
#[derive(GraphQLObject)]
|
||||||
struct Person {
|
struct Person {
|
||||||
name: String,
|
name: String,
|
||||||
age: i32,
|
age: i32,
|
||||||
#[graphql(name = "websiteURL")]
|
#[graphql(name = "websiteURL")]
|
||||||
website_url: Option<String>, // Now exposed as websiteURL in the schema
|
website_url: Option<String>, // Now exposed as websiteURL in the schema
|
||||||
}
|
}
|
||||||
|
#
|
||||||
# fn main() {}
|
# fn main() {}
|
||||||
</code></pre></pre>
|
</code></pre></pre>
|
||||||
<a class="header" href="#deprecating-fields" id="deprecating-fields"><h2>Deprecating fields</h2></a>
|
<a class="header" href="#deprecating-fields" id="deprecating-fields"><h2>Deprecating fields</h2></a>
|
||||||
<p>To deprecate a field, you specify a deprecation reason using the <code>graphql</code>
|
<p>To deprecate a field, you specify a deprecation reason using the <code>graphql</code>
|
||||||
attribute:</p>
|
attribute:</p>
|
||||||
<pre><pre class="playpen"><code class="language-rust"># extern crate juniper;
|
<pre><pre class="playpen"><code class="language-rust"># extern crate juniper;
|
||||||
#[derive(juniper::GraphQLObject)]
|
# use juniper::GraphQLObject;
|
||||||
|
#[derive(GraphQLObject)]
|
||||||
struct Person {
|
struct Person {
|
||||||
name: String,
|
name: String,
|
||||||
age: i32,
|
age: i32,
|
||||||
#[graphql(deprecated = "Please use the name field instead")]
|
#[graphql(deprecated = "Please use the name field instead")]
|
||||||
first_name: String,
|
first_name: String,
|
||||||
}
|
}
|
||||||
|
#
|
||||||
# fn main() {}
|
# fn main() {}
|
||||||
</code></pre></pre>
|
</code></pre></pre>
|
||||||
<p>The <code>name</code>, <code>description</code>, and <code>deprecation</code> arguments can of course be
|
<p>The <code>name</code>, <code>description</code>, and <code>deprecation</code> arguments can of course be
|
||||||
|
@ -283,7 +291,8 @@ only deprecate object fields and enum values.</p>
|
||||||
<a class="header" href="#skipping-fields" id="skipping-fields"><h2>Skipping fields</h2></a>
|
<a class="header" href="#skipping-fields" id="skipping-fields"><h2>Skipping fields</h2></a>
|
||||||
<p>By default all fields in a <code>GraphQLObject</code> are included in the generated GraphQL type. To prevent including a specific field, annotate the field with <code>#[graphql(skip)]</code>:</p>
|
<p>By default all fields in a <code>GraphQLObject</code> are included in the generated GraphQL type. To prevent including a specific field, annotate the field with <code>#[graphql(skip)]</code>:</p>
|
||||||
<pre><pre class="playpen"><code class="language-rust"># extern crate juniper;
|
<pre><pre class="playpen"><code class="language-rust"># extern crate juniper;
|
||||||
#[derive(juniper::GraphQLObject)]
|
# use juniper::GraphQLObject;
|
||||||
|
#[derive(GraphQLObject)]
|
||||||
struct Person {
|
struct Person {
|
||||||
name: String,
|
name: String,
|
||||||
age: i32,
|
age: i32,
|
||||||
|
@ -291,7 +300,7 @@ struct Person {
|
||||||
# #[allow(dead_code)]
|
# #[allow(dead_code)]
|
||||||
password_hash: String, // This cannot be queried or modified from GraphQL
|
password_hash: String, // This cannot be queried or modified from GraphQL
|
||||||
}
|
}
|
||||||
|
#
|
||||||
# fn main() {}
|
# fn main() {}
|
||||||
</code></pre></pre>
|
</code></pre></pre>
|
||||||
|
|
||||||
|
|
|
@ -160,13 +160,13 @@ use std::{
|
||||||
fs::{File},
|
fs::{File},
|
||||||
io::{Read},
|
io::{Read},
|
||||||
};
|
};
|
||||||
use juniper::FieldResult;
|
use juniper::{graphql_object, FieldResult};
|
||||||
|
|
||||||
struct Example {
|
struct Example {
|
||||||
filename: PathBuf,
|
filename: PathBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[juniper::graphql_object]
|
#[graphql_object]
|
||||||
impl Example {
|
impl Example {
|
||||||
fn contents() -> FieldResult<String> {
|
fn contents() -> FieldResult<String> {
|
||||||
let mut file = File::open(&self.filename)?;
|
let mut file = File::open(&self.filename)?;
|
||||||
|
@ -185,7 +185,7 @@ impl Example {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#
|
||||||
# fn main() {}
|
# fn main() {}
|
||||||
</code></pre></pre>
|
</code></pre></pre>
|
||||||
<p><code>FieldResult<T></code> is an alias for <code>Result<T, FieldError></code>, which is the error
|
<p><code>FieldResult<T></code> is an alias for <code>Result<T, FieldError></code>, which is the error
|
||||||
|
@ -245,14 +245,16 @@ following would be returned:</p>
|
||||||
<p>Sometimes it is desirable to return additional structured error information
|
<p>Sometimes it is desirable to return additional structured error information
|
||||||
to clients. This can be accomplished by implementing <a href="https://docs.rs/juniper/latest/juniper/trait.IntoFieldError.html"><code>IntoFieldError</code></a>:</p>
|
to clients. This can be accomplished by implementing <a href="https://docs.rs/juniper/latest/juniper/trait.IntoFieldError.html"><code>IntoFieldError</code></a>:</p>
|
||||||
<pre><pre class="playpen"><code class="language-rust"># #[macro_use] extern crate juniper;
|
<pre><pre class="playpen"><code class="language-rust"># #[macro_use] extern crate juniper;
|
||||||
|
# use juniper::{graphql_object, FieldError, IntoFieldError, ScalarValue};
|
||||||
|
#
|
||||||
enum CustomError {
|
enum CustomError {
|
||||||
WhateverNotSet,
|
WhateverNotSet,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl juniper::IntoFieldError for CustomError {
|
impl<S: ScalarValue> IntoFieldError<S> for CustomError {
|
||||||
fn into_field_error(self) -> juniper::FieldError {
|
fn into_field_error(self) -> FieldError<S> {
|
||||||
match self {
|
match self {
|
||||||
CustomError::WhateverNotSet => juniper::FieldError::new(
|
CustomError::WhateverNotSet => FieldError::new(
|
||||||
"Whatever does not exist",
|
"Whatever does not exist",
|
||||||
graphql_value!({
|
graphql_value!({
|
||||||
"type": "NO_WHATEVER"
|
"type": "NO_WHATEVER"
|
||||||
|
@ -266,7 +268,7 @@ struct Example {
|
||||||
whatever: Option<bool>,
|
whatever: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[juniper::graphql_object]
|
#[graphql_object]
|
||||||
impl Example {
|
impl Example {
|
||||||
fn whatever() -> Result<bool, CustomError> {
|
fn whatever() -> Result<bool, CustomError> {
|
||||||
if let Some(value) = self.whatever {
|
if let Some(value) = self.whatever {
|
||||||
|
@ -275,18 +277,18 @@ impl Example {
|
||||||
Err(CustomError::WhateverNotSet)
|
Err(CustomError::WhateverNotSet)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#
|
||||||
# fn main() {}
|
# fn main() {}
|
||||||
</code></pre></pre>
|
</code></pre></pre>
|
||||||
<p>The specified structured error information is included in the <a href="https://facebook.github.io/graphql/June2018/#sec-Errors"><code>extensions</code></a> key:</p>
|
<p>The specified structured error information is included in the <a href="https://facebook.github.io/graphql/June2018/#sec-Errors"><code>extensions</code></a> key:</p>
|
||||||
<pre><code class="language-js">{
|
<pre><code class="language-json">{
|
||||||
"errors": [
|
"errors": [{
|
||||||
"message": "Whatever does not exist",
|
"message": "Whatever does not exist",
|
||||||
"locations": [{ "line": 2, "column": 4 }]),
|
"locations": [{"line": 2, "column": 4}],
|
||||||
"extensions": {
|
"extensions": {
|
||||||
"type": "NO_WHATEVER"
|
"type": "NO_WHATEVER"
|
||||||
}
|
}
|
||||||
]
|
}]
|
||||||
}
|
}
|
||||||
</code></pre>
|
</code></pre>
|
||||||
<a class="header" href="#errors-backed-by-graphqls-schema" id="errors-backed-by-graphqls-schema"><h2>Errors Backed by GraphQL's Schema</h2></a>
|
<a class="header" href="#errors-backed-by-graphqls-schema" id="errors-backed-by-graphqls-schema"><h2>Errors Backed by GraphQL's Schema</h2></a>
|
||||||
|
@ -308,24 +310,26 @@ 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
|
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.</p>
|
possible to return a unique string identifier and have the client present a localized string to the user.</p>
|
||||||
<pre><pre class="playpen"><code class="language-rust"># extern crate juniper;
|
<pre><pre class="playpen"><code class="language-rust"># extern crate juniper;
|
||||||
#[derive(juniper::GraphQLObject)]
|
# use juniper::{graphql_object, GraphQLObject, GraphQLUnion};
|
||||||
|
#
|
||||||
|
#[derive(GraphQLObject)]
|
||||||
pub struct Item {
|
pub struct Item {
|
||||||
name: String,
|
name: String,
|
||||||
quantity: i32,
|
quantity: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(juniper::GraphQLObject)]
|
#[derive(GraphQLObject)]
|
||||||
pub struct ValidationError {
|
pub struct ValidationError {
|
||||||
field: String,
|
field: String,
|
||||||
message: String,
|
message: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(juniper::GraphQLObject)]
|
#[derive(GraphQLObject)]
|
||||||
pub struct ValidationErrors {
|
pub struct ValidationErrors {
|
||||||
errors: Vec<ValidationError>,
|
errors: Vec<ValidationError>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(juniper::GraphQLUnion)]
|
#[derive(GraphQLUnion)]
|
||||||
pub enum GraphQLResult {
|
pub enum GraphQLResult {
|
||||||
Ok(Item),
|
Ok(Item),
|
||||||
Err(ValidationErrors),
|
Err(ValidationErrors),
|
||||||
|
@ -333,7 +337,7 @@ pub enum GraphQLResult {
|
||||||
|
|
||||||
pub struct Mutation;
|
pub struct Mutation;
|
||||||
|
|
||||||
#[juniper::graphql_object]
|
#[graphql_object]
|
||||||
impl Mutation {
|
impl Mutation {
|
||||||
fn addItem(&self, name: String, quantity: i32) -> GraphQLResult {
|
fn addItem(&self, name: String, quantity: i32) -> GraphQLResult {
|
||||||
let mut errors = Vec::new();
|
let mut errors = Vec::new();
|
||||||
|
@ -359,7 +363,7 @@ impl Mutation {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#
|
||||||
# fn main() {}
|
# fn main() {}
|
||||||
</code></pre></pre>
|
</code></pre></pre>
|
||||||
<p>Each function may have a different return type and depending on the input
|
<p>Each function may have a different return type and depending on the input
|
||||||
|
@ -395,19 +399,21 @@ field is set if the validation for that particular field fails. You will likely
|
||||||
before. Each resolver function has a custom <code>ValidationResult</code> which
|
before. Each resolver function has a custom <code>ValidationResult</code> which
|
||||||
contains only fields provided by the function.</p>
|
contains only fields provided by the function.</p>
|
||||||
<pre><pre class="playpen"><code class="language-rust"># extern crate juniper;
|
<pre><pre class="playpen"><code class="language-rust"># extern crate juniper;
|
||||||
#[derive(juniper::GraphQLObject)]
|
# use juniper::{graphql_object, GraphQLObject, GraphQLUnion};
|
||||||
|
#
|
||||||
|
#[derive(GraphQLObject)]
|
||||||
pub struct Item {
|
pub struct Item {
|
||||||
name: String,
|
name: String,
|
||||||
quantity: i32,
|
quantity: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(juniper::GraphQLObject)]
|
#[derive(GraphQLObject)]
|
||||||
pub struct ValidationError {
|
pub struct ValidationError {
|
||||||
name: Option<String>,
|
name: Option<String>,
|
||||||
quantity: Option<String>,
|
quantity: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(juniper::GraphQLUnion)]
|
#[derive(GraphQLUnion)]
|
||||||
pub enum GraphQLResult {
|
pub enum GraphQLResult {
|
||||||
Ok(Item),
|
Ok(Item),
|
||||||
Err(ValidationError),
|
Err(ValidationError),
|
||||||
|
@ -415,7 +421,7 @@ pub enum GraphQLResult {
|
||||||
|
|
||||||
pub struct Mutation;
|
pub struct Mutation;
|
||||||
|
|
||||||
#[juniper::graphql_object]
|
#[graphql_object]
|
||||||
impl Mutation {
|
impl Mutation {
|
||||||
fn addItem(&self, name: String, quantity: i32) -> GraphQLResult {
|
fn addItem(&self, name: String, quantity: i32) -> GraphQLResult {
|
||||||
let mut error = ValidationError {
|
let mut error = ValidationError {
|
||||||
|
@ -438,7 +444,7 @@ impl Mutation {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#
|
||||||
# fn main() {}
|
# fn main() {}
|
||||||
</code></pre></pre>
|
</code></pre></pre>
|
||||||
<pre><code class="language-graphql">{
|
<pre><code class="language-graphql">{
|
||||||
|
@ -464,22 +470,23 @@ errors when they occur.</p>
|
||||||
<p>In the following example, a theoretical database could fail
|
<p>In the following example, a theoretical database could fail
|
||||||
and would generate errors. Since it is not common for the database to
|
and would generate errors. Since it is not common for the database to
|
||||||
fail, the corresponding error is returned as a critical error:</p>
|
fail, the corresponding error is returned as a critical error:</p>
|
||||||
<pre><pre class="playpen"><code class="language-rust"># // Only needed due to 2018 edition because the macro is not accessible.
|
<pre><pre class="playpen"><code class="language-rust"># extern crate juniper;
|
||||||
# #[macro_use] extern crate juniper;
|
#
|
||||||
|
use juniper::{graphql_object, graphql_value, FieldError, GraphQLObject, GraphQLUnion, ScalarValue};
|
||||||
|
|
||||||
#[derive(juniper::GraphQLObject)]
|
#[derive(GraphQLObject)]
|
||||||
pub struct Item {
|
pub struct Item {
|
||||||
name: String,
|
name: String,
|
||||||
quantity: i32,
|
quantity: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(juniper::GraphQLObject)]
|
#[derive(GraphQLObject)]
|
||||||
pub struct ValidationErrorItem {
|
pub struct ValidationErrorItem {
|
||||||
name: Option<String>,
|
name: Option<String>,
|
||||||
quantity: Option<String>,
|
quantity: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(juniper::GraphQLUnion)]
|
#[derive(GraphQLUnion)]
|
||||||
pub enum GraphQLResult {
|
pub enum GraphQLResult {
|
||||||
Ok(Item),
|
Ok(Item),
|
||||||
Err(ValidationErrorItem),
|
Err(ValidationErrorItem),
|
||||||
|
@ -489,10 +496,10 @@ pub enum ApiError {
|
||||||
Database,
|
Database,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl juniper::IntoFieldError for ApiError {
|
impl<S: ScalarValue> juniper::IntoFieldError<S> for ApiError {
|
||||||
fn into_field_error(self) -> juniper::FieldError {
|
fn into_field_error(self) -> FieldError<S> {
|
||||||
match self {
|
match self {
|
||||||
ApiError::Database => juniper::FieldError::new(
|
ApiError::Database => FieldError::new(
|
||||||
"Internal database error",
|
"Internal database error",
|
||||||
graphql_value!({
|
graphql_value!({
|
||||||
"type": "DATABASE"
|
"type": "DATABASE"
|
||||||
|
@ -504,7 +511,7 @@ impl juniper::IntoFieldError for ApiError {
|
||||||
|
|
||||||
pub struct Mutation;
|
pub struct Mutation;
|
||||||
|
|
||||||
#[juniper::graphql_object]
|
#[graphql_object]
|
||||||
impl Mutation {
|
impl Mutation {
|
||||||
fn addItem(&self, name: String, quantity: i32) -> Result<GraphQLResult, ApiError> {
|
fn addItem(&self, name: String, quantity: i32) -> Result<GraphQLResult, ApiError> {
|
||||||
let mut error = ValidationErrorItem {
|
let mut error = ValidationErrorItem {
|
||||||
|
@ -527,7 +534,7 @@ impl Mutation {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#
|
||||||
# fn main() {}
|
# fn main() {}
|
||||||
</code></pre></pre>
|
</code></pre></pre>
|
||||||
<a class="header" href="#additional-material" id="additional-material"><h2>Additional Material</h2></a>
|
<a class="header" href="#additional-material" id="additional-material"><h2>Additional Material</h2></a>
|
||||||
|
|
|
@ -147,7 +147,7 @@ integration.</p>
|
||||||
resolvers. Let's say that we have a simple user database in a <code>HashMap</code>:</p>
|
resolvers. Let's say that we have a simple user database in a <code>HashMap</code>:</p>
|
||||||
<pre><pre class="playpen"><code class="language-rust"># #![allow(dead_code)]
|
<pre><pre class="playpen"><code class="language-rust"># #![allow(dead_code)]
|
||||||
# use std::collections::HashMap;
|
# use std::collections::HashMap;
|
||||||
|
#
|
||||||
struct Database {
|
struct Database {
|
||||||
users: HashMap<i32, User>,
|
users: HashMap<i32, User>,
|
||||||
}
|
}
|
||||||
|
@ -157,7 +157,7 @@ struct User {
|
||||||
name: String,
|
name: String,
|
||||||
friend_ids: Vec<i32>,
|
friend_ids: Vec<i32>,
|
||||||
}
|
}
|
||||||
|
#
|
||||||
# fn main() { }
|
# fn main() { }
|
||||||
</code></pre></pre>
|
</code></pre></pre>
|
||||||
<p>We would like a <code>friends</code> field on <code>User</code> that returns a list of <code>User</code> objects.
|
<p>We would like a <code>friends</code> field on <code>User</code> that returns a list of <code>User</code> objects.
|
||||||
|
@ -166,9 +166,10 @@ In order to write such a field though, the database must be queried.</p>
|
||||||
the user object.</p>
|
the user object.</p>
|
||||||
<p>To gain access to the context, we need to specify an argument with the same
|
<p>To gain access to the context, we need to specify an argument with the same
|
||||||
type as the specified <code>Context</code> for the type:</p>
|
type as the specified <code>Context</code> for the type:</p>
|
||||||
<pre><pre class="playpen"><code class="language-rust"># use std::collections::HashMap;
|
<pre><pre class="playpen"><code class="language-rust"># extern crate juniper;
|
||||||
extern crate juniper;
|
# use std::collections::HashMap;
|
||||||
|
# use juniper::graphql_object;
|
||||||
|
#
|
||||||
// This struct represents our context.
|
// This struct represents our context.
|
||||||
struct Database {
|
struct Database {
|
||||||
users: HashMap<i32, User>,
|
users: HashMap<i32, User>,
|
||||||
|
@ -183,11 +184,8 @@ struct User {
|
||||||
friend_ids: Vec<i32>,
|
friend_ids: Vec<i32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Assign Database as the context type for User
|
// Assign Database as the context type for User
|
||||||
#[juniper::graphql_object(
|
#[graphql_object(context = Database)]
|
||||||
Context = Database,
|
|
||||||
)]
|
|
||||||
impl User {
|
impl User {
|
||||||
// 3. Inject the context by specifying an argument
|
// 3. Inject the context by specifying an argument
|
||||||
// with the context type.
|
// with the context type.
|
||||||
|
@ -210,7 +208,7 @@ impl User {
|
||||||
self.id
|
self.id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#
|
||||||
# fn main() { }
|
# fn main() { }
|
||||||
</code></pre></pre>
|
</code></pre></pre>
|
||||||
<p>You only get an immutable reference to the context, so if you want to affect
|
<p>You only get an immutable reference to the context, so if you want to affect
|
||||||
|
|
Loading…
Reference in a new issue