Query Fragments
QueryFragments are the main tool for building queries & mutations in cynic. A QueryFragment tells cynic what fields to select from a GraphQL object, and how to decode those fields into a struct.
Generally you'll use a derive to create query fragments, like this:
#![allow(unused_variables)] fn main() { #[derive(cynic::QueryFragment, Debug)] #[cynic( schema_path = "examples/starwars.schema.graphql", query_module = "query_dsl", graphql_type = "Film" )] struct Film { title: Option<String>, director: Option<String>, } }
When this struct is used in a query it will select the title
& director
fields of the Film
type, which are both optional strings. QueryFragments can
be nested inside each other, like so:
#![allow(unused_variables)] fn main() { #[derive(cynic::QueryFragment, Debug)] #[cynic( schema_path = "examples/starwars.schema.graphql", query_module = "query_dsl", graphql_type = "Root", )] struct FilmsConnection { films: Vec<Film>, } }
If the above QueryFragment was used in a query, it would result in GraphQL that looked like:
films {
title
director
}
QueryFragments are compile time checked against the provided GraphQL schema.
You cannot nest a Film
QueryFragment into a field that was expecting an
Actor
for example. Similarly, nullable fields must be an Option
and lists
must be a Vec
.
Making a Query with QueryFragments
QueryFragments that apply to the Query type (otherwise known as the Root type)
of a schema can be used to build a cynic::Query
. This is the type that can
be sent to a server and used to decode the response.
If we wanted to use our FilmConnection to get all the films from the star wars API we need a QueryFragment like this:
#![allow(unused_variables)] fn main() { #[derive(cynic::QueryFragment, Debug)] #[cynic( schema_path = "examples/starwars.schema.graphql", query_module = "query_dsl", graphql_type = "Root", )] struct AllFilmsQuery { all_films: Option<FilmConnection>, } }
An Operation
can be created from this QueryFragment
:
#![allow(unused_variables)] fn main() { use cynic::QueryBuilder; let operation = AllFilmsQuery::build(()); }
This particular query has no arguments so we provide the unit type ()
in place
of actual arguments.
This Operation
can be converted into JSON using serde
, sent to a server, and
then then it's decode_response
function can be used to decode the response
itself. An example of this is in the Quickstart.
Passing Arguments
To pass arguments into queries you must pass an argument_struct
parameter
to the cynic
attribute, and then add arguments
attributes to the
fields for which you want to provide arugments. The argument_struct
parameter must name a struct that implements cynic::FragmentArguments
, which
can also be derived. (See query arguments for more details)
Here, we define a query that fetches a film by a particular ID:
#![allow(unused_variables)] fn main() { #[derive(cynic::FragmentArguments)] struct FilmArguments { id: Option<cynic::Id>, } #[derive(cynic::QueryFragment, Debug)] #[cynic( schema_path = "examples/starwars.schema.graphql", query_module = "query_dsl", graphql_type = "Root", argument_struct = "FilmArguments" )] struct FilmQuery { #[arguments(id = &args.id)] film: Option<Film>, } }
This can be converted into a query in a similar way we just need to provide
some FilmArguments
:
#![allow(unused_variables)] fn main() { use cynic::QueryBuilder; let operation = FilmQuery::build( FilmArguments{ id: Some("ZmlsbXM6MQ==".into()), } ); }
Nested Arguments
The example above showed how to pass arguments to the top level of a query. If
you want to pass arguments to a nested QueryFragment then all it's parent
QueryFragment
s must specify the same argument_struct
in their cynic
attribute. This is neccesary so that the FragmentArgument
struct gets passed
down to that level of a query.
If no nested QueryFragments require arguments, you can omit the
argument_struct
attr.
Mutations
Mutations are also constructed using QueryFragments in a very similar way to
queries. Instead of selecting query fields you select a mutation, and pass in
any arguments in exactly the same way. Mutations use the MutationBuilder
rather than QueryBulder
:
#![allow(unused_variables)] fn main() { use cynic::MutationBuilder; let operation = SomeMutation::build(SomeArguments { ... }); }
This operation
can then be used in exactly the same way as with queries.
Struct Attributes
A QueryFragment can be configured with several attributes on the struct itself:
graphql_type = "AType"
is required and tells cynic which type in the GraphQL schema to map this struct toschema_path
sets the path to the GraphQL schema. This is required, but can be provided by nesting the QueryFragment inside a query module with this attr.query_module
tells cynic where to find the query module - that is a module that has called thequery_dsl!
macro. This is required but can also be provided by nesting the QueryFragment inside a query module.
Field Attributes
Each field can also have it's own attributes:
recurse = "5"
tells cynic that this field is recursive and should be fetched to a maximum depth of 5. See Recursive Queries for more info.- The
flatten
attr can be used to "flatten" out excessive Options from lists. As GraphQL is used in languages with implicit nulls, it's not uncommon to see a type[Int]
- which in Rust maps toOption<Vec<Option<i32>>
. This isn't a very nice type to work with - applying theflatten
attribute lets you represent this as aVec<i32>
in your QueryFragment. Any outer nulls become an empty list and inner nulls are dropped.
Related
- FragmentArguments are used to provide arguments to the fields of a QueryFragment.
- Struct Level Attributes can be added to a QueryFragment.
- Recursive queries are supported by QueryFragments.