[][src]Crate cynic

Cynic

Cynic is a GraphQL query builder & data mapper for Rust.

See the README on GitHub for more details.

Overview

To get started with Cynic you'll need a GraphQL schema for the API you wish to query. The examples will be using the star wars API.

Generating a Query DSL

Once you've got your schema installed locally, you'll need to use the query_dsl macro to generate a query_dsl for your schema:

mod query_dsl {
    cynic::query_dsl!("swapi.graphql");
}

This module generates a few things:

  1. Some structs to represent the Input types the underlying schema. You may need to use these to build mutations or as parameters to queries.
  2. Definitons of all the Enums in the provided schema. You'll need these if you want to use any enum types.
  3. Type safe selection set functions. These can be used to build up a query manually, though it's usually easier to use the QueryFragment derive functionality explained below. Hopefully you'll not need to use these directly too often.

Though using macros to generate these is convenient, it does leave a lot of code to the imagination. You can get a glimpse of the things this defines by running cargo doc --document-private-items and having a look in the query_dsl module It's not ideal, but at least provides some visibility into the various enum types.

Creating QueryFragments

Now that you have a query_dsl defined, you can start building some queries. Cynic lets you do this by deriving QueryFragment for a struct. For example, if we wanted to know what director title & director a Star Wars film had, we could define this QueryFragment:

#[derive(cynic::QueryFragment)]
#[cynic(
    schema_path = "swapi.graphql",
    query_module = "query_dsl",
    graphql_type = "Film"
)]
struct Film {
    title: String,
    director: String
}

This Film struct can now be used as the type of a field on any other QueryFragment struct and cynic will know how to turn that into a GraphQL query, and populate the Film struct from the response.

For example, if we wanted to know the Director for a particular film:

#[derive(cynic::QueryFragment)]
#[cynic(
    schema_path = "swapi.graphql",
    query_module = "query_dsl",
    graphql_type = "Root"
)]
struct FilmDirectorQuery {
    #[cynic_arguments(id = "ZmlsbXM6MQ==")]
    film: Film,
}

Here we use the #[cynic_arguments()] attribute on the film field to provide a hard coded film ID to look up. Though useful for demonstration, hard coded arguments like this aren't much use in reality. For more details on providing runtime arguments please see below.

Sending Queries

Notice that FilmDirectorQuery above defines it's graphql_type as Root - the root query type in SWAPI. Whenever you define a QueryFragment at this level of the heirarchy it can be used as a query on its own rather than as part of another query.

To send the FilmDirectorQuery above:

let query = cynic::Query::new(FilmDirectorQuery::fragment(()))
let response = reqwest::Client::new()
                    .post("a_url")
                    .json(query.body().unwrap())
                    .send();
let result = query.decode_response(response.json().await?).unwrap();

After this code has run, result will be an instance of FilmDirectorQuery with the film populated appropriately.

Dynamic Query Arguments

The query above was useful for demonstration, but you'll usually want to be able to provide parameters to your query. To do this, you should define a struct that contains all of the parameters you want to provide:

#[derive(cynic::FragmentArguments)]
struct FilmArguments {
    id: Option<String>
}

Deriving FragmentArguments allows this struct to be used as arguments to a QueryFragment fragment, whether it represents part of a query or a whole query.

You can now define a query to use these arguments on. For example, to make FilmDirectorQuery a bit more dynamic:

#[derive(cynic::QueryFragment)]
#[cynic(
    schema_path = "swapi.graphql",
    query_module = "query_dsl",
    graphql_type = "Root",
    argument_struct = "FilmArguments"
)]
struct FilmDirectorQueryWithArgs {
    #[cynic_arguments(id = args.id)]
    film: Film,
}

By adding the argument_struct parameter to our QueryFragment we've made a variable named args avaiable for use in the cynic_arguments attribute. This args will be an instance of FilmArguments and will need to be provided whenever this is used as a query.

To build a query using this new struct:

let query = cynic::Query::new(
    FilmDirectorQueryWithArgs::fragment(
        FilmArguments{ id: "ZmlsbXM6MQ==".to_string() }
    )
);

Re-exports

pub use selection_set::SelectionSet;

Modules

selection_set

Macros

query_dsl

Structs

Argument
GraphQLError
GraphQLResponse
Query
QueryBody

Enums

PossiblyParsedData

The data returned by a GraphQL query when the query had errors.
GraphQL allows servers to return partial data in this case, but if there's missing fields that aren't represented by an Option we won't have been able to decode that data.

Traits

FragmentArguments

A marker trait for the arguments types on QueryFragments.

IntoArguments

Used for converting between different argument types in a QueryFragment heirarchy.

QueryFragment
QueryRoot
Scalar

Type Definitions

GraphQLResult

The result of a GraphQL Operation.

Derive Macros

FragmentArguments
QueryFragment