juniper/master/print.html

2399 lines
105 KiB
HTML
Raw Normal View History

2019-03-23 08:08:09 -05:00
<!DOCTYPE HTML>
<html lang="en" class="sidebar-visible no-js light">
2019-03-23 08:08:09 -05:00
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
<title>Juniper - GraphQL Server for Rust</title>
<meta name="robots" content="noindex" />
<!-- Custom HTML head -->
2019-03-23 08:08:09 -05:00
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
<meta name="description" content="Documentation for juniper, a GraphQL server library for Rust.">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#ffffff" />
<link rel="icon" href="favicon.svg">
2021-09-25 23:31:16 -05:00
<link rel="shortcut icon" href="favicon.png">
<link rel="stylesheet" href="css/variables.css">
2019-03-23 08:08:09 -05:00
<link rel="stylesheet" href="css/general.css">
<link rel="stylesheet" href="css/chrome.css">
2021-09-25 23:31:16 -05:00
<link rel="stylesheet" href="css/print.css" media="print">
2019-03-23 08:08:09 -05:00
<!-- Fonts -->
<link rel="stylesheet" href="FontAwesome/css/font-awesome.css">
<link rel="stylesheet" href="fonts/fonts.css">
2019-03-23 08:08:09 -05:00
<!-- Highlight.js Stylesheets -->
<link rel="stylesheet" href="highlight.css">
<link rel="stylesheet" href="tomorrow-night.css">
<link rel="stylesheet" href="ayu-highlight.css">
<!-- Custom theme stylesheets -->
2021-09-25 23:31:16 -05:00
</head>
<body>
2019-03-23 08:08:09 -05:00
<!-- Provide site root to javascript -->
<script type="text/javascript">
var path_to_root = "";
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "navy" : "light";
</script>
2019-03-23 08:08:09 -05:00
<!-- Work around some values being stored in localStorage wrapped in quotes -->
<script type="text/javascript">
try {
var theme = localStorage.getItem('mdbook-theme');
var sidebar = localStorage.getItem('mdbook-sidebar');
if (theme.startsWith('"') && theme.endsWith('"')) {
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
}
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
}
} catch (e) { }
</script>
<!-- Set the theme before any content is loaded, prevents flash -->
<script type="text/javascript">
var theme;
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
if (theme === null || theme === undefined) { theme = default_theme; }
var html = document.querySelector('html');
html.classList.remove('no-js')
html.classList.remove('light')
html.classList.add(theme);
html.classList.add('js');
2019-03-23 08:08:09 -05:00
</script>
<!-- Hide / unhide sidebar before it is displayed -->
<script type="text/javascript">
var html = document.querySelector('html');
var sidebar = 'hidden';
if (document.body.clientWidth >= 1080) {
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
sidebar = sidebar || 'visible';
}
html.classList.remove('sidebar-visible');
html.classList.add("sidebar-" + sidebar);
</script>
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
<div class="sidebar-scrollbox">
<ol class="chapter"><li class="chapter-item expanded affix "><a href="index.html">Introduction</a></li><li class="chapter-item expanded affix "><a href="quickstart.html">Quickstart</a></li><li class="chapter-item expanded affix "><a href="types/index.html">Type System</a></li><li class="chapter-item expanded "><a href="types/objects/defining_objects.html"><strong aria-hidden="true">1.</strong> Defining objects</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="types/objects/complex_fields.html"><strong aria-hidden="true">1.1.</strong> Complex fields</a></li><li class="chapter-item expanded "><a href="types/objects/using_contexts.html"><strong aria-hidden="true">1.2.</strong> Using contexts</a></li><li class="chapter-item expanded "><a href="types/objects/error_handling.html"><strong aria-hidden="true">1.3.</strong> Error handling</a></li></ol></li><li class="chapter-item expanded "><a href="types/other-index.html"><strong aria-hidden="true">2.</strong> Other types</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="types/enums.html"><strong aria-hidden="true">2.1.</strong> Enums</a></li><li class="chapter-item expanded "><a href="types/interfaces.html"><strong aria-hidden="true">2.2.</strong> Interfaces</a></li><li class="chapter-item expanded "><a href="types/input_objects.html"><strong aria-hidden="true">2.3.</strong> Input objects</a></li><li class="chapter-item expanded "><a href="types/scalars.html"><strong aria-hidden="true">2.4.</strong> Scalars</a></li><li class="chapter-item expanded "><a href="types/unions.html"><strong aria-hidden="true">2.5.</strong> Unions</a></li></ol></li></ol>
</div>
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
2019-03-23 08:08:09 -05:00
</nav>
<div id="page-wrapper" class="page-wrapper">
<div class="page">
<div id="menu-bar-hover-placeholder"></div>
<div id="menu-bar" class="menu-bar sticky bordered">
<div class="left-buttons">
<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
<i class="fa fa-bars"></i>
</button>
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
<i class="fa fa-paint-brush"></i>
</button>
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
<li role="none"><button role="menuitem" class="theme" id="light">Light (default)</button></li>
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
</ul>
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
<i class="fa fa-search"></i>
</button>
</div>
<h1 class="menu-title">Juniper - GraphQL Server for Rust</h1>
<div class="right-buttons">
<a href="print.html" title="Print this book" aria-label="Print this book">
<i id="print-button" class="fa fa-print"></i>
</a>
2019-03-23 08:08:09 -05:00
</div>
</div>
2021-09-25 23:31:16 -05:00
<div id="search-wrapper" class="hidden">
2019-03-23 08:08:09 -05:00
<form id="searchbar-outer" class="searchbar-outer">
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
2019-03-23 08:08:09 -05:00
</form>
<div id="searchresults-outer" class="searchresults-outer hidden">
<div id="searchresults-header" class="searchresults-header"></div>
<ul id="searchresults">
</ul>
</div>
</div>
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
<script type="text/javascript">
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
});
</script>
<div id="content" class="content">
<main>
<h1 id="juniper"><a class="header" href="#juniper">Juniper</a></h1>
2019-03-23 08:08:09 -05:00
<p>Juniper is a <a href="http://graphql.org">GraphQL</a> server library for Rust. Build type-safe and fast API
servers with minimal boilerplate and configuration.</p>
<p><a href="http://graphql.org">GraphQL</a> is a data query language developed by Facebook intended to
serve mobile and web application frontends.</p>
<p><em>Juniper</em> makes it possible to write GraphQL servers in Rust that are
type-safe and blazingly fast. We also try to make declaring and resolving
GraphQL schemas as convenient as possible as Rust will allow.</p>
<p>Juniper does not include a web server - instead it provides building blocks to
make integration with existing servers straightforward. It optionally provides a
2021-03-01 21:47:41 -06:00
pre-built integration for the <a href="https://hyper.rs">Hyper</a>, <a href="https://github.com/iron/iron">Iron</a>, <a href="https://rocket.rs">Rocket</a>, and <a href="https://github.com/seanmonstar/warp">Warp</a> frameworks, including
2019-03-23 08:08:09 -05:00
embedded <a href="https://github.com/graphql/graphiql">Graphiql</a> for easy debugging.</p>
<ul>
<li><a href="https://crates.io/crates/juniper">Cargo crate</a></li>
<li><a href="https://docs.rs/juniper">API Reference</a></li>
</ul>
<h2 id="features"><a class="header" href="#features">Features</a></h2>
2019-03-23 08:08:09 -05:00
<p>Juniper supports the full GraphQL query language according to the
<a href="http://facebook.github.io/graphql">specification</a>, including interfaces, unions, schema
introspection, and validations.
It does not, however, support the schema language.</p>
<p>As an exception to other GraphQL libraries for other languages, Juniper builds
non-null types by default. A field of type <code>Vec&lt;Episode&gt;</code> will be converted into
<code>[Episode!]!</code>. The corresponding Rust type for e.g. <code>[Episode]</code> would be
<code>Option&lt;Vec&lt;Option&lt;Episode&gt;&gt;&gt;</code>.</p>
<h2 id="integrations"><a class="header" href="#integrations">Integrations</a></h2>
<h3 id="data-types"><a class="header" href="#data-types">Data types</a></h3>
2019-03-23 08:08:09 -05:00
<p>Juniper has automatic integration with some very common Rust crates to make
building schemas a breeze. The types from these crates will be usable in
your Schemas automatically.</p>
<ul>
<li><a href="https://crates.io/crates/uuid">uuid</a></li>
<li><a href="https://crates.io/crates/url">url</a></li>
<li><a href="https://crates.io/crates/chrono">chrono</a></li>
<li><a href="https://crates.io/crates/bson">bson</a></li>
2019-03-23 08:08:09 -05:00
</ul>
<h3 id="web-frameworks"><a class="header" href="#web-frameworks">Web Frameworks</a></h3>
2019-03-23 08:08:09 -05:00
<ul>
<li><a href="https://hyper.rs">hyper</a></li>
<li><a href="https://rocket.rs">rocket</a></li>
2021-03-01 21:47:41 -06:00
<li><a href="https://github.com/iron/iron">iron</a></li>
2019-03-23 08:08:09 -05:00
<li><a href="https://github.com/seanmonstar/warp">warp</a></li>
</ul>
<h2 id="api-stability"><a class="header" href="#api-stability">API Stability</a></h2>
2019-03-23 08:08:09 -05:00
<p>Juniper has not reached 1.0 yet, thus some API instability should be expected.</p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="quickstart"><a class="header" href="#quickstart">Quickstart</a></h1>
2019-03-23 08:08:09 -05:00
<p>This page will give you a short introduction to the concepts in Juniper.</p>
2020-06-05 22:59:45 -05:00
<p>Juniper follows a <a href="https://blog.logrocket.com/code-first-vs-schema-first-development-graphql/">code-first approach</a> to defining GraphQL schemas. If you would like to use a <a href="https://blog.logrocket.com/code-first-vs-schema-first-development-graphql/">schema-first approach</a> instead, consider <a href="https://github.com/davidpdrsn/juniper-from-schema">juniper-from-schema</a> for generating code from a schema file.</p>
<h2 id="installation"><a class="header" href="#installation">Installation</a></h2>
2019-03-23 08:08:09 -05:00
<pre><code class="language-toml">[dependencies]
juniper = &quot;0.15&quot;
2019-03-23 08:08:09 -05:00
</code></pre>
<h2 id="schema-example"><a class="header" href="#schema-example">Schema example</a></h2>
2019-03-23 08:08:09 -05:00
<p>Exposing simple enums and structs as GraphQL is just a matter of adding a custom
derive attribute to them. Juniper includes support for basic Rust types that
naturally map to GraphQL features, such as <code>Option&lt;T&gt;</code>, <code>Vec&lt;T&gt;</code>, <code>Box&lt;T&gt;</code>,
<code>String</code>, <code>f64</code>, and <code>i32</code>, references, and slices.</p>
<p>For more advanced mappings, Juniper provides multiple macros to map your Rust
types to a GraphQL schema. The most important one is the
2020-07-16 02:55:41 -05:00
<a href="https://docs.rs/juniper/latest/juniper/macro.graphql_object.html">graphql_object</a> procedural macro that is used for declaring an object with
2019-03-23 08:08:09 -05:00
resolvers, which you will use for the <code>Query</code> and <code>Mutation</code> roots.</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused_variables)]
</span><span class="boring">extern crate juniper;
</span><span class="boring">use std::fmt::Display;
</span>use juniper::{
2020-11-06 20:32:22 -06:00
graphql_object, EmptySubscription, FieldResult, GraphQLEnum,
GraphQLInputObject, GraphQLObject, ScalarValue,
};
<span class="boring">
</span><span class="boring">struct DatabasePool;
</span><span class="boring">impl DatabasePool {
</span><span class="boring"> fn get_connection(&amp;self) -&gt; FieldResult&lt;DatabasePool&gt; { Ok(DatabasePool) }
</span><span class="boring"> fn find_human(&amp;self, _id: &amp;str) -&gt; FieldResult&lt;Human&gt; { Err(&quot;&quot;)? }
</span><span class="boring"> fn insert_human(&amp;self, _human: &amp;NewHuman) -&gt; FieldResult&lt;Human&gt; { Err(&quot;&quot;)? }
</span><span class="boring">}
</span>
2020-11-06 20:32:22 -06:00
#[derive(GraphQLEnum)]
2019-03-23 08:08:09 -05:00
enum Episode {
NewHope,
Empire,
Jedi,
}
2020-11-06 20:32:22 -06:00
#[derive(GraphQLObject)]
#[graphql(description = &quot;A humanoid creature in the Star Wars universe&quot;)]
2019-03-23 08:08:09 -05:00
struct Human {
id: String,
name: String,
appears_in: Vec&lt;Episode&gt;,
home_planet: String,
}
// There is also a custom derive for mapping GraphQL input objects.
2020-11-06 20:32:22 -06:00
#[derive(GraphQLInputObject)]
#[graphql(description = &quot;A humanoid creature in the Star Wars universe&quot;)]
2019-03-23 08:08:09 -05:00
struct NewHuman {
name: String,
appears_in: Vec&lt;Episode&gt;,
home_planet: String,
}
// Now, we create our root Query and Mutation types with resolvers by using the
2019-05-14 01:52:54 -05:00
// object macro.
2019-03-23 08:08:09 -05:00
// Objects can have contexts that allow accessing shared state like a database
// pool.
struct Context {
// Use your real database pool here.
pool: DatabasePool,
}
// To make our context usable by Juniper, we have to implement a marker trait.
impl juniper::Context for Context {}
struct Query;
2020-11-06 20:32:22 -06:00
#[graphql_object(
2019-05-14 01:52:54 -05:00
// Here we specify the context type for the object.
// We need to do this in every type that
// needs access to the context.
2020-11-06 20:32:22 -06:00
context = Context,
2019-05-14 01:52:54 -05:00
)]
impl Query {
fn apiVersion() -&gt; &amp;'static str {
2019-03-23 08:08:09 -05:00
&quot;1.0&quot;
}
// Arguments to resolvers can either be simple types or input objects.
2019-05-14 01:52:54 -05:00
// To gain access to the context, we specify a argument
// that is a reference to the Context type.
// Juniper automatically injects the correct context here.
fn human(context: &amp;Context, id: String) -&gt; FieldResult&lt;Human&gt; {
2019-03-23 08:08:09 -05:00
// Get a db connection.
let connection = context.pool.get_connection()?;
// Execute a db query.
// Note the use of `?` to propagate errors.
let human = connection.find_human(&amp;id)?;
// Return the result.
Ok(human)
}
2019-05-14 01:52:54 -05:00
}
// Now, we do the same for our Mutation type.
2019-03-23 08:08:09 -05:00
struct Mutation;
2020-11-06 20:32:22 -06:00
#[graphql_object(
context = Context,
// If we need to use `ScalarValue` parametrization explicitly somewhere
// in the object definition (like here in `FieldResult`), we could
2020-11-06 20:32:22 -06:00
// declare an explicit type parameter for that, and specify it.
scalar = S: ScalarValue + Display,
2020-11-06 20:32:22 -06:00
)]
impl Mutation {
fn createHuman&lt;S: ScalarValue + Display&gt;(context: &amp;Context, new_human: NewHuman) -&gt; FieldResult&lt;Human, S&gt; {
2020-11-06 20:32:22 -06:00
let db = context.pool.get_connection().map_err(|e| e.map_scalar_value())?;
let human: Human = db.insert_human(&amp;new_human).map_err(|e| e.map_scalar_value())?;
2019-03-23 08:08:09 -05:00
Ok(human)
}
2019-05-14 01:52:54 -05:00
}
2019-03-23 08:08:09 -05:00
2020-07-18 20:35:25 -05:00
// A root schema consists of a query, a mutation, and a subscription.
2019-03-23 08:08:09 -05:00
// Request queries can be executed against a RootNode.
2020-03-18 22:40:03 -05:00
type Schema = juniper::RootNode&lt;'static, Query, Mutation, EmptySubscription&lt;Context&gt;&gt;;
<span class="boring">
</span><span class="boring">fn main() {
</span><span class="boring"> let _ = Schema::new(Query, Mutation{}, EmptySubscription::new());
</span><span class="boring">}
</span></code></pre></pre>
2019-03-23 08:08:09 -05:00
<p>We now have a very simple but functional schema for a GraphQL server!</p>
<p>To actually serve the schema, see the guides for our various <a href="./servers/index.html">server integrations</a>.</p>
2020-07-16 02:55:41 -05:00
<p>Juniper is a library that can be used in many contexts--it does not require a server and it does not have a dependency on a particular transport or serialization format. You can invoke the executor directly to get a result for a query:</p>
<h2 id="executor"><a class="header" href="#executor">Executor</a></h2>
2019-03-23 08:08:09 -05:00
<p>You can invoke <code>juniper::execute</code> directly to run a GraphQL query:</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">// Only needed due to 2018 edition because the macro is not accessible.
</span><span class="boring">#[macro_use] extern crate juniper;
</span>use juniper::{
2020-11-06 20:32:22 -06:00
graphql_object, EmptyMutation, EmptySubscription, FieldResult,
2020-12-28 12:04:30 -06:00
GraphQLEnum, Variables, graphql_value,
2020-11-06 20:32:22 -06:00
};
2019-05-14 01:52:54 -05:00
2020-11-06 20:32:22 -06:00
#[derive(GraphQLEnum, Clone, Copy)]
2019-03-23 08:08:09 -05:00
enum Episode {
NewHope,
Empire,
Jedi,
}
2019-05-14 01:52:54 -05:00
// Arbitrary context data.
struct Ctx(Episode);
impl juniper::Context for Ctx {}
2019-03-23 08:08:09 -05:00
struct Query;
2020-11-06 20:32:22 -06:00
#[graphql_object(context = Ctx)]
2019-05-14 01:52:54 -05:00
impl Query {
fn favoriteEpisode(context: &amp;Ctx) -&gt; FieldResult&lt;Episode&gt; {
Ok(context.0)
2019-03-23 08:08:09 -05:00
}
2019-05-14 01:52:54 -05:00
}
2019-03-23 08:08:09 -05:00
2020-07-18 20:35:25 -05:00
// A root schema consists of a query, a mutation, and a subscription.
2019-03-23 08:08:09 -05:00
// Request queries can be executed against a RootNode.
2020-03-18 22:40:03 -05:00
type Schema = juniper::RootNode&lt;'static, Query, EmptyMutation&lt;Ctx&gt;, EmptySubscription&lt;Ctx&gt;&gt;;
2019-03-23 08:08:09 -05:00
fn main() {
// Create a context object.
let ctx = Ctx(Episode::NewHope);
// Run the executor.
2020-03-10 00:45:33 -05:00
let (res, _errors) = juniper::execute_sync(
2019-03-23 08:08:09 -05:00
&quot;query { favoriteEpisode }&quot;,
None,
2020-03-18 22:40:03 -05:00
&amp;Schema::new(Query, EmptyMutation::new(), EmptySubscription::new()),
2019-03-23 08:08:09 -05:00
&amp;Variables::new(),
&amp;ctx,
).unwrap();
// Ensure the value matches.
assert_eq!(
res,
graphql_value!({
2019-04-09 19:26:56 -05:00
&quot;favoriteEpisode&quot;: &quot;NEW_HOPE&quot;,
2019-03-23 08:08:09 -05:00
})
);
}
</code></pre></pre>
<div style="break-before: page; page-break-before: always;"></div><h1 id="type-system"><a class="header" href="#type-system">Type System</a></h1>
<p>Most of the work in working with juniper consists of mapping the
2019-03-23 08:08:09 -05:00
GraphQL type system to the Rust types your application uses.</p>
<p>Juniper provides some convenient abstractions that try to make this process
as painless as possible.</p>
<p>Find out more in the individual chapters below.</p>
<ul>
<li><a href="types/objects/defining_objects.html">Defining objects</a>
2019-03-23 08:08:09 -05:00
<ul>
<li><a href="types/objects/complex_fields.html">Complex fields</a></li>
<li><a href="types/objects/using_contexts.html">Using contexts</a></li>
<li><a href="types/objects/error_handling.html">Error handling</a></li>
2019-03-23 08:08:09 -05:00
</ul>
</li>
<li><a href="types/other-index.html">Other types</a>
2019-03-23 08:08:09 -05:00
<ul>
<li><a href="types/enums.html">Enums</a></li>
<li><a href="types/interfaces.html">Interfaces</a></li>
<li><a href="types/input_objects.html">Input objects</a></li>
<li><a href="types/scalars.html">Scalars</a></li>
<li><a href="types/unions.html">Unions</a></li>
2019-03-23 08:08:09 -05:00
</ul>
</li>
</ul>
<div style="break-before: page; page-break-before: always;"></div><h1 id="defining-objects"><a class="header" href="#defining-objects">Defining objects</a></h1>
2019-03-23 08:08:09 -05:00
<p>While any type in Rust can be exposed as a GraphQL object, the most common one
is a struct.</p>
<p>There are two ways to create a GraphQL object in Juniper. If you've got a simple
struct you want to expose, the easiest way is to use the custom derive
attribute. The other way is described in the <a href="types/objects/complex_fields.html">Complex fields</a>
2019-03-23 08:08:09 -05:00
chapter.</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">extern crate juniper;
</span><span class="boring">use juniper::GraphQLObject;
</span>#[derive(GraphQLObject)]
2019-03-23 08:08:09 -05:00
struct Person {
name: String,
age: i32,
}
<span class="boring">
</span><span class="boring">fn main() {}
</span></code></pre></pre>
2019-03-23 08:08:09 -05:00
<p>This will create a GraphQL object type called <code>Person</code>, with two fields: <code>name</code>
of type <code>String!</code>, and <code>age</code> of type <code>Int!</code>. Because of Rust's type system,
everything is exported as non-null by default. If you need a nullable field, you
can use <code>Option&lt;T&gt;</code>.</p>
<p>We should take advantage of the
fact that GraphQL is self-documenting and add descriptions to the type and
fields. Juniper will automatically use associated doc comments as GraphQL
descriptions:</p>
<p>!FILENAME GraphQL descriptions via Rust doc comments</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">extern crate juniper;
</span><span class="boring">use juniper::GraphQLObject;
</span>#[derive(GraphQLObject)]
2019-03-23 08:08:09 -05:00
/// Information about a person
struct Person {
/// The person's full name, including both first and last names
name: String,
/// The person's age in years, rounded down
age: i32,
}
<span class="boring">
</span><span class="boring">fn main() {}
</span></code></pre></pre>
2019-03-23 08:08:09 -05:00
<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>
<p>!FILENAME GraphQL descriptions via attribute</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">extern crate juniper;
</span><span class="boring">use juniper::GraphQLObject;
</span>#[derive(GraphQLObject)]
2020-11-06 20:32:22 -06:00
#[graphql(description = &quot;Information about a person&quot;)]
2019-03-23 08:08:09 -05:00
struct Person {
2020-11-06 20:32:22 -06:00
#[graphql(description = &quot;The person's full name, including both first and last names&quot;)]
2019-03-23 08:08:09 -05:00
name: String,
2020-11-06 20:32:22 -06:00
#[graphql(description = &quot;The person's age in years, rounded down&quot;)]
2019-03-23 08:08:09 -05:00
age: i32,
}
<span class="boring">
</span><span class="boring">fn main() {}
</span></code></pre></pre>
2019-03-23 08:08:09 -05:00
<p>Descriptions set via the <code>graphql</code> attribute take precedence over Rust
doc comments. This enables internal Rust documentation and external GraphQL
documentation to differ:</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">extern crate juniper;
</span><span class="boring">use juniper::GraphQLObject;
</span>#[derive(GraphQLObject)]
2020-11-06 20:32:22 -06:00
#[graphql(description = &quot;This description shows up in GraphQL&quot;)]
2019-03-23 08:08:09 -05:00
/// This description shows up in RustDoc
struct Person {
2020-11-06 20:32:22 -06:00
#[graphql(description = &quot;This description shows up in GraphQL&quot;)]
2019-03-23 08:08:09 -05:00
/// This description shows up in RustDoc
name: String,
/// This description shows up in both RustDoc and GraphQL
age: i32,
}
<span class="boring">
</span><span class="boring">fn main() {}
</span></code></pre></pre>
<h2 id="relationships"><a class="header" href="#relationships">Relationships</a></h2>
2019-03-23 08:08:09 -05:00
<p>You can only use the custom derive attribute under these circumstances:</p>
<ul>
<li>The annotated type is a <code>struct</code>,</li>
<li>Every struct field is either
<ul>
<li>A primitive type (<code>i32</code>, <code>f64</code>, <code>bool</code>, <code>String</code>, <code>juniper::ID</code>), or</li>
<li>A valid custom GraphQL type, e.g. another struct marked with this attribute,
or</li>
<li>A container/reference containing any of the above, e.g. <code>Vec&lt;T&gt;</code>, <code>Box&lt;T&gt;</code>,
<code>Option&lt;T&gt;</code></li>
</ul>
</li>
</ul>
<p>Let's see what that means for building relationships between objects:</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">extern crate juniper;
</span><span class="boring">use juniper::GraphQLObject;
</span>#[derive(GraphQLObject)]
2019-03-23 08:08:09 -05:00
struct Person {
name: String,
age: i32,
}
2020-11-06 20:32:22 -06:00
#[derive(GraphQLObject)]
2019-03-23 08:08:09 -05:00
struct House {
address: Option&lt;String&gt;, // Converted into String (nullable)
inhabitants: Vec&lt;Person&gt;, // Converted into [Person!]!
}
<span class="boring">
</span><span class="boring">fn main() {}
</span></code></pre></pre>
2019-03-23 08:08:09 -05:00
<p>Because <code>Person</code> is a valid GraphQL type, you can have a <code>Vec&lt;Person&gt;</code> in a
struct and it'll be automatically converted into a list of non-nullable <code>Person</code>
objects.</p>
<h2 id="renaming-fields"><a class="header" href="#renaming-fields">Renaming fields</a></h2>
2019-03-23 08:08:09 -05:00
<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>
<pre><pre class="playground"><code class="language-rust"><span class="boring">extern crate juniper;
</span><span class="boring">use juniper::GraphQLObject;
</span>#[derive(GraphQLObject)]
2019-03-23 08:08:09 -05:00
struct Person {
first_name: String, // Would be exposed as firstName in the GraphQL schema
last_name: String, // Exposed as lastName
}
<span class="boring">
</span><span class="boring">fn main() {}
</span></code></pre></pre>
2019-03-23 08:08:09 -05:00
<p>You can override the name by using the <code>graphql</code> attribute on individual struct
fields:</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">extern crate juniper;
</span><span class="boring">use juniper::GraphQLObject;
</span>#[derive(GraphQLObject)]
2019-03-23 08:08:09 -05:00
struct Person {
name: String,
age: i32,
2020-11-06 20:32:22 -06:00
#[graphql(name = &quot;websiteURL&quot;)]
website_url: Option&lt;String&gt;, // now exposed as `websiteURL` in the schema
}
<span class="boring">
</span><span class="boring">fn main() {}
</span></code></pre></pre>
<p>Or provide a different renaming policy on a struct for all its fields:</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">extern crate juniper;
</span><span class="boring">use juniper::GraphQLObject;
</span>#[derive(GraphQLObject)]
#[graphql(rename_all = &quot;none&quot;)] // disables any renaming
struct Person {
name: String,
age: i32,
website_url: Option&lt;String&gt;, // now exposed as `website_url` in the schema
2019-03-23 08:08:09 -05:00
}
<span class="boring">
</span><span class="boring">fn main() {}
</span></code></pre></pre>
<h2 id="deprecating-fields"><a class="header" href="#deprecating-fields">Deprecating fields</a></h2>
2019-03-23 08:08:09 -05:00
<p>To deprecate a field, you specify a deprecation reason using the <code>graphql</code>
attribute:</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">extern crate juniper;
</span><span class="boring">use juniper::GraphQLObject;
</span>#[derive(GraphQLObject)]
2019-03-23 08:08:09 -05:00
struct Person {
name: String,
age: i32,
2019-05-14 01:52:54 -05:00
#[graphql(deprecated = &quot;Please use the name field instead&quot;)]
2019-03-23 08:08:09 -05:00
first_name: String,
}
<span class="boring">
</span><span class="boring">fn main() {}
</span></code></pre></pre>
2019-03-23 08:08:09 -05:00
<p>The <code>name</code>, <code>description</code>, and <code>deprecation</code> arguments can of course be
combined. Some restrictions from the GraphQL spec still applies though; you can
only deprecate object fields and enum values.</p>
<h2 id="ignoring-fields"><a class="header" href="#ignoring-fields">Ignoring fields</a></h2>
<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(ignore)]</code>:</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">extern crate juniper;
</span><span class="boring">use juniper::GraphQLObject;
</span>#[derive(GraphQLObject)]
2019-03-23 08:08:09 -05:00
struct Person {
name: String,
age: i32,
#[graphql(ignore)]
<span class="boring"> #[allow(dead_code)]
</span> password_hash: String, // cannot be queried or modified from GraphQL
}
<span class="boring">
</span><span class="boring">fn main() {}
</span></code></pre></pre>
<div style="break-before: page; page-break-before: always;"></div><h1 id="complex-fields"><a class="header" href="#complex-fields">Complex fields</a></h1>
2019-03-23 08:08:09 -05:00
<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:
2020-11-06 20:32:22 -06:00
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 GraphQL fields are defined in
this <code>impl</code> block by default. If you want to define normal methods on the struct,
you have to do so either in a separate &quot;normal&quot; <code>impl</code> block, or mark them with
<code>#[graphql(ignore)]</code> attribute to be omitted by the macro. Continuing with the
2019-03-23 08:08:09 -05:00
example from the last chapter, this is how you would define <code>Person</code> using the
macro:</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(dead_code)]
</span><span class="boring">extern crate juniper;
</span><span class="boring">use juniper::graphql_object;
</span><span class="boring">
</span>struct Person {
2019-03-23 08:08:09 -05:00
name: String,
age: i32,
}
2020-11-06 20:32:22 -06:00
#[graphql_object]
2019-05-14 01:52:54 -05:00
impl Person {
fn name(&amp;self) -&gt; &amp;str {
2019-03-23 08:08:09 -05:00
self.name.as_str()
}
2019-05-14 01:52:54 -05:00
fn age(&amp;self) -&gt; i32 {
2019-03-23 08:08:09 -05:00
self.age
}
#[graphql(ignore)]
pub fn hidden_from_graphql(&amp;self) {
// [...]
}
2019-05-14 01:52:54 -05:00
}
2019-03-23 08:08:09 -05:00
2019-12-14 02:34:44 -06:00
impl Person {
pub fn hidden_from_graphql2(&amp;self) {
2019-12-14 02:34:44 -06:00
// [...]
}
}
<span class="boring">
</span><span class="boring">fn main() { }
</span></code></pre></pre>
2019-03-23 08:08:09 -05:00
<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>
<pre><pre class="playground"><code class="language-rust"><span class="boring">extern crate juniper;
</span><span class="boring">use juniper::{graphql_object, GraphQLObject};
</span><span class="boring">
</span>#[derive(GraphQLObject)]
2019-03-23 08:08:09 -05:00
struct Person {
name: String,
age: i32,
}
struct House {
inhabitants: Vec&lt;Person&gt;,
}
2020-11-06 20:32:22 -06:00
#[graphql_object]
2019-05-14 01:52:54 -05:00
impl House {
// Creates the field `inhabitantWithName(name)`, returning a nullable `Person`.
2019-05-14 01:52:54 -05:00
fn inhabitant_with_name(&amp;self, name: String) -&gt; Option&lt;&amp;Person&gt; {
2019-03-23 08:08:09 -05:00
self.inhabitants.iter().find(|p| p.name == name)
}
2019-05-14 01:52:54 -05:00
}
<span class="boring">
</span><span class="boring">fn main() {}
</span></code></pre></pre>
2019-03-23 08:08:09 -05:00
<p>To access global data such as database connections or authentication
information, a <em>context</em> is used. To learn more about this, see the next
chapter: <a href="types/objects/using_contexts.html">Using contexts</a>.</p>
<h2 id="description-renaming-and-deprecation"><a class="header" href="#description-renaming-and-deprecation">Description, renaming, and deprecation</a></h2>
2019-03-23 08:08:09 -05:00
<p>Like with the derive attribute, field names will be converted from <code>snake_case</code>
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>
<pre><pre class="playground"><code class="language-rust"><span class="boring">extern crate juniper;
</span><span class="boring">use juniper::graphql_object;
</span><span class="boring">
</span>struct Person;
2019-03-23 08:08:09 -05:00
2019-05-16 07:19:41 -05:00
/// Doc comments are used as descriptions for GraphQL.
2020-11-06 20:32:22 -06:00
#[graphql_object(
2020-07-14 15:07:30 -05:00
// With this attribute you can change the public GraphQL name of the type.
2019-05-14 01:52:54 -05:00
name = &quot;PersonObject&quot;,
2020-11-06 20:32:22 -06:00
2019-05-16 07:19:41 -05:00
// You can also specify a description here, which will overwrite
// a doc comment description.
description = &quot;...&quot;,
2019-05-14 01:52:54 -05:00
)]
impl Person {
2019-05-16 07:19:41 -05:00
/// A doc comment on the field will also be used for GraphQL.
#[graphql(
// Or provide a description here.
description = &quot;...&quot;,
)]
fn doc_comment(&amp;self) -&gt; &amp;str {
&quot;&quot;
}
// Fields can also be renamed if required.
2020-11-06 20:32:22 -06:00
#[graphql(name = &quot;myCustomFieldName&quot;)]
2019-05-16 07:19:41 -05:00
fn renamed_field() -&gt; bool {
true
}
// Deprecations also work as you'd expect.
// Both the standard Rust syntax and a custom attribute is accepted.
#[deprecated(note = &quot;...&quot;)]
fn deprecated_standard() -&gt; bool {
false
2019-03-23 08:08:09 -05:00
}
2019-05-16 07:19:41 -05:00
#[graphql(deprecated = &quot;...&quot;)]
fn deprecated_graphql() -&gt; bool {
true
}
}
<span class="boring">
</span><span class="boring">fn main() { }
</span></code></pre></pre>
<p>Or provide a different renaming policy on a <code>impl</code> block for all its fields:</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">extern crate juniper;
</span><span class="boring">use juniper::graphql_object;
</span>struct Person;
#[graphql_object(rename_all = &quot;none&quot;)] // disables any renaming
impl Person {
// Now exposed as `renamed_field` in the schema
fn renamed_field() -&gt; bool {
true
}
}
<span class="boring">
</span><span class="boring">fn main() {}
</span></code></pre></pre>
<h2 id="customizing-arguments"><a class="header" href="#customizing-arguments">Customizing arguments</a></h2>
2019-05-16 07:19:41 -05:00
<p>Method field arguments can also be customized.</p>
<p>They can have custom descriptions and default values.</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">extern crate juniper;
</span><span class="boring">use juniper::graphql_object;
</span><span class="boring">
</span>struct Person {}
2019-05-16 07:19:41 -05:00
2020-11-06 20:32:22 -06:00
#[graphql_object]
2019-05-16 07:19:41 -05:00
impl Person {
fn field1(
&amp;self,
#[graphql(
// Arguments can also be renamed if required.
name = &quot;arg&quot;,
// Set a default value which will be injected if not present.
// The default can be any valid Rust expression, including a function call, etc.
default = true,
// Set a description.
description = &quot;The first argument...&quot;
)]
arg1: bool,
// If default expression is not specified then `Default::default()` value is used.
#[graphql(default)]
arg2: i32,
) -&gt; String {
2019-05-16 07:19:41 -05:00
format!(&quot;{} {}&quot;, arg1, arg2)
2019-03-23 08:08:09 -05:00
}
2019-05-14 01:52:54 -05:00
}
<span class="boring">
</span><span class="boring">fn main() { }
</span></code></pre></pre>
<p>Provide a different renaming policy on a <code>impl</code> block also implies for arguments:</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">extern crate juniper;
</span><span class="boring">use juniper::graphql_object;
</span>struct Person;
#[graphql_object(rename_all = &quot;none&quot;)] // disables any renaming
impl Person {
// Now exposed as `my_arg` in the schema
fn field(my_arg: bool) -&gt; bool {
my_arg
}
}
<span class="boring">
</span><span class="boring">fn main() {}
</span></code></pre></pre>
<h2 id="more-features"><a class="header" href="#more-features">More features</a></h2>
<p>These, and more features, are described more thoroughly in <a href="https://docs.rs/juniper/latest/juniper/attr.graphql_object.html">the reference documentation</a>.</p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="using-contexts"><a class="header" href="#using-contexts">Using contexts</a></h1>
2019-03-23 08:08:09 -05:00
<p>The context type is a feature in Juniper that lets field resolvers access global
data, most commonly database connections or authentication information. The
context is usually created from a <em>context factory</em>. How this is defined is
specific to the framework integration you're using, so check out the
documentation for either the <a href="types/objects/../../servers/iron.html">Iron</a> or <a href="types/objects/../../servers/rocket.html">Rocket</a>
2019-03-23 08:08:09 -05:00
integration.</p>
<p>In this chapter, we'll show you how to define a context type and use it in field
resolvers. Let's say that we have a simple user database in a <code>HashMap</code>:</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(dead_code)]
</span><span class="boring">use std::collections::HashMap;
</span><span class="boring">
</span>struct Database {
2019-03-23 08:08:09 -05:00
users: HashMap&lt;i32, User&gt;,
}
struct User {
id: i32,
name: String,
friend_ids: Vec&lt;i32&gt;,
}
<span class="boring">
</span><span class="boring">fn main() { }
</span></code></pre></pre>
2019-03-23 08:08:09 -05:00
<p>We would like a <code>friends</code> field on <code>User</code> that returns a list of <code>User</code> objects.
In order to write such a field though, the database must be queried.</p>
<p>To solve this, we mark the <code>Database</code> as a valid context type and assign it to
the user object. </p>
<p>To gain access to the context, we need to specify an argument with the same
2019-05-14 01:52:54 -05:00
type as the specified <code>Context</code> for the type:</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">extern crate juniper;
</span><span class="boring">use std::collections::HashMap;
</span><span class="boring">use juniper::graphql_object;
</span><span class="boring">
</span>// This struct represents our context.
2019-03-23 08:08:09 -05:00
struct Database {
users: HashMap&lt;i32, User&gt;,
}
2019-05-14 01:52:54 -05:00
// Mark the Database as a valid context type for Juniper
impl juniper::Context for Database {}
2019-03-23 08:08:09 -05:00
struct User {
id: i32,
name: String,
friend_ids: Vec&lt;i32&gt;,
}
2019-05-14 01:52:54 -05:00
// Assign Database as the context type for User
2020-11-06 20:32:22 -06:00
#[graphql_object(context = Database)]
2019-05-14 01:52:54 -05:00
impl User {
// Inject the context by specifying an argument with the context type.
2019-05-14 01:52:54 -05:00
// Note:
// - the type must be a reference
// - the name of the argument SHOULD be `context`
fn friends&lt;'db&gt;(&amp;self, context: &amp;'db Database) -&gt; Vec&lt;&amp;'db User&gt; {
// Use the database to lookup users
2019-03-23 08:08:09 -05:00
self.friend_ids.iter()
2019-05-14 01:52:54 -05:00
.map(|id| context.users.get(id).expect(&quot;Could not find user with ID&quot;))
2019-03-23 08:08:09 -05:00
.collect()
}
2019-05-14 01:52:54 -05:00
fn name(&amp;self) -&gt; &amp;str {
self.name.as_str()
}
fn id(&amp;self) -&gt; i32 {
self.id
}
}
<span class="boring">
</span><span class="boring">fn main() { }
</span></code></pre></pre>
2019-03-23 08:08:09 -05:00
<p>You only get an immutable reference to the context, so if you want to affect
change to the execution, you'll need to use <a href="https://doc.rust-lang.org/book/first-edition/mutability.html#interior-vs-exterior-mutability">interior
mutability</a>
using e.g. <code>RwLock</code> or <code>RefCell</code>.</p>
<h2 id="dealing-with-mutable-references"><a class="header" href="#dealing-with-mutable-references">Dealing with mutable references</a></h2>
<p>Context cannot be specified by a mutable reference, because concurrent fields resolving may be performed. If you have something in your context that requires access by mutable reference, then you need to leverage the <a href="https://doc.rust-lang.org/book/ch15-05-interior-mutability.html">interior mutability</a> for that.</p>
<p>For example, when using async runtime with <a href="https://en.wikipedia.org/wiki/Work_stealing">work stealing</a> (like <code>tokio</code>), which obviously requires thread safety in addition, you will need to use a corresponding async version of <code>RwLock</code>:</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">extern crate juniper;
</span><span class="boring">use std::collections::HashMap;
</span><span class="boring">use juniper::graphql_object;
</span>use tokio::sync::RwLock;
struct Database {
requested_count: HashMap&lt;i32, i32&gt;,
}
// Since we cannot directly implement juniper::Context
// for RwLock we use the newtype idiom
struct DatabaseContext(RwLock&lt;Database&gt;);
impl juniper::Context for DatabaseContext {}
struct User {
id: i32,
name: String
}
#[graphql_object(context=DatabaseContext)]
impl User {
async fn times_requested&lt;'db&gt;(&amp;self, context: &amp;'db DatabaseContext) -&gt; i32 {
// Acquire a mutable reference and await if async RwLock is used,
// which is necessary if context consists async operations like
// querying remote databases.
// Obtain base type
let DatabaseContext(context) = context;
// If context is immutable use .read() on RwLock.
let mut context = context.write().await;
// Preform a mutable operation.
context.requested_count.entry(self.id).and_modify(|e| { *e += 1 }).or_insert(1).clone()
}
fn name(&amp;self) -&gt; &amp;str {
self.name.as_str()
}
fn id(&amp;self) -&gt; i32 {
self.id
}
}
<span class="boring">
</span><span class="boring">fn main() { }
</span></code></pre></pre>
<p>Replace <code>tokio::sync::RwLock</code> with <code>std::sync::RwLock</code> (or similar) if you don't intend to use async resolving.</p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="error-handling"><a class="header" href="#error-handling">Error handling</a></h1>
2020-04-28 12:03:19 -05:00
<p>Error handling in GraphQL can be done in multiple ways. In the
following two different error handling models are discussed: field
results and GraphQL schema backed errors. Each approach has its
advantages. Choosing the right error handling method depends on the
requirements of the application--investigating both approaches is
beneficial.</p>
<h2 id="field-results"><a class="header" href="#field-results">Field Results</a></h2>
2019-03-23 08:08:09 -05:00
<p>Rust
<a href="https://doc.rust-lang.org/book/second-edition/ch09-00-error-handling.html">provides</a>
two ways of dealing with errors: <code>Result&lt;T, E&gt;</code> for recoverable errors and
<code>panic!</code> for unrecoverable errors. Juniper does not do anything about panicking;
it will bubble up to the surrounding framework and hopefully be dealt with
there.</p>
<p>For recoverable errors, Juniper works well with the built-in <code>Result</code> type, you
can use the <code>?</code> operator and things will generally just work as you expect them to:</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">extern crate juniper;
</span>use std::{
2019-03-23 08:08:09 -05:00
str,
path::PathBuf,
fs::{File},
io::{Read},
};
2020-11-06 20:32:22 -06:00
use juniper::{graphql_object, FieldResult};
2019-03-23 08:08:09 -05:00
struct Example {
filename: PathBuf,
}
2020-11-06 20:32:22 -06:00
#[graphql_object]
2019-05-14 01:52:54 -05:00
impl Example {
fn contents(&amp;self) -&gt; FieldResult&lt;String&gt; {
2019-03-23 08:08:09 -05:00
let mut file = File::open(&amp;self.filename)?;
let mut contents = String::new();
file.read_to_string(&amp;mut contents)?;
Ok(contents)
}
2019-05-14 01:52:54 -05:00
fn foo() -&gt; FieldResult&lt;Option&lt;String&gt;&gt; {
// Some invalid bytes.
let invalid = vec![128, 223];
2019-03-23 08:08:09 -05:00
Ok(Some(str::from_utf8(&amp;invalid)?.to_string()))
2019-03-23 08:08:09 -05:00
}
2019-05-14 01:52:54 -05:00
}
<span class="boring">
</span><span class="boring">fn main() {}
</span></code></pre></pre>
2019-03-23 08:08:09 -05:00
<p><code>FieldResult&lt;T&gt;</code> is an alias for <code>Result&lt;T, FieldError&gt;</code>, which is the error
type all fields must return. By using the <code>?</code> operator or <code>try!</code> macro, any type
that implements the <code>Display</code> trait - which are most of the error types out
there - those errors are automatically converted into <code>FieldError</code>.</p>
<h2 id="error-payloads-null-and-partial-errors"><a class="header" href="#error-payloads-null-and-partial-errors">Error payloads, <code>null</code>, and partial errors</a></h2>
2020-07-16 02:17:45 -05:00
<p>Juniper's error behavior conforms to the <a href="https://spec.graphql.org/June2018/#sec-Errors-and-Non-Nullability">GraphQL specification</a>.</p>
2019-03-23 08:08:09 -05:00
<p>When a field returns an error, the field's result is replaced by <code>null</code>, an
additional <code>errors</code> object is created at the top level of the response, and the
execution is resumed. For example, with the previous example and the following
query:</p>
<pre><code class="language-graphql">{
example {
contents
foo
}
}
</code></pre>
<p>If <code>str::from_utf8</code> resulted in a <code>std::str::Utf8Error</code>, the following would be
returned:</p>
<p>!FILENAME Response for nullable field with error</p>
<pre><code class="language-json">{
2019-03-23 08:08:09 -05:00
&quot;data&quot;: {
&quot;example&quot;: {
contents: &quot;&lt;Contents of the file&gt;&quot;,
foo: null
2019-03-23 08:08:09 -05:00
}
},
&quot;errors&quot;: [
&quot;message&quot;: &quot;invalid utf-8 sequence of 2 bytes from index 0&quot;,
&quot;locations&quot;: [{ &quot;line&quot;: 2, &quot;column&quot;: 4 }])
]
}
</code></pre>
<p>If an error is returned from a non-null field, such as the
example above, the <code>null</code> value is propagated up to the first nullable parent
field, or the root <code>data</code> object if there are no nullable fields.</p>
<p>For example, with the following query:</p>
<pre><code class="language-graphql">{
example {
contents
}
}
</code></pre>
<p>If <code>File::open()</code> above resulted in <code>std::io::ErrorKind::PermissionDenied</code>, the
following would be returned:</p>
<p>!FILENAME Response for non-null field with error and no nullable parent</p>
<pre><code class="language-json">{
2019-03-23 08:08:09 -05:00
&quot;errors&quot;: [
&quot;message&quot;: &quot;Permission denied (os error 13)&quot;,
&quot;locations&quot;: [{ &quot;line&quot;: 2, &quot;column&quot;: 4 }])
]
}
</code></pre>
<h3 id="structured-errors"><a class="header" href="#structured-errors">Structured errors</a></h3>
2019-03-23 08:08:09 -05:00
<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>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#[macro_use] extern crate juniper;
</span><span class="boring">use juniper::{graphql_object, FieldError, IntoFieldError, ScalarValue};
</span><span class="boring">
</span>enum CustomError {
2019-03-23 08:08:09 -05:00
WhateverNotSet,
}
2020-11-06 20:32:22 -06:00
impl&lt;S: ScalarValue&gt; IntoFieldError&lt;S&gt; for CustomError {
fn into_field_error(self) -&gt; FieldError&lt;S&gt; {
2019-03-23 08:08:09 -05:00
match self {
2020-11-06 20:32:22 -06:00
CustomError::WhateverNotSet =&gt; FieldError::new(
2019-03-23 08:08:09 -05:00
&quot;Whatever does not exist&quot;,
2019-05-02 12:22:08 -05:00
graphql_value!({
2019-03-23 08:08:09 -05:00
&quot;type&quot;: &quot;NO_WHATEVER&quot;
}),
),
}
}
}
struct Example {
whatever: Option&lt;bool&gt;,
}
2020-11-06 20:32:22 -06:00
#[graphql_object]
2019-05-14 01:52:54 -05:00
impl Example {
fn whatever(&amp;self) -&gt; Result&lt;bool, CustomError&gt; {
if let Some(value) = self.whatever {
return Ok(value);
}
Err(CustomError::WhateverNotSet)
2019-03-23 08:08:09 -05:00
}
2019-05-14 01:52:54 -05:00
}
<span class="boring">
</span><span class="boring">fn main() {}
</span></code></pre></pre>
2019-03-23 08:08:09 -05:00
<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>
2020-11-06 20:32:22 -06:00
<pre><code class="language-json">{
&quot;errors&quot;: [{
2019-03-23 08:08:09 -05:00
&quot;message&quot;: &quot;Whatever does not exist&quot;,
2020-11-06 20:32:22 -06:00
&quot;locations&quot;: [{&quot;line&quot;: 2, &quot;column&quot;: 4}],
2019-03-23 08:08:09 -05:00
&quot;extensions&quot;: {
&quot;type&quot;: &quot;NO_WHATEVER&quot;
}
2020-11-06 20:32:22 -06:00
}]
2019-03-23 08:08:09 -05:00
}
</code></pre>
<h2 id="errors-backed-by-graphqls-schema"><a class="header" href="#errors-backed-by-graphqls-schema">Errors Backed by GraphQL's Schema</a></h2>
2020-04-28 12:03:19 -05:00
<p>Rust's model of errors can be adapted for GraphQL. Rust's panic is
similar to a <code>FieldError</code>--the whole query is aborted and nothing can
be extracted (except for error related information).</p>
<p>Not all errors require this strict handling. Recoverable or partial errors can be put
into the GraphQL schema so the client can intelligently handle them.</p>
<p>To implement this approach, all errors must be partitioned into two error classes:</p>
<ul>
<li>Critical errors that cannot be fixed by the user (e.g. a database error).</li>
<li>Recoverable errors that can be fixed by the user (e.g. invalid input data).</li>
</ul>
<p>Critical errors are returned from resolvers as <code>FieldErrors</code> (from the previous section). Non-critical errors are part of the GraphQL schema and can be handled gracefully by clients. Similar to Rust, GraphQL allows similar error models with unions (see Unions).</p>
<h3 id="example-input-validation-simple"><a class="header" href="#example-input-validation-simple">Example Input Validation (simple)</a></h3>
2020-04-28 12:03:19 -05:00
<p>In this example, basic input validation is implemented with GraphQL
types. Strings are used to identify the problematic field name. Errors
for a particular field are also returned as a string. In this example
the string contains a server-side localized error message. However, it is also
possible to return a unique string identifier and have the client present a localized string to the user.</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">extern crate juniper;
</span><span class="boring">use juniper::{graphql_object, GraphQLObject, GraphQLUnion};
</span><span class="boring">
</span>#[derive(GraphQLObject)]
2020-04-28 12:03:19 -05:00
pub struct Item {
name: String,
quantity: i32,
}
2020-11-06 20:32:22 -06:00
#[derive(GraphQLObject)]
2020-04-28 12:03:19 -05:00
pub struct ValidationError {
field: String,
message: String,
}
2020-11-06 20:32:22 -06:00
#[derive(GraphQLObject)]
2020-04-28 12:03:19 -05:00
pub struct ValidationErrors {
errors: Vec&lt;ValidationError&gt;,
}
2020-11-06 20:32:22 -06:00
#[derive(GraphQLUnion)]
2020-04-28 12:03:19 -05:00
pub enum GraphQLResult {
Ok(Item),
Err(ValidationErrors),
}
pub struct Mutation;
2020-11-06 20:32:22 -06:00
#[graphql_object]
2020-04-28 12:03:19 -05:00
impl Mutation {
fn addItem(&amp;self, name: String, quantity: i32) -&gt; GraphQLResult {
let mut errors = Vec::new();
if !(10 &lt;= name.len() &amp;&amp; name.len() &lt;= 100) {
errors.push(ValidationError {
field: &quot;name&quot;.to_string(),
message: &quot;between 10 and 100&quot;.to_string()
});
}
if !(1 &lt;= quantity &amp;&amp; quantity &lt;= 10) {
errors.push(ValidationError {
field: &quot;quantity&quot;.to_string(),
message: &quot;between 1 and 10&quot;.to_string()
});
}
if errors.is_empty() {
GraphQLResult::Ok(Item { name, quantity })
} else {
GraphQLResult::Err(ValidationErrors { errors })
}
}
}
<span class="boring">
</span><span class="boring">fn main() {}
</span></code></pre></pre>
2020-04-28 12:03:19 -05:00
<p>Each function may have a different return type and depending on the input
parameters a new result type is required. For example, adding a user
requires a new result type which contains the variant <code>Ok(User)</code>
instead of <code>Ok(Item)</code>.</p>
<p>The client can send a mutation request and handle the
resulting errors as shown in the following example:</p>
<pre><code class="language-graphql">{
mutation {
addItem(name: &quot;&quot;, quantity: 0) {
... on Item {
name
}
... on ValidationErrors {
errors {
field
message
}
}
}
}
}
</code></pre>
<p>A useful side effect of this approach is to have partially successful
queries or mutations. If one resolver fails, the results of the
successful resolvers are not discarded.</p>
<h3 id="example-input-validation-complex"><a class="header" href="#example-input-validation-complex">Example Input Validation (complex)</a></h3>
2020-04-28 12:03:19 -05:00
<p>Instead of using strings to propagate errors, it is possible to use
GraphQL's type system to describe the errors more precisely.</p>
<p>For each fallible input variable a field in a GraphQL object is created. The
field is set if the validation for that particular field fails. You will likely want some kind of code generation to reduce repetition as the number of types required is significantly larger than
before. Each resolver function has a custom <code>ValidationResult</code> which
contains only fields provided by the function.</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">extern crate juniper;
</span><span class="boring">use juniper::{graphql_object, GraphQLObject, GraphQLUnion};
</span><span class="boring">
</span>#[derive(GraphQLObject)]
2020-04-28 12:03:19 -05:00
pub struct Item {
name: String,
quantity: i32,
}
2020-11-06 20:32:22 -06:00
#[derive(GraphQLObject)]
2020-04-28 12:03:19 -05:00
pub struct ValidationError {
name: Option&lt;String&gt;,
quantity: Option&lt;String&gt;,
}
2020-11-06 20:32:22 -06:00
#[derive(GraphQLUnion)]
2020-04-28 12:03:19 -05:00
pub enum GraphQLResult {
Ok(Item),
Err(ValidationError),
}
pub struct Mutation;
2020-11-06 20:32:22 -06:00
#[graphql_object]
2020-04-28 12:03:19 -05:00
impl Mutation {
fn addItem(&amp;self, name: String, quantity: i32) -&gt; GraphQLResult {
let mut error = ValidationError {
name: None,
quantity: None,
};
if !(10 &lt;= name.len() &amp;&amp; name.len() &lt;= 100) {
error.name = Some(&quot;between 10 and 100&quot;.to_string());
}
if !(1 &lt;= quantity &amp;&amp; quantity &lt;= 10) {
error.quantity = Some(&quot;between 1 and 10&quot;.to_string());
}
if error.name.is_none() &amp;&amp; error.quantity.is_none() {
GraphQLResult::Ok(Item { name, quantity })
} else {
GraphQLResult::Err(error)
}
}
}
<span class="boring">
</span><span class="boring">fn main() {}
</span></code></pre></pre>
2020-04-28 12:03:19 -05:00
<pre><code class="language-graphql">{
mutation {
addItem {
... on Item {
name
}
... on ValidationErrorsItem {
name
quantity
}
}
}
}
</code></pre>
<p>Expected errors are handled directly inside the query. Additionally, all
non-critical errors are known in advance by both the server and the client.</p>
<h3 id="example-input-validation-complex-with-critical-error"><a class="header" href="#example-input-validation-complex-with-critical-error">Example Input Validation (complex with critical error)</a></h3>
2020-04-28 12:03:19 -05:00
<p>Our examples so far have only included non-critical errors. Providing
errors inside the GraphQL schema still allows you to return unexpected critical
errors when they occur.</p>
<p>In the following example, a theoretical database could fail
and would generate errors. Since it is not common for the database to
fail, the corresponding error is returned as a critical error:</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">extern crate juniper;
</span><span class="boring">
</span>use juniper::{graphql_object, graphql_value, FieldError, GraphQLObject, GraphQLUnion, ScalarValue};
2020-04-28 12:03:19 -05:00
2020-11-06 20:32:22 -06:00
#[derive(GraphQLObject)]
2020-04-28 12:03:19 -05:00
pub struct Item {
name: String,
quantity: i32,
}
2020-11-06 20:32:22 -06:00
#[derive(GraphQLObject)]
2020-04-28 12:03:19 -05:00
pub struct ValidationErrorItem {
name: Option&lt;String&gt;,
quantity: Option&lt;String&gt;,
}
2020-11-06 20:32:22 -06:00
#[derive(GraphQLUnion)]
2020-04-28 12:03:19 -05:00
pub enum GraphQLResult {
Ok(Item),
Err(ValidationErrorItem),
}
pub enum ApiError {
Database,
}
2020-11-06 20:32:22 -06:00
impl&lt;S: ScalarValue&gt; juniper::IntoFieldError&lt;S&gt; for ApiError {
fn into_field_error(self) -&gt; FieldError&lt;S&gt; {
2020-04-28 12:03:19 -05:00
match self {
2020-11-06 20:32:22 -06:00
ApiError::Database =&gt; FieldError::new(
2020-04-28 12:03:19 -05:00
&quot;Internal database error&quot;,
graphql_value!({
&quot;type&quot;: &quot;DATABASE&quot;
}),
),
}
}
}
pub struct Mutation;
2020-11-06 20:32:22 -06:00
#[graphql_object]
2020-04-28 12:03:19 -05:00
impl Mutation {
fn addItem(&amp;self, name: String, quantity: i32) -&gt; Result&lt;GraphQLResult, ApiError&gt; {
let mut error = ValidationErrorItem {
name: None,
quantity: None,
};
if !(10 &lt;= name.len() &amp;&amp; name.len() &lt;= 100) {
error.name = Some(&quot;between 10 and 100&quot;.to_string());
}
if !(1 &lt;= quantity &amp;&amp; quantity &lt;= 10) {
error.quantity = Some(&quot;between 1 and 10&quot;.to_string());
}
if error.name.is_none() &amp;&amp; error.quantity.is_none() {
Ok(GraphQLResult::Ok(Item { name, quantity }))
} else {
Ok(GraphQLResult::Err(error))
}
}
}
<span class="boring">
</span><span class="boring">fn main() {}
</span></code></pre></pre>
<h2 id="additional-material"><a class="header" href="#additional-material">Additional Material</a></h2>
2020-04-28 12:03:19 -05:00
<p>The <a href="https://shopify.dev/docs/admin-api/graphql/reference">Shopify API</a>
implements a similar approach. Their API is a good reference to
explore this approach in a real world application.</p>
<h1 id="comparison"><a class="header" href="#comparison">Comparison</a></h1>
2020-04-28 12:03:19 -05:00
<p>The first approach discussed above--where every error is a critical error defined by <code>FieldResult</code> --is easier to implement. However, the client does not know what errors may occur and must instead infer what happened from the error string. This is brittle and could change over time due to either the client or server changing. Therefore, extensive integration testing between the client and server is required to maintain the implicit contract between the two.</p>
<p>Encoding non-critical errors in the GraphQL schema makes the contract between the client and the server explicit. This allows the client to understand and handle these errors correctly and the server to know when changes are potentially breaking clients. However, encoding this error information into the GraphQL schema requires additional code and up-front definition of non-critical errors.</p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="other-types"><a class="header" href="#other-types">Other Types</a></h1>
2019-03-23 08:08:09 -05:00
<p>The GraphQL type system provides several types in additon to objects.</p>
<p>Find out more about each type below:</p>
<ul>
<li><a href="types/enums.html">Enums</a></li>
<li><a href="types/interfaces.html">Interfaces</a></li>
<li><a href="types/input_objects.html">Input objects</a></li>
<li><a href="types/scalars.html">Scalars</a></li>
<li><a href="types/unions.html">Unions</a></li>
2019-03-23 08:08:09 -05:00
</ul>
<div style="break-before: page; page-break-before: always;"></div><h1 id="enums"><a class="header" href="#enums">Enums</a></h1>
2019-03-23 08:08:09 -05:00
<p>Enums in GraphQL are string constants grouped together to represent a set of
possible values. Simple Rust enums can be converted to GraphQL enums by using a
custom derive attribute:</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">extern crate juniper;
</span>#[derive(juniper::GraphQLEnum)]
2019-03-23 08:08:09 -05:00
enum Episode {
NewHope,
Empire,
Jedi,
}
<span class="boring">fn main() {}
</span></code></pre></pre>
2019-03-23 08:08:09 -05:00
<p>Juniper converts all enum variants to uppercase, so the corresponding string
values for these variants are <code>NEWHOPE</code>, <code>EMPIRE</code>, and <code>JEDI</code>, respectively. If
you want to override this, you can use the <code>graphql</code> attribute, similar to how
it works when <a href="types/objects/defining_objects.html">defining objects</a>:</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">extern crate juniper;
</span>#[derive(juniper::GraphQLEnum)]
2019-03-23 08:08:09 -05:00
enum Episode {
#[graphql(name=&quot;NEW_HOPE&quot;)]
NewHope,
Empire,
Jedi,
}
<span class="boring">fn main() {}
</span></code></pre></pre>
<h2 id="documentation-and-deprecation"><a class="header" href="#documentation-and-deprecation">Documentation and deprecation</a></h2>
2019-03-23 08:08:09 -05:00
<p>Just like when defining objects, the type itself can be renamed and documented,
while individual enum variants can be renamed, documented, and deprecated:</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">extern crate juniper;
</span>#[derive(juniper::GraphQLEnum)]
2019-03-23 08:08:09 -05:00
#[graphql(name=&quot;Episode&quot;, description=&quot;An episode of Star Wars&quot;)]
enum StarWarsEpisode {
#[graphql(deprecated=&quot;We don't really talk about this one&quot;)]
ThePhantomMenace,
#[graphql(name=&quot;NEW_HOPE&quot;)]
NewHope,
#[graphql(description=&quot;Arguably the best one in the trilogy&quot;)]
Empire,
Jedi,
}
<span class="boring">fn main() {}
</span></code></pre></pre>
<h2 id="supported-macro-attributes-derive"><a class="header" href="#supported-macro-attributes-derive">Supported Macro Attributes (Derive)</a></h2>
<table><thead><tr><th>Name of Attribute</th><th align="center">Container Support</th><th align="center">Field Support</th></tr></thead><tbody>
<tr><td>context</td><td align="center"></td><td align="center">?</td></tr>
<tr><td>deprecated</td><td align="center"></td><td align="center"></td></tr>
<tr><td>description</td><td align="center"></td><td align="center"></td></tr>
<tr><td>interfaces</td><td align="center">?</td><td align="center"></td></tr>
<tr><td>name</td><td align="center"></td><td align="center"></td></tr>
<tr><td>noasync</td><td align="center"></td><td align="center">?</td></tr>
<tr><td>scalar</td><td align="center"></td><td align="center">?</td></tr>
<tr><td>skip</td><td align="center">?</td><td align="center"></td></tr>
<tr><td>✔: supported</td><td align="center">✘: not supported</td><td align="center">?: not available</td></tr>
2020-04-18 00:12:14 -05:00
</tbody></table>
<div style="break-before: page; page-break-before: always;"></div><h1 id="interfaces"><a class="header" href="#interfaces">Interfaces</a></h1>
2020-10-06 05:18:31 -05:00
<p><a href="https://spec.graphql.org/June2018/#sec-Interfaces">GraphQL interfaces</a> map well to interfaces known from common object-oriented languages such as Java or C#, but Rust, unfortunately, has no concept that maps perfectly to them. The nearest analogue of <a href="https://spec.graphql.org/June2018/#sec-Interfaces">GraphQL interfaces</a> are Rust traits, and the main difference is that in GraphQL an <a href="https://spec.graphql.org/June2018/#sec-Interfaces">interface type</a> serves both as an <em>abstraction</em> and a <em>boxed value (downcastable to concrete implementers)</em>, while in Rust, a trait is an <em>abstraction only</em> and <em>to represent such a boxed value a separate type is required</em>, like enum or trait object, because Rust trait doesn't represent a type itself, and so can have no values. This difference imposes some unintuitive and non-obvious corner cases when we try to express <a href="https://spec.graphql.org/June2018/#sec-Interfaces">GraphQL interfaces</a> in Rust, but on the other hand gives you full control over which type is backing your interface, and how it's resolved.</p>
<p>For implementing <a href="https://spec.graphql.org/June2018/#sec-Interfaces">GraphQL interfaces</a> Juniper provides the <code>#[graphql_interface]</code> macro.</p>
<h2 id="traits"><a class="header" href="#traits">Traits</a></h2>
2020-10-06 05:18:31 -05:00
<p>Defining a trait is mandatory for defining a <a href="https://spec.graphql.org/June2018/#sec-Interfaces">GraphQL interface</a>, because this is the <em>obvious</em> way we describe an <em>abstraction</em> in Rust. All <a href="https://spec.graphql.org/June2018/#sec-Interfaces">interface</a> fields are defined as computed ones via trait methods.</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">extern crate juniper;
</span>use juniper::graphql_interface;
2019-03-23 08:08:09 -05:00
2020-10-06 05:18:31 -05:00
#[graphql_interface]
trait Character {
fn id(&amp;self) -&gt; &amp;str;
2019-03-23 08:08:09 -05:00
}
<span class="boring">
</span><span class="boring">fn main() {}
</span></code></pre></pre>
2020-10-06 05:18:31 -05:00
<p>However, to return values of such <a href="https://spec.graphql.org/June2018/#sec-Interfaces">interface</a>, we should provide its implementers and the Rust type representing a <em>boxed value of this trait</em>. The last one can be represented in two flavors: enum and <a href="https://doc.rust-lang.org/reference/types/trait-object.html">trait object</a>.</p>
<h3 id="enum-values-default"><a class="header" href="#enum-values-default">Enum values (default)</a></h3>
2020-10-06 05:18:31 -05:00
<p>By default, Juniper generates an enum representing the values of the defined <a href="https://spec.graphql.org/June2018/#sec-Interfaces">GraphQL interface</a>, and names it straightforwardly, <code>{Interface}Value</code>.</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">extern crate juniper;
</span>use juniper::{graphql_interface, GraphQLObject};
2019-03-23 08:08:09 -05:00
2020-10-06 05:18:31 -05:00
#[graphql_interface(for = [Human, Droid])] // enumerating all implementers is mandatory
2019-03-23 08:08:09 -05:00
trait Character {
fn id(&amp;self) -&gt; &amp;str;
}
2020-10-06 05:18:31 -05:00
#[derive(GraphQLObject)]
#[graphql(impl = CharacterValue)] // notice enum name, NOT trait name
struct Human {
id: String,
}
#[graphql_interface] // implementing requires macro attribute too, (°o°)!
2019-03-23 08:08:09 -05:00
impl Character for Human {
2020-10-06 05:18:31 -05:00
fn id(&amp;self) -&gt; &amp;str {
&amp;self.id
}
2019-03-23 08:08:09 -05:00
}
2020-10-06 05:18:31 -05:00
#[derive(GraphQLObject)]
#[graphql(impl = CharacterValue)]
struct Droid {
id: String,
}
#[graphql_interface]
2019-03-23 08:08:09 -05:00
impl Character for Droid {
2020-10-06 05:18:31 -05:00
fn id(&amp;self) -&gt; &amp;str {
&amp;self.id
}
2019-03-23 08:08:09 -05:00
}
<span class="boring">fn main() {
</span>let human = Human { id: &quot;human-32&quot;.to_owned() };
2020-10-06 05:18:31 -05:00
// Values type for interface has `From` implementations for all its implementers,
// so we don't need to bother with enum variant names.
let character: CharacterValue = human.into();
assert_eq!(character.id(), &quot;human-32&quot;);
<span class="boring">}
</span></code></pre></pre>
2020-10-06 05:18:31 -05:00
<p>Also, enum name can be specified explicitly, if desired.</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">extern crate juniper;
</span>use juniper::{graphql_interface, GraphQLObject};
2019-03-23 08:08:09 -05:00
2020-10-06 05:18:31 -05:00
#[graphql_interface(enum = CharaterInterface, for = Human)]
trait Character {
fn id(&amp;self) -&gt; &amp;str;
}
2019-03-23 08:08:09 -05:00
2020-10-06 05:18:31 -05:00
#[derive(GraphQLObject)]
#[graphql(impl = CharaterInterface)]
2019-03-23 08:08:09 -05:00
struct Human {
id: String,
home_planet: String,
}
2020-10-06 05:18:31 -05:00
#[graphql_interface]
impl Character for Human {
fn id(&amp;self) -&gt; &amp;str {
&amp;self.id
}
}
<span class="boring">
</span><span class="boring">fn main() {}
</span></code></pre></pre>
<h3 id="trait-object-values"><a class="header" href="#trait-object-values">Trait object values</a></h3>
2020-10-06 05:18:31 -05:00
<p>If, for some reason, we would like to use <a href="https://doc.rust-lang.org/reference/types/trait-object.html">trait objects</a> for representing <a href="https://spec.graphql.org/June2018/#sec-Interfaces">interface</a> values incorporating dynamic dispatch, then it should be specified explicitly in the trait definition.</p>
<p>Downcasting <a href="https://doc.rust-lang.org/reference/types/trait-object.html">trait objects</a> in Rust is not that trivial, that's why macro transforms the trait definition slightly, imposing some additional type parameters under-the-hood.</p>
<blockquote>
<p><strong>NOTICE</strong>:<br />
A <strong>trait has to be <a href="https://doc.rust-lang.org/stable/reference/items/traits.html#object-safety">object safe</a></strong>, because schema resolvers will need to return a <a href="https://doc.rust-lang.org/reference/types/trait-object.html">trait object</a> to specify a <a href="https://spec.graphql.org/June2018/#sec-Interfaces">GraphQL interface</a> behind it.</p>
</blockquote>
<pre><pre class="playground"><code class="language-rust"><span class="boring">extern crate juniper;
</span><span class="boring">extern crate tokio;
</span>use juniper::{graphql_interface, GraphQLObject};
2020-10-06 05:18:31 -05:00
// `dyn` argument accepts the name of type alias for the required trait object,
// and macro generates this alias automatically.
#[graphql_interface(dyn = DynCharacter, for = Human)]
trait Character {
async fn id(&amp;self) -&gt; &amp;str; // async fields are supported natively
}
2019-03-23 08:08:09 -05:00
2020-10-06 05:18:31 -05:00
#[derive(GraphQLObject)]
#[graphql(impl = DynCharacter&lt;__S&gt;)] // macro adds `ScalarValue` type parameter to trait,
struct Human { // so it may be specified explicitly when required
2019-03-23 08:08:09 -05:00
id: String,
2020-10-06 05:18:31 -05:00
}
#[graphql_interface(dyn)] // implementing requires to know about dynamic dispatch too
impl Character for Human {
async fn id(&amp;self) -&gt; &amp;str {
&amp;self.id
}
2019-03-23 08:08:09 -05:00
}
2020-10-06 05:18:31 -05:00
#[derive(GraphQLObject)]
#[graphql(impl = DynCharacter&lt;__S&gt;)]
struct Droid {
id: String,
}
#[graphql_interface]
impl Character for Droid {
async fn id(&amp;self) -&gt; &amp;str {
&amp;self.id
}
2019-03-23 08:08:09 -05:00
}
<span class="boring">#[tokio::main]
</span><span class="boring">async fn main() {
</span>let human = Human { id: &quot;human-32&quot;.to_owned() };
2020-10-06 05:18:31 -05:00
let character: Box&lt;DynCharacter&gt; = Box::new(human);
assert_eq!(character.id().await, &quot;human-32&quot;);
<span class="boring">}
</span></code></pre></pre>
<h3 id="ignoring-trait-methods"><a class="header" href="#ignoring-trait-methods">Ignoring trait methods</a></h3>
2020-10-06 05:18:31 -05:00
<p>We may want to omit some trait methods to be assumed as <a href="https://spec.graphql.org/June2018/#sec-Interfaces">GraphQL interface</a> fields and ignore them.</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">extern crate juniper;
</span>use juniper::{graphql_interface, GraphQLObject};
2019-03-23 08:08:09 -05:00
2020-10-06 05:18:31 -05:00
#[graphql_interface(for = Human)]
2019-03-23 08:08:09 -05:00
trait Character {
fn id(&amp;self) -&gt; &amp;str;
2020-10-06 05:18:31 -05:00
2020-11-14 07:58:04 -06:00
#[graphql(ignore)] // or `#[graphql(skip)]`, your choice
2020-10-06 05:18:31 -05:00
fn ignored(&amp;self) -&gt; u32 { 0 }
2019-03-23 08:08:09 -05:00
}
2020-10-06 05:18:31 -05:00
#[derive(GraphQLObject)]
#[graphql(impl = CharacterValue)]
struct Human {
id: String,
}
#[graphql_interface]
2019-03-23 08:08:09 -05:00
impl Character for Human {
2020-10-06 05:18:31 -05:00
fn id(&amp;self) -&gt; &amp;str {
&amp;self.id
}
2019-03-23 08:08:09 -05:00
}
<span class="boring">
</span><span class="boring">fn main() {}
</span></code></pre></pre>
<h3 id="fields-arguments-and-interface-customization"><a class="header" href="#fields-arguments-and-interface-customization">Fields, arguments and interface customization</a></h3>
2020-10-06 05:18:31 -05:00
<p>Similarly to <a href="https://spec.graphql.org/June2018/#sec-Objects">GraphQL objects</a> Juniper allows to fully customize <a href="https://spec.graphql.org/June2018/#sec-Interfaces">interface</a> fields and their arguments.</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(deprecated)]
</span><span class="boring">extern crate juniper;
</span>use juniper::graphql_interface;
2020-10-06 05:18:31 -05:00
// Renames the interface in GraphQL schema.
#[graphql_interface(name = &quot;MyCharacter&quot;)]
// Describes the interface in GraphQL schema.
#[graphql_interface(description = &quot;My own character.&quot;)]
// Usual Rust docs are supported too as GraphQL interface description,
// but `description` attribute argument takes precedence over them, if specified.
/// This doc is absent in GraphQL schema.
trait Character {
// Renames the field in GraphQL schema.
2020-11-14 07:58:04 -06:00
#[graphql(name = &quot;myId&quot;)]
2020-10-06 05:18:31 -05:00
// Deprecates the field in GraphQL schema.
// Usual Rust `#[deprecated]` attribute is supported too as field deprecation,
// but `deprecated` attribute argument takes precedence over it, if specified.
2020-11-14 07:58:04 -06:00
#[graphql(deprecated = &quot;Do not use it.&quot;)]
2020-10-06 05:18:31 -05:00
// Describes the field in GraphQL schema.
2020-11-14 07:58:04 -06:00
#[graphql(description = &quot;ID of my own character.&quot;)]
2020-10-06 05:18:31 -05:00
// Usual Rust docs are supported too as field description,
// but `description` attribute argument takes precedence over them, if specified.
/// This description is absent in GraphQL schema.
fn id(
&amp;self,
// Renames the argument in GraphQL schema.
2020-11-14 07:58:04 -06:00
#[graphql(name = &quot;myNum&quot;)]
2020-10-06 05:18:31 -05:00
// Describes the argument in GraphQL schema.
2020-11-14 07:58:04 -06:00
#[graphql(description = &quot;ID number of my own character.&quot;)]
2020-10-06 05:18:31 -05:00
// Specifies the default value for the argument.
// The concrete value may be omitted, and the `Default::default` one
// will be used in such case.
2020-11-14 07:58:04 -06:00
#[graphql(default = 5)]
2020-10-06 05:18:31 -05:00
num: i32,
) -&gt; &amp;str;
}
<span class="boring">
</span><span class="boring">fn main() {}
</span></code></pre></pre>
<p>Renaming policies for all <a href="https://spec.graphql.org/June2018/#sec-Interfaces">GraphQL interface</a> fields and arguments are supported as well:</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(deprecated)]
</span><span class="boring">extern crate juniper;
</span>use juniper::graphql_interface;
#[graphql_interface(rename_all = &quot;none&quot;)] // disables any renaming
trait Character {
// Now exposed as `my_id` and `my_num` in the schema
fn my_id(&amp;self, my_num: i32) -&gt; &amp;str;
}
<span class="boring">
</span><span class="boring">fn main() {}
</span></code></pre></pre>
<h3 id="custom-context"><a class="header" href="#custom-context">Custom context</a></h3>
2020-10-07 03:24:29 -05:00
<p>If a <a href="https://docs.rs/juniper/0.14.2/juniper/trait.Context.html"><code>Context</code></a> is required in a trait method to resolve a <a href="https://spec.graphql.org/June2018/#sec-Interfaces">GraphQL interface</a> field, specify it as an argument.</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">extern crate juniper;
</span><span class="boring">use std::collections::HashMap;
</span>use juniper::{graphql_interface, GraphQLObject};
2019-03-23 08:08:09 -05:00
2020-10-06 05:18:31 -05:00
struct Database {
humans: HashMap&lt;String, Human&gt;,
2019-03-23 08:08:09 -05:00
}
2020-10-06 05:18:31 -05:00
impl juniper::Context for Database {}
2019-03-23 08:08:09 -05:00
2020-10-06 05:18:31 -05:00
#[graphql_interface(for = Human)] // look, ma, context type is inferred! (^o^)
trait Character { // while still can be specified via `Context = ...` attribute argument
// If a field argument is named `context` or `ctx`, it's automatically assumed
// as a context argument.
fn id(&amp;self, context: &amp;Database) -&gt; Option&lt;&amp;str&gt;;
2019-03-23 08:08:09 -05:00
2020-10-06 05:18:31 -05:00
// Otherwise, you may mark it explicitly as a context argument.
2020-11-14 07:58:04 -06:00
fn name(&amp;self, #[graphql(context)] db: &amp;Database) -&gt; Option&lt;&amp;str&gt;;
2020-10-06 05:18:31 -05:00
}
#[derive(GraphQLObject)]
#[graphql(impl = CharacterValue, Context = Database)]
struct Human {
id: String,
name: String,
}
#[graphql_interface]
impl Character for Human {
fn id(&amp;self, db: &amp;Database) -&gt; Option&lt;&amp;str&gt; {
if db.humans.contains_key(&amp;self.id) {
Some(&amp;self.id)
} else {
None
}
2019-03-23 08:08:09 -05:00
}
2020-10-06 05:18:31 -05:00
fn name(&amp;self, db: &amp;Database) -&gt; Option&lt;&amp;str&gt; {
if db.humans.contains_key(&amp;self.id) {
Some(&amp;self.name)
} else {
None
}
}
}
<span class="boring">
</span><span class="boring">fn main() {}
</span></code></pre></pre>
<h3 id="using-executor-and-explicit-generic-scalar"><a class="header" href="#using-executor-and-explicit-generic-scalar">Using executor and explicit generic scalar</a></h3>
2020-10-07 03:24:29 -05:00
<p>If an <a href="https://docs.rs/juniper/latest/juniper/struct.Executor.html"><code>Executor</code></a> is required in a trait method to resolve a <a href="https://spec.graphql.org/June2018/#sec-Interfaces">GraphQL interface</a> field, specify it as an argument.</p>
<p>This requires to explicitly parametrize over <a href="https://docs.rs/juniper/latest/juniper/trait.ScalarValue.html"><code>ScalarValue</code></a>, as <a href="https://docs.rs/juniper/latest/juniper/struct.Executor.html"><code>Executor</code></a> does so. </p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">extern crate juniper;
</span>use juniper::{graphql_interface, Executor, GraphQLObject, LookAheadMethods as _, ScalarValue};
2020-10-06 05:18:31 -05:00
2020-10-07 03:24:29 -05:00
#[graphql_interface(for = Human, Scalar = S)] // notice specifying `ScalarValue` as existing type parameter
2020-10-06 05:18:31 -05:00
trait Character&lt;S: ScalarValue&gt; {
// If a field argument is named `executor`, it's automatically assumed
// as an executor argument.
async fn id&lt;'a&gt;(&amp;self, executor: &amp;'a Executor&lt;'_, '_, (), S&gt;) -&gt; &amp;'a str
where
S: Send + Sync; // required by `#[async_trait]` transformation ¯\_(ツ)_/¯
// Otherwise, you may mark it explicitly as an executor argument.
async fn name&lt;'b&gt;(
&amp;'b self,
2020-11-14 07:58:04 -06:00
#[graphql(executor)] another: &amp;Executor&lt;'_, '_, (), S&gt;,
2020-10-06 05:18:31 -05:00
) -&gt; &amp;'b str
where
S: Send + Sync;
}
#[derive(GraphQLObject)]
#[graphql(impl = CharacterValue&lt;__S&gt;)]
2019-03-23 08:08:09 -05:00
struct Human {
id: String,
2020-10-06 05:18:31 -05:00
name: String,
2019-03-23 08:08:09 -05:00
}
2020-11-14 07:58:04 -06:00
#[graphql_interface(scalar = S)]
2020-10-06 05:18:31 -05:00
impl&lt;S: ScalarValue&gt; Character&lt;S&gt; for Human {
async fn id&lt;'a&gt;(&amp;self, executor: &amp;'a Executor&lt;'_, '_, (), S&gt;) -&gt; &amp;'a str
where
S: Send + Sync,
{
executor.look_ahead().field_name()
}
2019-03-23 08:08:09 -05:00
2020-10-06 05:18:31 -05:00
async fn name&lt;'b&gt;(&amp;'b self, _: &amp;Executor&lt;'_, '_, (), S&gt;) -&gt; &amp;'b str
where
S: Send + Sync,
{
&amp;self.name
}
2019-03-23 08:08:09 -05:00
}
<span class="boring">
</span><span class="boring">fn main() {}
</span></code></pre></pre>
<h3 id="downcasting"><a class="header" href="#downcasting">Downcasting</a></h3>
2020-10-07 03:24:29 -05:00
<p>By default, the <a href="https://spec.graphql.org/June2018/#sec-Interfaces">GraphQL interface</a> value is downcast to one of its implementer types via matching the enum variant or downcasting the trait object (if <code>dyn</code> macro argument is used).</p>
2020-10-06 05:18:31 -05:00
<p>However, if some custom logic is needed to downcast a <a href="https://spec.graphql.org/June2018/#sec-Interfaces">GraphQL interface</a> implementer, you may specify either an external function or a trait method to do so.</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">extern crate juniper;
</span><span class="boring">use std::collections::HashMap;
</span>use juniper::{graphql_interface, GraphQLObject};
2019-03-23 08:08:09 -05:00
struct Database {
droids: HashMap&lt;String, Droid&gt;,
}
impl juniper::Context for Database {}
2020-11-14 07:58:04 -06:00
#[graphql_interface(for = [Human, Droid], context = Database)]
2020-10-06 05:18:31 -05:00
#[graphql_interface(on Droid = get_droid)] // enables downcasting `Droid` via `get_droid()` function
trait Character {
fn id(&amp;self) -&gt; &amp;str;
2020-11-14 07:58:04 -06:00
#[graphql(downcast)] // makes method a downcast to `Human`, not a field
2020-10-06 05:18:31 -05:00
// NOTICE: The method signature may optionally contain `&amp;Database` context argument.
fn as_human(&amp;self) -&gt; Option&lt;&amp;Human&gt; {
None
}
}
#[derive(GraphQLObject)]
#[graphql(impl = CharacterValue, Context = Database)]
struct Human {
2019-03-23 08:08:09 -05:00
id: String,
}
2020-10-06 05:18:31 -05:00
#[graphql_interface]
impl Character for Human {
fn id(&amp;self) -&gt; &amp;str {
&amp;self.id
}
2019-03-23 08:08:09 -05:00
2020-10-06 05:18:31 -05:00
fn as_human(&amp;self) -&gt; Option&lt;&amp;Self&gt; {
Some(self)
}
}
2019-03-23 08:08:09 -05:00
2020-10-06 05:18:31 -05:00
#[derive(GraphQLObject)]
#[graphql(impl = CharacterValue, Context = Database)]
struct Droid {
id: String,
}
#[graphql_interface]
impl Character for Droid {
fn id(&amp;self) -&gt; &amp;str {
&amp;self.id
2019-03-23 08:08:09 -05:00
}
2020-10-06 05:18:31 -05:00
}
2019-03-23 08:08:09 -05:00
2020-10-06 05:18:31 -05:00
// External downcast function doesn't have to be a method of a type.
// It's only a matter of the function signature to match the requirements.
fn get_droid&lt;'db&gt;(ch: &amp;CharacterValue, db: &amp;'db Database) -&gt; Option&lt;&amp;'db Droid&gt; {
db.droids.get(ch.id())
}
<span class="boring">
</span><span class="boring">fn main() {}
</span></code></pre></pre>
2020-10-07 03:24:29 -05:00
<p>The attribute syntax <code>#[graphql_interface(on ImplementerType = resolver_fn)]</code> follows the <a href="https://spec.graphql.org/June2018/#example-5cc55">GraphQL syntax for downcasting interface implementer</a>.</p>
<h2 id="scalarvalue-considerations"><a class="header" href="#scalarvalue-considerations"><code>ScalarValue</code> considerations</a></h2>
2020-10-06 05:18:31 -05:00
<p>By default, <code>#[graphql_interface]</code> macro generates code, which is generic over a <a href="https://docs.rs/juniper/latest/juniper/trait.ScalarValue.html"><code>ScalarValue</code></a> type. This may introduce a problem when at least one of <a href="https://spec.graphql.org/June2018/#sec-Interfaces">GraphQL interface</a> implementers is restricted to a concrete <a href="https://docs.rs/juniper/latest/juniper/trait.ScalarValue.html"><code>ScalarValue</code></a> type in its implementation. To resolve such problem, a concrete <a href="https://docs.rs/juniper/latest/juniper/trait.ScalarValue.html"><code>ScalarValue</code></a> type should be specified.</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">extern crate juniper;
</span>use juniper::{graphql_interface, DefaultScalarValue, GraphQLObject};
2020-10-06 05:18:31 -05:00
#[graphql_interface(for = [Human, Droid])]
2020-11-14 07:58:04 -06:00
#[graphql_interface(scalar = DefaultScalarValue)] // removing this line will fail compilation
2020-10-06 05:18:31 -05:00
trait Character {
fn id(&amp;self) -&gt; &amp;str;
}
#[derive(GraphQLObject)]
2020-10-07 03:24:29 -05:00
#[graphql(impl = CharacterValue, Scalar = DefaultScalarValue)]
2019-03-23 08:08:09 -05:00
struct Human {
id: String,
home_planet: String,
}
2020-11-14 07:58:04 -06:00
#[graphql_interface(scalar = DefaultScalarValue)]
2020-10-06 05:18:31 -05:00
impl Character for Human {
fn id(&amp;self) -&gt; &amp;str {
&amp;self.id
}
}
2019-03-23 08:08:09 -05:00
2020-10-06 05:18:31 -05:00
#[derive(GraphQLObject)]
2020-10-07 03:24:29 -05:00
#[graphql(impl = CharacterValue, Scalar = DefaultScalarValue)]
2019-03-23 08:08:09 -05:00
struct Droid {
id: String,
primary_function: String,
}
2020-11-14 07:58:04 -06:00
#[graphql_interface(scalar = DefaultScalarValue)]
2020-10-06 05:18:31 -05:00
impl Character for Droid {
fn id(&amp;self) -&gt; &amp;str {
&amp;self.id
}
2019-03-23 08:08:09 -05:00
}
<span class="boring">
</span><span class="boring">fn main() {}
</span></code></pre></pre>
<div style="break-before: page; page-break-before: always;"></div><h1 id="input-objects"><a class="header" href="#input-objects">Input objects</a></h1>
2019-03-23 08:08:09 -05:00
<p>Input objects are complex data structures that can be used as arguments to
GraphQL fields. In Juniper, you can define input objects using a custom derive
attribute, similar to simple objects and enums:</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused_variables)]
</span><span class="boring">extern crate juniper;
</span>#[derive(juniper::GraphQLInputObject)]
2019-03-23 08:08:09 -05:00
struct Coordinate {
latitude: f64,
longitude: f64
}
struct Root;
<span class="boring">#[derive(juniper::GraphQLObject)] struct User { name: String }
</span>
#[juniper::graphql_object]
2019-05-14 01:52:54 -05:00
impl Root {
fn users_at_location(coordinate: Coordinate, radius: f64) -&gt; Vec&lt;User&gt; {
2019-03-23 08:08:09 -05:00
// Send coordinate to database
2019-05-14 01:52:54 -05:00
// ...
<span class="boring">unimplemented!()
</span> }
2019-05-14 01:52:54 -05:00
}
2019-03-23 08:08:09 -05:00
<span class="boring">fn main() {}
</span></code></pre></pre>
<h2 id="documentation-and-renaming"><a class="header" href="#documentation-and-renaming">Documentation and renaming</a></h2>
<p>Just like the <a href="types/objects/defining_objects.html">other</a> <a href="types/enums.html">derives</a>, you can rename
2019-03-23 08:08:09 -05:00
and add documentation to both the type and the fields:</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused_variables)]
</span><span class="boring">extern crate juniper;
</span>#[derive(juniper::GraphQLInputObject)]
2019-03-23 08:08:09 -05:00
#[graphql(name=&quot;Coordinate&quot;, description=&quot;A position on the globe&quot;)]
struct WorldCoordinate {
#[graphql(name=&quot;lat&quot;, description=&quot;The latitude&quot;)]
latitude: f64,
#[graphql(name=&quot;long&quot;, description=&quot;The longitude&quot;)]
longitude: f64
}
struct Root;
<span class="boring">#[derive(juniper::GraphQLObject)] struct User { name: String }
</span>
#[juniper::graphql_object]
2019-05-14 01:52:54 -05:00
impl Root {
fn users_at_location(coordinate: WorldCoordinate, radius: f64) -&gt; Vec&lt;User&gt; {
2019-03-23 08:08:09 -05:00
// Send coordinate to database
2019-05-14 01:52:54 -05:00
// ...
<span class="boring">unimplemented!()
</span> }
2019-05-14 01:52:54 -05:00
}
2019-03-23 08:08:09 -05:00
<span class="boring">fn main() {}
</span></code></pre></pre>
<div style="break-before: page; page-break-before: always;"></div><h1 id="scalars"><a class="header" href="#scalars">Scalars</a></h1>
2019-03-23 08:08:09 -05:00
<p>Scalars are the primitive types at the leaves of a GraphQL query: numbers,
strings, and booleans. You can create custom scalars to other primitive values,
but this often requires coordination with the client library intended to consume
the API you're building.</p>
<p>Since any value going over the wire is eventually transformed into JSON, you're
also limited in the data types you can use. </p>
<p>There are two ways to define custom scalars. </p>
2019-06-25 12:02:41 -05:00
<ul>
<li>For simple scalars that just wrap a primitive type, you can use the newtype pattern with
a custom derive. </li>
2019-06-25 12:02:41 -05:00
<li>For more advanced use cases with custom validation, you can use
2020-04-20 15:24:09 -05:00
the <code>graphql_scalar</code> proc macro.</li>
2019-06-25 12:02:41 -05:00
</ul>
<h2 id="built-in-scalars"><a class="header" href="#built-in-scalars">Built-in scalars</a></h2>
2019-06-25 12:02:41 -05:00
<p>Juniper has built-in support for:</p>
<ul>
<li><code>i32</code> as <code>Int</code></li>
<li><code>f64</code> as <code>Float</code></li>
<li><code>String</code> and <code>&amp;str</code> as <code>String</code></li>
<li><code>bool</code> as <code>Boolean</code></li>
<li><code>juniper::ID</code> as <code>ID</code>. This type is defined <a href="http://facebook.github.io/graphql/#sec-ID">in the
spec</a> as a type that is serialized
as a string but can be parsed from both a string and an integer.</li>
</ul>
<p>Note that there is no built-in support for <code>i64</code>/<code>u64</code>, as the GraphQL spec <a href="https://spec.graphql.org/June2018/#sec-Int">doesn't define any built-in scalars for <code>i64</code>/<code>u64</code> by default</a>. You may wish to leverage a <a href="types/scalars.html#custom-scalars">custom GraphQL scalar</a> in your schema to support them.</p>
2019-06-25 12:02:41 -05:00
<p><strong>Third party types</strong>:</p>
<p>Juniper has built-in support for a few additional types from common third party
crates. They are enabled via features that are on by default.</p>
<ul>
<li>uuid::Uuid</li>
<li>chrono::DateTime</li>
<li>url::Url</li>
<li>bson::oid::ObjectId</li>
2019-06-25 12:02:41 -05:00
</ul>
<h2 id="newtype-pattern"><a class="header" href="#newtype-pattern">newtype pattern</a></h2>
<p>Often, you might need a custom scalar that just wraps an existing type. </p>
2019-06-25 12:02:41 -05:00
<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="playground"><code class="language-rust"><span class="boring">extern crate juniper;
</span>#[derive(juniper::GraphQLScalarValue)]
2019-06-25 12:02:41 -05:00
pub struct UserId(i32);
#[derive(juniper::GraphQLObject)]
struct User {
id: UserId,
}
<span class="boring">fn main() {}
</span></code></pre></pre>
2019-06-25 12:02:41 -05:00
<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="playground"><code class="language-rust"><span class="boring">extern crate juniper;
</span>/// You can use a doc comment to specify a description.
2019-06-25 12:02:41 -05:00
#[derive(juniper::GraphQLScalarValue)]
#[graphql(
transparent,
// Overwrite the GraphQL type name.
name = &quot;MyUserId&quot;,
// Specify a custom description.
// A description in the attribute will overwrite a doc comment.
description = &quot;My user id description&quot;,
)]
pub struct UserId(i32);
<span class="boring">fn main() {}
</span></code></pre></pre>
<h2 id="custom-scalars"><a class="header" href="#custom-scalars">Custom scalars</a></h2>
<p>For more complex situations where you also need custom parsing or validation,
2020-04-20 15:24:09 -05:00
you can use the <code>graphql_scalar</code> proc macro.</p>
2019-06-25 12:02:41 -05:00
<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
2019-06-25 12:02:41 -05:00
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="playground"><code class="language-rust"><span class="boring">extern crate juniper;
</span><span class="boring">mod date {
</span><span class="boring"> pub struct Date;
</span><span class="boring"> impl std::str::FromStr for Date{
</span><span class="boring"> type Err = String; fn from_str(_value: &amp;str) -&gt; Result&lt;Self, Self::Err&gt; { unimplemented!() }
</span><span class="boring"> }
</span><span class="boring"> // And we define how to represent date as a string.
</span><span class="boring"> impl std::fmt::Display for Date {
</span><span class="boring"> fn fmt(&amp;self, _f: &amp;mut std::fmt::Formatter) -&gt; std::fmt::Result {
</span><span class="boring"> unimplemented!()
</span><span class="boring"> }
</span><span class="boring"> }
</span><span class="boring">}
</span><span class="boring">
</span>use juniper::{Value, ParseScalarResult, ParseScalarValue};
2019-06-25 12:02:41 -05:00
use date::Date;
2019-03-23 08:08:09 -05:00
2020-04-20 15:24:09 -05:00
#[juniper::graphql_scalar(description = &quot;Date&quot;)]
impl&lt;S&gt; GraphQLScalar for Date
where
S: ScalarValue
{
2019-06-25 12:02:41 -05:00
// Define how to convert your custom scalar into a primitive type.
2020-04-20 15:24:09 -05:00
fn resolve(&amp;self) -&gt; Value {
2019-06-25 12:02:41 -05:00
Value::scalar(self.to_string())
2019-03-23 08:08:09 -05:00
}
2019-06-25 12:02:41 -05:00
// Define how to parse a primitive type into your custom scalar.
// NOTE: The error type should implement `IntoFieldError&lt;S&gt;`.
fn from_input_value(v: &amp;InputValue) -&gt; Result&lt;Date, String&gt; {
v.as_string_value()
.ok_or_else(|| format!(&quot;Expected `String`, found: {}&quot;, v))
.and_then(|s| s.parse().map_err(|e| format!(&quot;Failed to parse `Date`: {}&quot;, e)))
2019-03-23 08:08:09 -05:00
}
2019-06-25 12:02:41 -05:00
// Define how to parse a string value.
2020-04-20 15:24:09 -05:00
fn from_str&lt;'a&gt;(value: ScalarToken&lt;'a&gt;) -&gt; ParseScalarResult&lt;'a, S&gt; {
2019-06-25 12:02:41 -05:00
&lt;String as ParseScalarValue&lt;S&gt;&gt;::from_str(value)
2019-03-23 08:08:09 -05:00
}
2020-04-20 15:24:09 -05:00
}
<span class="boring">
</span><span class="boring">fn main() {}
</span></code></pre></pre>
<div style="break-before: page; page-break-before: always;"></div><h1 id="unions"><a class="header" href="#unions">Unions</a></h1>
2020-10-07 03:24:29 -05:00
<p>From the server's point of view, <a href="https://spec.graphql.org/June2018/#sec-Unions">GraphQL unions</a> are somewhat similar to <a href="https://spec.graphql.org/June2018/#sec-Interfaces">interfaces</a> - the main difference is that they don't contain fields on their own.</p>
<p>The most obvious and straightforward way to represent a <a href="https://spec.graphql.org/June2018/#sec-Unions">GraphQL union</a> in Rust is enum. However, we also can do so either with trait or a regular struct. That's why, for implementing <a href="https://spec.graphql.org/June2018/#sec-Unions">GraphQL unions</a> Juniper provides:</p>
2020-06-04 03:28:07 -05:00
<ul>
<li><code>#[derive(GraphQLUnion)]</code> macro for enums and structs.</li>
<li><code>#[graphql_union]</code> for traits.</li>
</ul>
<h2 id="enums-1"><a class="header" href="#enums-1">Enums</a></h2>
2020-06-04 03:28:07 -05:00
<p>Most of the time, we just need a trivial and straightforward Rust enum to represent a <a href="https://spec.graphql.org/June2018/#sec-Unions">GraphQL union</a>.</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">extern crate juniper;
</span><span class="boring">extern crate derive_more;
</span>use derive_more::From;
2020-06-04 03:28:07 -05:00
use juniper::{GraphQLObject, GraphQLUnion};
#[derive(GraphQLObject)]
2019-03-23 08:08:09 -05:00
struct Human {
id: String,
home_planet: String,
}
2020-06-04 03:28:07 -05:00
#[derive(GraphQLObject)]
2019-03-23 08:08:09 -05:00
struct Droid {
id: String,
primary_function: String,
}
2020-06-04 03:28:07 -05:00
#[derive(From, GraphQLUnion)]
enum Character {
Human(Human),
Droid(Droid),
}
<span class="boring">
</span><span class="boring">fn main() {}
</span></code></pre></pre>
<h3 id="ignoring-enum-variants"><a class="header" href="#ignoring-enum-variants">Ignoring enum variants</a></h3>
2020-06-04 03:28:07 -05:00
<p>In some rare situations we may want to omit exposing an enum variant in the GraphQL schema.</p>
<p>As an example, let's consider the situation where we need to bind some type parameter <code>T</code> for doing interesting type-level stuff in our resolvers. To achieve this we need to have <code>PhantomData&lt;T&gt;</code>, but we don't want it exposed in the GraphQL schema.</p>
<blockquote>
<p><strong>WARNING</strong>:<br />
It's the <em>library user's responsibility</em> to ensure that ignored enum variant is <em>never</em> returned from resolvers, otherwise resolving the GraphQL query will <strong>panic at runtime</strong>.</p>
</blockquote>
<pre><pre class="playground"><code class="language-rust"><span class="boring">extern crate juniper;
</span><span class="boring">extern crate derive_more;
</span><span class="boring">use std::marker::PhantomData;
</span>use derive_more::From;
2020-06-04 03:28:07 -05:00
use juniper::{GraphQLObject, GraphQLUnion};
#[derive(GraphQLObject)]
struct Human {
id: String,
home_planet: String,
2019-03-23 08:08:09 -05:00
}
2020-06-04 03:28:07 -05:00
#[derive(GraphQLObject)]
struct Droid {
id: String,
primary_function: String,
2019-03-23 08:08:09 -05:00
}
2020-06-04 03:28:07 -05:00
#[derive(From, GraphQLUnion)]
enum Character&lt;S&gt; {
Human(Human),
Droid(Droid),
#[from(ignore)]
#[graphql(ignore)] // or `#[graphql(skip)]`, your choice
_State(PhantomData&lt;S&gt;),
2019-03-23 08:08:09 -05:00
}
<span class="boring">
</span><span class="boring">fn main() {}
</span></code></pre></pre>
<h3 id="external-resolver-functions"><a class="header" href="#external-resolver-functions">External resolver functions</a></h3>
2020-06-04 03:28:07 -05:00
<p>If some custom logic is needed to resolve a <a href="https://spec.graphql.org/June2018/#sec-Unions">GraphQL union</a> variant, you may specify an external function to do so:</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(dead_code)]
</span><span class="boring">extern crate juniper;
</span>use juniper::{GraphQLObject, GraphQLUnion};
2019-03-23 08:08:09 -05:00
2020-06-04 03:28:07 -05:00
#[derive(GraphQLObject)]
#[graphql(Context = CustomContext)]
struct Human {
id: String,
home_planet: String,
}
#[derive(GraphQLObject)]
#[graphql(Context = CustomContext)]
struct Droid {
id: String,
primary_function: String,
}
pub struct CustomContext {
droid: Droid,
}
impl juniper::Context for CustomContext {}
#[derive(GraphQLUnion)]
#[graphql(Context = CustomContext)]
enum Character {
Human(Human),
#[graphql(with = Character::droid_from_context)]
Droid(Droid),
}
impl Character {
// NOTICE: The function signature must contain `&amp;self` and `&amp;Context`,
// and return `Option&lt;&amp;VariantType&gt;`.
fn droid_from_context&lt;'c&gt;(&amp;self, ctx: &amp;'c CustomContext) -&gt; Option&lt;&amp;'c Droid&gt; {
Some(&amp;ctx.droid)
2019-03-23 08:08:09 -05:00
}
}
<span class="boring">
</span><span class="boring">fn main() {}
</span></code></pre></pre>
2020-06-04 03:28:07 -05:00
<p>With an external resolver function we can even declare a new <a href="https://spec.graphql.org/June2018/#sec-Unions">GraphQL union</a> variant where the Rust type is absent in the initial enum definition. The attribute syntax <code>#[graphql(on VariantType = resolver_fn)]</code> follows the <a href="https://spec.graphql.org/June2018/#example-f8163">GraphQL syntax for dispatching union variants</a>.</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(dead_code)]
</span><span class="boring">extern crate juniper;
</span>use juniper::{GraphQLObject, GraphQLUnion};
2020-06-04 03:28:07 -05:00
#[derive(GraphQLObject)]
#[graphql(Context = CustomContext)]
struct Human {
id: String,
home_planet: String,
}
#[derive(GraphQLObject)]
#[graphql(Context = CustomContext)]
struct Droid {
id: String,
primary_function: String,
}
#[derive(GraphQLObject)]
#[graphql(Context = CustomContext)]
struct Ewok {
id: String,
is_funny: bool,
}
2019-03-23 08:08:09 -05:00
2020-06-04 03:28:07 -05:00
pub struct CustomContext {
ewok: Ewok,
}
impl juniper::Context for CustomContext {}
#[derive(GraphQLUnion)]
#[graphql(Context = CustomContext)]
#[graphql(on Ewok = Character::ewok_from_context)]
enum Character {
Human(Human),
Droid(Droid),
#[graphql(ignore)] // or `#[graphql(skip)]`, your choice
Ewok,
}
impl Character {
fn ewok_from_context&lt;'c&gt;(&amp;self, ctx: &amp;'c CustomContext) -&gt; Option&lt;&amp;'c Ewok&gt; {
if let Self::Ewok = self {
Some(&amp;ctx.ewok)
} else {
None
}
}
}
<span class="boring">
</span><span class="boring">fn main() {}
</span></code></pre></pre>
<h2 id="structs"><a class="header" href="#structs">Structs</a></h2>
2020-06-04 03:28:07 -05:00
<p>Using Rust structs as <a href="https://spec.graphql.org/June2018/#sec-Unions">GraphQL unions</a> is very similar to using enums, with the nuance that specifying an external resolver function is the only way to declare a <a href="https://spec.graphql.org/June2018/#sec-Unions">GraphQL union</a> variant.</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">extern crate juniper;
</span><span class="boring">use std::collections::HashMap;
</span>use juniper::{GraphQLObject, GraphQLUnion};
2020-06-04 03:28:07 -05:00
#[derive(GraphQLObject)]
2019-05-14 01:52:54 -05:00
#[graphql(Context = Database)]
2019-03-23 08:08:09 -05:00
struct Human {
id: String,
home_planet: String,
}
2020-06-04 03:28:07 -05:00
#[derive(GraphQLObject)]
2019-05-14 01:52:54 -05:00
#[graphql(Context = Database)]
2019-03-23 08:08:09 -05:00
struct Droid {
id: String,
primary_function: String,
}
struct Database {
humans: HashMap&lt;String, Human&gt;,
droids: HashMap&lt;String, Droid&gt;,
}
impl juniper::Context for Database {}
2020-06-04 03:28:07 -05:00
#[derive(GraphQLUnion)]
#[graphql(
Context = Database,
on Human = Character::get_human,
on Droid = Character::get_droid,
)]
struct Character {
id: String,
2019-03-23 08:08:09 -05:00
}
2020-06-04 03:28:07 -05:00
impl Character {
fn get_human&lt;'db&gt;(&amp;self, ctx: &amp;'db Database) -&gt; Option&lt;&amp;'db Human&gt;{
ctx.humans.get(&amp;self.id)
}
fn get_droid&lt;'db&gt;(&amp;self, ctx: &amp;'db Database) -&gt; Option&lt;&amp;'db Droid&gt;{
ctx.droids.get(&amp;self.id)
}
}
<span class="boring">
</span><span class="boring">fn main() {}
</span></code></pre></pre>
<h2 id="traits-1"><a class="header" href="#traits-1">Traits</a></h2>
2020-06-04 03:28:07 -05:00
<p>To use a Rust trait definition as a <a href="https://spec.graphql.org/June2018/#sec-Unions">GraphQL union</a> you need to use the <code>#[graphql_union]</code> macro. <a href="https://doc.rust-lang.org/stable/reference/procedural-macros.html#derive-macros">Rust doesn't allow derive macros on traits</a>, so using <code>#[derive(GraphQLUnion)]</code> on traits doesn't work.</p>
<blockquote>
<p><strong>NOTICE</strong>:<br />
A <strong>trait has to be <a href="https://doc.rust-lang.org/stable/reference/items/traits.html#object-safety">object safe</a></strong>, because schema resolvers will need to return a <a href="https://doc.rust-lang.org/stable/reference/types/trait-object.html">trait object</a> to specify a <a href="https://spec.graphql.org/June2018/#sec-Unions">GraphQL union</a> behind it.</p>
</blockquote>
<pre><pre class="playground"><code class="language-rust"><span class="boring">extern crate juniper;
</span>use juniper::{graphql_union, GraphQLObject};
2020-06-04 03:28:07 -05:00
#[derive(GraphQLObject)]
struct Human {
id: String,
home_planet: String,
2019-03-23 08:08:09 -05:00
}
2020-06-04 03:28:07 -05:00
#[derive(GraphQLObject)]
struct Droid {
id: String,
primary_function: String,
2019-03-23 08:08:09 -05:00
}
2020-06-04 03:28:07 -05:00
#[graphql_union]
trait Character {
// NOTICE: The method signature must contain `&amp;self` and return `Option&lt;&amp;VariantType&gt;`.
fn as_human(&amp;self) -&gt; Option&lt;&amp;Human&gt; { None }
fn as_droid(&amp;self) -&gt; Option&lt;&amp;Droid&gt; { None }
}
2020-06-04 03:28:07 -05:00
impl Character for Human {
fn as_human(&amp;self) -&gt; Option&lt;&amp;Human&gt; { Some(&amp;self) }
}
2019-03-23 08:08:09 -05:00
2020-06-04 03:28:07 -05:00
impl Character for Droid {
fn as_droid(&amp;self) -&gt; Option&lt;&amp;Droid&gt; { Some(&amp;self) }
}
<span class="boring">
</span><span class="boring">fn main() {}
</span></code></pre></pre>
<h3 id="custom-context-1"><a class="header" href="#custom-context-1">Custom context</a></h3>
2020-10-07 03:24:29 -05:00
<p>If a <a href="https://docs.rs/juniper/0.14.2/juniper/trait.Context.html"><code>Context</code></a> is required in a trait method to resolve a <a href="https://spec.graphql.org/June2018/#sec-Unions">GraphQL union</a> variant, specify it as an argument.</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused_variables)]
</span><span class="boring">extern crate juniper;
</span><span class="boring">use std::collections::HashMap;
</span>use juniper::{graphql_union, GraphQLObject};
2020-06-04 03:28:07 -05:00
#[derive(GraphQLObject)]
2019-05-14 01:52:54 -05:00
#[graphql(Context = Database)]
2019-03-23 08:08:09 -05:00
struct Human {
id: String,
home_planet: String,
}
2020-06-04 03:28:07 -05:00
#[derive(GraphQLObject)]
2019-05-14 01:52:54 -05:00
#[graphql(Context = Database)]
2019-03-23 08:08:09 -05:00
struct Droid {
id: String,
primary_function: String,
}
struct Database {
humans: HashMap&lt;String, Human&gt;,
droids: HashMap&lt;String, Droid&gt;,
}
impl juniper::Context for Database {}
2020-11-14 07:58:04 -06:00
#[graphql_union(context = Database)]
2020-06-04 03:28:07 -05:00
trait Character {
// NOTICE: The method signature may optionally contain `&amp;Context`.
fn as_human&lt;'db&gt;(&amp;self, ctx: &amp;'db Database) -&gt; Option&lt;&amp;'db Human&gt; { None }
fn as_droid&lt;'db&gt;(&amp;self, ctx: &amp;'db Database) -&gt; Option&lt;&amp;'db Droid&gt; { None }
2019-03-23 08:08:09 -05:00
}
2020-06-04 03:28:07 -05:00
impl Character for Human {
fn as_human&lt;'db&gt;(&amp;self, ctx: &amp;'db Database) -&gt; Option&lt;&amp;'db Human&gt; {
ctx.humans.get(&amp;self.id)
2019-03-23 08:08:09 -05:00
}
}
2019-03-23 08:08:09 -05:00
2020-06-04 03:28:07 -05:00
impl Character for Droid {
fn as_droid&lt;'db&gt;(&amp;self, ctx: &amp;'db Database) -&gt; Option&lt;&amp;'db Droid&gt; {
ctx.droids.get(&amp;self.id)
}
}
<span class="boring">
</span><span class="boring">fn main() {}
</span></code></pre></pre>
<h3 id="ignoring-trait-methods-1"><a class="header" href="#ignoring-trait-methods-1">Ignoring trait methods</a></h3>
2020-06-04 03:28:07 -05:00
<p>As with enums, we may want to omit some trait methods to be assumed as <a href="https://spec.graphql.org/June2018/#sec-Unions">GraphQL union</a> variants and ignore them.</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">extern crate juniper;
</span>use juniper::{graphql_union, GraphQLObject};
2020-06-04 03:28:07 -05:00
#[derive(GraphQLObject)]
2019-03-23 08:08:09 -05:00
struct Human {
id: String,
home_planet: String,
}
2020-06-04 03:28:07 -05:00
#[derive(GraphQLObject)]
2019-03-23 08:08:09 -05:00
struct Droid {
id: String,
primary_function: String,
}
2020-06-04 03:28:07 -05:00
#[graphql_union]
trait Character {
fn as_human(&amp;self) -&gt; Option&lt;&amp;Human&gt; { None }
fn as_droid(&amp;self) -&gt; Option&lt;&amp;Droid&gt; { None }
2020-11-14 07:58:04 -06:00
#[graphql(ignore)] // or `#[graphql(skip)]`, your choice
2020-06-04 03:28:07 -05:00
fn id(&amp;self) -&gt; &amp;str;
2019-03-23 08:08:09 -05:00
}
2020-06-04 03:28:07 -05:00
impl Character for Human {
fn as_human(&amp;self) -&gt; Option&lt;&amp;Human&gt; { Some(&amp;self) }
fn id(&amp;self) -&gt; &amp;str { self.id.as_str() }
}
impl Character for Droid {
fn as_droid(&amp;self) -&gt; Option&lt;&amp;Droid&gt; { Some(&amp;self) }
fn id(&amp;self) -&gt; &amp;str { self.id.as_str() }
}
<span class="boring">
</span><span class="boring">fn main() {}
</span></code></pre></pre>
<h3 id="external-resolver-functions-1"><a class="header" href="#external-resolver-functions-1">External resolver functions</a></h3>
2020-06-04 03:28:07 -05:00
<p>Similarly to enums and structs, it's not mandatory to use trait methods as <a href="https://spec.graphql.org/June2018/#sec-Unions">GraphQL union</a> variant resolvers. Instead, custom functions may be specified:</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">extern crate juniper;
</span><span class="boring">use std::collections::HashMap;
</span>use juniper::{graphql_union, GraphQLObject};
2020-06-04 03:28:07 -05:00
#[derive(GraphQLObject)]
#[graphql(Context = Database)]
struct Human {
id: String,
home_planet: String,
}
#[derive(GraphQLObject)]
#[graphql(Context = Database)]
struct Droid {
id: String,
primary_function: String,
}
struct Database {
humans: HashMap&lt;String, Human&gt;,
droids: HashMap&lt;String, Droid&gt;,
}
impl juniper::Context for Database {}
2020-11-14 07:58:04 -06:00
#[graphql_union(context = Database)]
2020-06-04 03:28:07 -05:00
#[graphql_union(
on Human = DynCharacter::get_human,
on Droid = get_droid,
)]
trait Character {
2020-11-14 07:58:04 -06:00
#[graphql(ignore)] // or `#[graphql(skip)]`, your choice
2020-06-04 03:28:07 -05:00
fn id(&amp;self) -&gt; &amp;str;
}
impl Character for Human {
fn id(&amp;self) -&gt; &amp;str { self.id.as_str() }
}
impl Character for Droid {
fn id(&amp;self) -&gt; &amp;str { self.id.as_str() }
}
// The trait object is always `Send` and `Sync`.
type DynCharacter&lt;'a&gt; = dyn Character + Send + Sync + 'a;
impl&lt;'a&gt; DynCharacter&lt;'a&gt; {
fn get_human&lt;'db&gt;(&amp;self, ctx: &amp;'db Database) -&gt; Option&lt;&amp;'db Human&gt; {
ctx.humans.get(self.id())
2019-03-23 08:08:09 -05:00
}
}
2019-03-23 08:08:09 -05:00
2020-06-04 03:28:07 -05:00
// External resolver function doesn't have to be a method of a type.
// It's only a matter of the function signature to match the requirements.
fn get_droid&lt;'db&gt;(ch: &amp;DynCharacter&lt;'_&gt;, ctx: &amp;'db Database) -&gt; Option&lt;&amp;'db Droid&gt; {
ctx.droids.get(ch.id())
}
<span class="boring">
</span><span class="boring">fn main() {}
</span></code></pre></pre>
<h2 id="scalarvalue-considerations-1"><a class="header" href="#scalarvalue-considerations-1"><code>ScalarValue</code> considerations</a></h2>
2020-06-04 03:28:07 -05:00
<p>By default, <code>#[derive(GraphQLUnion)]</code> and <code>#[graphql_union]</code> macros generate code, which is generic over a <a href="https://docs.rs/juniper/latest/juniper/trait.ScalarValue.html"><code>ScalarValue</code></a> type. This may introduce a problem when at least one of <a href="https://spec.graphql.org/June2018/#sec-Unions">GraphQL union</a> variants is restricted to a concrete <a href="https://docs.rs/juniper/latest/juniper/trait.ScalarValue.html"><code>ScalarValue</code></a> type in its implementation. To resolve such problem, a concrete <a href="https://docs.rs/juniper/latest/juniper/trait.ScalarValue.html"><code>ScalarValue</code></a> type should be specified:</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(dead_code)]
</span><span class="boring">extern crate juniper;
</span>use juniper::{DefaultScalarValue, GraphQLObject, GraphQLUnion};
2020-06-04 03:28:07 -05:00
#[derive(GraphQLObject)]
#[graphql(Scalar = DefaultScalarValue)]
2020-04-15 03:07:36 -05:00
struct Human {
id: String,
home_planet: String,
}
2020-06-04 03:28:07 -05:00
#[derive(GraphQLObject)]
2020-04-15 03:07:36 -05:00
struct Droid {
id: String,
primary_function: String,
}
2020-06-04 03:28:07 -05:00
#[derive(GraphQLUnion)]
#[graphql(Scalar = DefaultScalarValue)] // removing this line will fail compilation
2020-04-15 03:07:36 -05:00
enum Character {
Human(Human),
Droid(Droid),
}
<span class="boring">
</span><span class="boring">fn main() {}
</span></code></pre></pre>
2019-03-23 08:08:09 -05:00
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<div style="clear: both"></div>
</nav>
</div>
</div>
<nav class="nav-wide-wrapper" aria-label="Page navigation">
2021-09-25 23:31:16 -05:00
</nav>
2019-03-23 08:08:09 -05:00
</div>
<script type="text/javascript">
window.playground_copyable = true;
</script>
2021-09-25 23:31:16 -05:00
<script src="elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
2019-03-23 08:08:09 -05:00
<script src="mark.min.js" type="text/javascript" charset="utf-8"></script>
<script src="searcher.js" type="text/javascript" charset="utf-8"></script>
<script src="clipboard.min.js" type="text/javascript" charset="utf-8"></script>
<script src="highlight.js" type="text/javascript" charset="utf-8"></script>
<script src="book.js" type="text/javascript" charset="utf-8"></script>
<!-- Custom JS scripts -->
2021-09-25 23:31:16 -05:00
<script type="text/javascript">
2019-03-23 08:08:09 -05:00
window.addEventListener('load', function() {
window.setTimeout(window.print, 100);
});
</script>
</body>
</html>