Updated book for master ***NO_CI***
This commit is contained in:
parent
e4f385b088
commit
0853582b4f
4 changed files with 192 additions and 80 deletions
|
@ -1201,40 +1201,14 @@ strings, and booleans. You can create custom scalars to other primitive values,
|
||||||
but this often requires coordination with the client library intended to consume
|
but this often requires coordination with the client library intended to consume
|
||||||
the API you're building.</p>
|
the API you're building.</p>
|
||||||
<p>Since any value going over the wire is eventually transformed into JSON, you're
|
<p>Since any value going over the wire is eventually transformed into JSON, you're
|
||||||
also limited in the data types you can use. Typically, you represent your custom
|
also limited in the data types you can use.</p>
|
||||||
scalars as strings.</p>
|
<p>There are two ways to define custom scalars.</p>
|
||||||
<p>In Juniper, you use the <code>graphql_scalar!</code> macro to create a custom scalar. In
|
<ul>
|
||||||
this example, we're representing a user ID as a string wrapped in a custom type:</p>
|
<li>For simple scalars that just wrap a primitive type, you can use the newtype pattern with
|
||||||
<pre><pre class="playpen"><code class="language-rust">use juniper::Value;
|
a custom derive.</li>
|
||||||
|
<li>For more advanced use cases with custom validation, you can use
|
||||||
struct UserID(String);
|
the <code>graphql_scalar!</code> macro.</li>
|
||||||
|
</ul>
|
||||||
juniper::graphql_scalar!(UserID where Scalar = <S> {
|
|
||||||
description: "An opaque identifier, represented as a string"
|
|
||||||
|
|
||||||
resolve(&self) -> Value {
|
|
||||||
Value::scalar(self.0.clone())
|
|
||||||
}
|
|
||||||
|
|
||||||
from_input_value(v: &InputValue) -> Option<UserID> {
|
|
||||||
// If there's a parse error here, simply return None. Juniper will
|
|
||||||
// present an error to the client.
|
|
||||||
v.as_scalar_value::<String>().map(|s| UserID(s.to_owned()))
|
|
||||||
}
|
|
||||||
|
|
||||||
from_str<'a>(value: ScalarToken<'a>) -> juniper::ParseScalarResult<'a, S> {
|
|
||||||
<String as juniper::ParseScalarValue<S>>::from_str(value)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
#[derive(juniper::GraphQLObject)]
|
|
||||||
struct User {
|
|
||||||
id: UserID,
|
|
||||||
username: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
# fn main() {}
|
|
||||||
</code></pre></pre>
|
|
||||||
<a class="header" href="#built-in-scalars" id="built-in-scalars"><h2>Built-in scalars</h2></a>
|
<a class="header" href="#built-in-scalars" id="built-in-scalars"><h2>Built-in scalars</h2></a>
|
||||||
<p>Juniper has built-in support for:</p>
|
<p>Juniper has built-in support for:</p>
|
||||||
<ul>
|
<ul>
|
||||||
|
@ -1246,11 +1220,93 @@ struct User {
|
||||||
spec</a> as a type that is serialized
|
spec</a> as a type that is serialized
|
||||||
as a string but can be parsed from both a string and an integer.</li>
|
as a string but can be parsed from both a string and an integer.</li>
|
||||||
</ul>
|
</ul>
|
||||||
<a class="header" href="#non-standard-scalars" id="non-standard-scalars"><h3>Non-standard scalars</h3></a>
|
<p><strong>Third party types</strong>:</p>
|
||||||
<p>Juniper has built-in support for UUIDs from the <a href="https://doc.rust-lang.org/uuid/uuid/index.html">uuid
|
<p>Juniper has built-in support for a few additional types from common third party
|
||||||
crate</a>. This support is enabled
|
crates. They are enabled via features that are on by default.</p>
|
||||||
by default, but can be disabled if you want to reduce the number of dependencies
|
<ul>
|
||||||
in your application.</p>
|
<li>uuid::Uuid</li>
|
||||||
|
<li>chrono::DateTime</li>
|
||||||
|
<li>url::Url</li>
|
||||||
|
</ul>
|
||||||
|
<a class="header" href="#newtype-pattern" id="newtype-pattern"><h2>newtype pattern</h2></a>
|
||||||
|
<p>Often, you might need a custom scalar that just wraps an existing type.</p>
|
||||||
|
<p>This can be done with the newtype pattern and a custom derive, similar to how
|
||||||
|
serde supports this pattern with <code>#[serde(transparent)]</code>.</p>
|
||||||
|
<pre><pre class="playpen"><code class="language-rust">#[derive(juniper::GraphQLScalarValue)]
|
||||||
|
pub struct UserId(i32);
|
||||||
|
|
||||||
|
#[derive(juniper::GraphQLObject)]
|
||||||
|
struct User {
|
||||||
|
id: UserId,
|
||||||
|
}
|
||||||
|
|
||||||
|
# fn main() {}
|
||||||
|
</code></pre></pre>
|
||||||
|
<p>That's it, you can now user <code>UserId</code> in your schema.</p>
|
||||||
|
<p>The macro also allows for more customization:</p>
|
||||||
|
<pre><pre class="playpen"><code class="language-rust">/// You can use a doc comment to specify a description.
|
||||||
|
#[derive(juniper::GraphQLScalarValue)]
|
||||||
|
#[graphql(
|
||||||
|
transparent,
|
||||||
|
// Overwrite the GraphQL type name.
|
||||||
|
name = "MyUserId",
|
||||||
|
// Specify a custom description.
|
||||||
|
// A description in the attribute will overwrite a doc comment.
|
||||||
|
description = "My user id description",
|
||||||
|
)]
|
||||||
|
pub struct UserId(i32);
|
||||||
|
|
||||||
|
# fn main() {}
|
||||||
|
</code></pre></pre>
|
||||||
|
<a class="header" href="#custom-scalars" id="custom-scalars"><h2>Custom scalars</h2></a>
|
||||||
|
<p>For more complex situations where you also need custom parsing or validation,
|
||||||
|
you can use the <code>graphql_scalar!</code> macro.</p>
|
||||||
|
<p>Typically, you represent your custom scalars as strings.</p>
|
||||||
|
<p>The example below implements a custom scalar for a custom <code>Date</code> type.</p>
|
||||||
|
<p>Note: juniper already has built-in support for the <code>chrono::DateTime</code> type
|
||||||
|
via <code>chrono</code> feature, which is enabled by default and should be used for this
|
||||||
|
purpose.</p>
|
||||||
|
<p>The example below is used just for illustration.</p>
|
||||||
|
<p><strong>Note</strong>: the example assumes that the <code>Date</code> type implements
|
||||||
|
<code>std::fmt::Display</code> and <code>std::str::FromStr</code>.</p>
|
||||||
|
<pre><pre class="playpen"><code class="language-rust"># mod date {
|
||||||
|
# pub struct Date;
|
||||||
|
# impl std::str::FromStr for Date{
|
||||||
|
# type Err = String; fn from_str(_value: &str) -> Result<Self, Self::Err> { unimplemented!() }
|
||||||
|
# }
|
||||||
|
# // And we define how to represent date as a string.
|
||||||
|
# impl std::fmt::Display for Date {
|
||||||
|
# fn fmt(&self, _f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
# unimplemented!()
|
||||||
|
# }
|
||||||
|
# }
|
||||||
|
# }
|
||||||
|
|
||||||
|
use juniper::{Value, ParseScalarResult, ParseScalarValue};
|
||||||
|
use date::Date;
|
||||||
|
|
||||||
|
juniper::graphql_scalar!(Date where Scalar = <S> {
|
||||||
|
description: "Date"
|
||||||
|
|
||||||
|
// Define how to convert your custom scalar into a primitive type.
|
||||||
|
resolve(&self) -> Value {
|
||||||
|
Value::scalar(self.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Define how to parse a primitive type into your custom scalar.
|
||||||
|
from_input_value(v: &InputValue) -> Option<Date> {
|
||||||
|
v.as_scalar_value::<String>()
|
||||||
|
.and_then(|s| s.parse().ok())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Define how to parse a string value.
|
||||||
|
from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> {
|
||||||
|
<String as ParseScalarValue<S>>::from_str(value)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
# fn main() {}
|
||||||
|
</code></pre></pre>
|
||||||
<a class="header" href="#unions" id="unions"><h1>Unions</h1></a>
|
<a class="header" href="#unions" id="unions"><h1>Unions</h1></a>
|
||||||
<p>From a server's point of view, GraphQL unions are similar to interfaces: the
|
<p>From a server's point of view, GraphQL unions are similar to interfaces: the
|
||||||
only exception is that they don't contain fields on their own.</p>
|
only exception is that they don't contain fields on their own.</p>
|
||||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -142,40 +142,14 @@ strings, and booleans. You can create custom scalars to other primitive values,
|
||||||
but this often requires coordination with the client library intended to consume
|
but this often requires coordination with the client library intended to consume
|
||||||
the API you're building.</p>
|
the API you're building.</p>
|
||||||
<p>Since any value going over the wire is eventually transformed into JSON, you're
|
<p>Since any value going over the wire is eventually transformed into JSON, you're
|
||||||
also limited in the data types you can use. Typically, you represent your custom
|
also limited in the data types you can use.</p>
|
||||||
scalars as strings.</p>
|
<p>There are two ways to define custom scalars.</p>
|
||||||
<p>In Juniper, you use the <code>graphql_scalar!</code> macro to create a custom scalar. In
|
<ul>
|
||||||
this example, we're representing a user ID as a string wrapped in a custom type:</p>
|
<li>For simple scalars that just wrap a primitive type, you can use the newtype pattern with
|
||||||
<pre><pre class="playpen"><code class="language-rust">use juniper::Value;
|
a custom derive.</li>
|
||||||
|
<li>For more advanced use cases with custom validation, you can use
|
||||||
struct UserID(String);
|
the <code>graphql_scalar!</code> macro.</li>
|
||||||
|
</ul>
|
||||||
juniper::graphql_scalar!(UserID where Scalar = <S> {
|
|
||||||
description: "An opaque identifier, represented as a string"
|
|
||||||
|
|
||||||
resolve(&self) -> Value {
|
|
||||||
Value::scalar(self.0.clone())
|
|
||||||
}
|
|
||||||
|
|
||||||
from_input_value(v: &InputValue) -> Option<UserID> {
|
|
||||||
// If there's a parse error here, simply return None. Juniper will
|
|
||||||
// present an error to the client.
|
|
||||||
v.as_scalar_value::<String>().map(|s| UserID(s.to_owned()))
|
|
||||||
}
|
|
||||||
|
|
||||||
from_str<'a>(value: ScalarToken<'a>) -> juniper::ParseScalarResult<'a, S> {
|
|
||||||
<String as juniper::ParseScalarValue<S>>::from_str(value)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
#[derive(juniper::GraphQLObject)]
|
|
||||||
struct User {
|
|
||||||
id: UserID,
|
|
||||||
username: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
# fn main() {}
|
|
||||||
</code></pre></pre>
|
|
||||||
<a class="header" href="#built-in-scalars" id="built-in-scalars"><h2>Built-in scalars</h2></a>
|
<a class="header" href="#built-in-scalars" id="built-in-scalars"><h2>Built-in scalars</h2></a>
|
||||||
<p>Juniper has built-in support for:</p>
|
<p>Juniper has built-in support for:</p>
|
||||||
<ul>
|
<ul>
|
||||||
|
@ -187,11 +161,93 @@ struct User {
|
||||||
spec</a> as a type that is serialized
|
spec</a> as a type that is serialized
|
||||||
as a string but can be parsed from both a string and an integer.</li>
|
as a string but can be parsed from both a string and an integer.</li>
|
||||||
</ul>
|
</ul>
|
||||||
<a class="header" href="#non-standard-scalars" id="non-standard-scalars"><h3>Non-standard scalars</h3></a>
|
<p><strong>Third party types</strong>:</p>
|
||||||
<p>Juniper has built-in support for UUIDs from the <a href="https://doc.rust-lang.org/uuid/uuid/index.html">uuid
|
<p>Juniper has built-in support for a few additional types from common third party
|
||||||
crate</a>. This support is enabled
|
crates. They are enabled via features that are on by default.</p>
|
||||||
by default, but can be disabled if you want to reduce the number of dependencies
|
<ul>
|
||||||
in your application.</p>
|
<li>uuid::Uuid</li>
|
||||||
|
<li>chrono::DateTime</li>
|
||||||
|
<li>url::Url</li>
|
||||||
|
</ul>
|
||||||
|
<a class="header" href="#newtype-pattern" id="newtype-pattern"><h2>newtype pattern</h2></a>
|
||||||
|
<p>Often, you might need a custom scalar that just wraps an existing type.</p>
|
||||||
|
<p>This can be done with the newtype pattern and a custom derive, similar to how
|
||||||
|
serde supports this pattern with <code>#[serde(transparent)]</code>.</p>
|
||||||
|
<pre><pre class="playpen"><code class="language-rust">#[derive(juniper::GraphQLScalarValue)]
|
||||||
|
pub struct UserId(i32);
|
||||||
|
|
||||||
|
#[derive(juniper::GraphQLObject)]
|
||||||
|
struct User {
|
||||||
|
id: UserId,
|
||||||
|
}
|
||||||
|
|
||||||
|
# fn main() {}
|
||||||
|
</code></pre></pre>
|
||||||
|
<p>That's it, you can now user <code>UserId</code> in your schema.</p>
|
||||||
|
<p>The macro also allows for more customization:</p>
|
||||||
|
<pre><pre class="playpen"><code class="language-rust">/// You can use a doc comment to specify a description.
|
||||||
|
#[derive(juniper::GraphQLScalarValue)]
|
||||||
|
#[graphql(
|
||||||
|
transparent,
|
||||||
|
// Overwrite the GraphQL type name.
|
||||||
|
name = "MyUserId",
|
||||||
|
// Specify a custom description.
|
||||||
|
// A description in the attribute will overwrite a doc comment.
|
||||||
|
description = "My user id description",
|
||||||
|
)]
|
||||||
|
pub struct UserId(i32);
|
||||||
|
|
||||||
|
# fn main() {}
|
||||||
|
</code></pre></pre>
|
||||||
|
<a class="header" href="#custom-scalars" id="custom-scalars"><h2>Custom scalars</h2></a>
|
||||||
|
<p>For more complex situations where you also need custom parsing or validation,
|
||||||
|
you can use the <code>graphql_scalar!</code> macro.</p>
|
||||||
|
<p>Typically, you represent your custom scalars as strings.</p>
|
||||||
|
<p>The example below implements a custom scalar for a custom <code>Date</code> type.</p>
|
||||||
|
<p>Note: juniper already has built-in support for the <code>chrono::DateTime</code> type
|
||||||
|
via <code>chrono</code> feature, which is enabled by default and should be used for this
|
||||||
|
purpose.</p>
|
||||||
|
<p>The example below is used just for illustration.</p>
|
||||||
|
<p><strong>Note</strong>: the example assumes that the <code>Date</code> type implements
|
||||||
|
<code>std::fmt::Display</code> and <code>std::str::FromStr</code>.</p>
|
||||||
|
<pre><pre class="playpen"><code class="language-rust"># mod date {
|
||||||
|
# pub struct Date;
|
||||||
|
# impl std::str::FromStr for Date{
|
||||||
|
# type Err = String; fn from_str(_value: &str) -> Result<Self, Self::Err> { unimplemented!() }
|
||||||
|
# }
|
||||||
|
# // And we define how to represent date as a string.
|
||||||
|
# impl std::fmt::Display for Date {
|
||||||
|
# fn fmt(&self, _f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
# unimplemented!()
|
||||||
|
# }
|
||||||
|
# }
|
||||||
|
# }
|
||||||
|
|
||||||
|
use juniper::{Value, ParseScalarResult, ParseScalarValue};
|
||||||
|
use date::Date;
|
||||||
|
|
||||||
|
juniper::graphql_scalar!(Date where Scalar = <S> {
|
||||||
|
description: "Date"
|
||||||
|
|
||||||
|
// Define how to convert your custom scalar into a primitive type.
|
||||||
|
resolve(&self) -> Value {
|
||||||
|
Value::scalar(self.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Define how to parse a primitive type into your custom scalar.
|
||||||
|
from_input_value(v: &InputValue) -> Option<Date> {
|
||||||
|
v.as_scalar_value::<String>()
|
||||||
|
.and_then(|s| s.parse().ok())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Define how to parse a string value.
|
||||||
|
from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> {
|
||||||
|
<String as ParseScalarValue<S>>::from_str(value)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
# fn main() {}
|
||||||
|
</code></pre></pre>
|
||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue