Quickstart

If you just want to get going with cynic and don't care too much about how it works, this is the chapter for you.

Pre-requisites

There's a few things you'll need before you get started:

  1. An existing rust project (though you can just run cargo new if you don't have one).
  2. A GraphQL API that you'd like to query.

Setting up dependencies.

First things first: you need to add cynic to your dependencies. We'll also need an HTTP client library. For the purposes of this quickstart we'll be using surf but you can use any library you want. Open up your Cargo.toml and add the following under the [dependencies] section:

cynic = { version = "3", features = ["http-surf"] }
surf = "2"

Note that we've added the http-surf feature flag of cynic - this pulls in some surf integration code, which we'll be using. If you're using a different HTTP client, you'll need a different feature flag or you may need to see the documentation for making an HTTP request manually.

You'll also want to add a [build-dependencies]:

[build-dependencies]
cynic-codegen = { version = "3" }

You may also optionally want to install insta - a snapshot testing library that can be useful for double checking your GraphQL queries are as expected. Add this under [dev-dependencies] so it's available under test but not at runtime:

[dev-dependencies]
insta = "1"

Run a cargo check to make sure this builds and you're good to go.

Fetching the GraphQL schema

Each GraphQL server has a schema that describes the information it can provide. Cynic needs a local copy of this schema for any APIs that it needs to talk to. We'll fetch this schema using cynic-cli. Run the following in the root of your project:

# First, install cynic-cli if you don't already have it:
cargo install --locked cynic-cli

# Next, we'll introspect the schema.  
# I'm using the StarWars schema in this guide but you can use any API you want
cynic introspect https://swapi-graphql.netlify.app/.netlify/functions/index -o schemas/starwars.graphql

Adding your schema to the build.

GraphQL APIs have a schema that defines the data they can provide. Cynic needs a copy of this schema to make sure you're writing valid queries. To register your schema you should create a build.rs in the root of your project (next to Cargo.toml) and add the following:

fn main() {
    cynic_codegen::register_schema("starwars")
        .from_sdl_file("schemas/starwars.graphql")
        .unwrap()
        .as_default()
        .unwrap();
}

I'm using the Star Wars schema which I've put in a schemas folder, but you should adjust the path and name for whatever schema you're using.

Defining your schema module.

Cynics macros need a "schema module" - this module contain a bunch of autogenerated types that cynic do its job. To define one of these, you should open the rust file where you want to write a query and add the following:


#![allow(unused)]
fn main() {
// Pull in the Star Wars schema we registered in build.rs
#[cynic::schema("starwars")]
mod schema {}
}

I registered my schema as starwars in build.rs so I've passed that name in here. You should use whatever name you decided on in your build.rs

Building your query structs.

Cynic allows you to build queries from Rust structs - so you'll need to take the query you're wanting to run and convert it into some rust structs. This can be quite laborious and error prone for larger queries so cynic provides a generator to help you get started.

Go to https://generator.cynic-rs.dev and select how you'd like to input your schema. If the GraphQL API you wish to use is accessible on the internet you can just link directly to it (although it will need to have CORS headers enabled). Otherwise you can upload your schema to the generator.

Once you've provided the schema, you should be dropped into a GraphiQL interface but with an extra panel that contains Rust generated from your query & schema.

For example, I've chosen to add the star wars schema and the following query:

query FilmDirectorQuery(id: ID!){
  film {
    title
    director
  }
}

and been given, the following rust code:


#![allow(unused)]
fn main() {
#[derive(cynic::QueryFragment, Debug)]
struct Film {
    title: Option<String>,
    director: Option<String>,
}

#[derive(cynic::QueryVariables)]
struct FilmArguments {
    id: Option<cynic::Id>,
}

#[derive(cynic::QueryFragment, Debug)]
#[cynic(graphql_type = "Root", variables = "FilmArguments")]
struct FilmDirectorQuery {
    #[arguments(id: $id)]
    film: Option<Film>,
}
}

You should add this to the same file as your schema module.

Checking your query (optional)

Since cynic generates queries for you based on Rust structs, it's not always obvious what the GraphQL queries look like. Sometimes you might want to run a query manually via Graphiql, or you might just want to see what effects changing the rust structs have on the query itself.

I find writing snapshot tests using insta useful for this purpose. Assuming your query is called AllFilmsQuery like mine is, you can add the following to the same file you put the struct output into:


#![allow(unused)]
fn main() {
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn all_films_query_gql_output() {
        use cynic::QueryBuilder;

        let operation = AllFilmsQuery::build(());

        insta::assert_snapshot!(operation.query);
    }
}
}

You can now run this test with cargo test. It should fail the first time, as you've not yet saved a snapshot. Run cargo insta review (you may need to cargo install insta first) to approve the snapshot, and the test should succeed.

You can use this snapshot test to double check the query whenever you make changes to the rust code, or just when you need some actual GraphQL to make a query outside of cynic.

Making your query

Now, you're ready to make a query against a server. Cynic provides integrations for the surf & reqwest HTTP clients. We'll use surf here, but it should be similar for reqwest (or if you're using another HTTP library see here for how to use cynic with it).

First, you'll want to build an Operation similar to how we did it in the snapshot test above (again, swapping AllFilmsQuery for the name of your root query struct):


#![allow(unused)]
fn main() {
use cynic::{QueryBuilder, http::SurfExt};

let operation = AllFilmsQuery::build(());
}

This builds an Operation struct with is serializable using serde::Serialize. You should pass it in as the HTTP body using your HTTP client and then make a request. For example, to use surf to talk to the StarWars API (see the docs for cynic::http if you're using another client):


#![allow(unused)]
fn main() {
let response = surf::post("https://swapi-graphql.netlify.app/.netlify/functions/index")
    .run_graphql(&operation)
    .await
    .unwrap();
}

Now, assuming everything went well, you should have the response to your query which you can do whatever you want with. And that's the end of the quickstart.