graphql-client

repository·main·Indexed 22 days ago

https://github.com/graphql-rust/graphql-client

A typed GraphQL client library for Rust that uses procedural macros to generate precise Rust types for queries and responses at compile-time. It includes the graphql_client_cli for introspecting GraphQL APIs and generating Rust modules from schemas and query files. The library supports custom scalars, configurable deprecation handling, and can be compiled to WebAssembly for browser use.

Tokens
6.9K
Snippets
17
Records
38
Agent score
79%

What's inside graphql-client

  1. Use graphql-introspection-query to deserialize GraphQL introspection results

    main
    The graphql-introspection-query crate provides a set of structs that implement serde::Deserialize. These structs are designed to match the exact schema shape returned by a spec-compliant GraphQL API when performing an introspection query. Use this crate when you need to programmatically inspect the schema of a GraphQL server by deserializing its introspection response into structured Rust types.
  2. Use multiple operations in a single query document

    main

    If a .graphql file contains multiple operations, you can select a specific one by naming your #[derive(GraphQLQuery)] struct with the exact same name as the operation in the GraphQL file. This allows you to share fragments across different operations within the same document.

    use graphql_client::GraphQLQuery;
    
    #[derive(GraphQLQuery)]
    #[graphql(
        schema_path = "tests/unions/union_schema.graphql",
        query_path = "tests/unions/union_query.graphql",
    )]
    pub struct Heights;
  3. Handle custom GraphQL scalars

    main

    When a schema defines custom scalars (e.g., scalar URI), you must provide matching Rust types in the scope of the struct under #[derive(GraphQLQuery)]. If you don't, the compiler will fail because it cannot find the type.

    You can resolve this by declaring a type alias or a newtype in your scope:

    type URI = String;
  4. Build the library for WebAssembly (Wasm) use

    main

    To use graphql-client in a browser, you must compile it to WebAssembly. This requires the Rust toolchain and the wasm-pack CLI. After building, the compiled Wasm program and the necessary JavaScript glue code will be generated in the ./pkg directory.

    wasm-pack build --target=web
  5. Get started with graphql_client

    main

    To use graphql_client, you need a GraphQL schema (in .graphql or .json format) and a .graphql file containing your query. The library uses a procedural macro to generate typed Rust structures at compile-time.

    1. Save your query in a .graphql file.
    2. Download your schema (you can use the provided graphql_client_cli).
    3. Use the #[derive(GraphQLQuery)] macro on a struct to generate the necessary types.

    The macro generates a module named after your struct (in snake case). This module contains:

    • ResponseData: The root type for the response.
    • Variables: A struct representing the expected query variables.

    To send a request, use the GraphQLQuery::build_query(variables) method to create the request payload.

    use graphql_client::{GraphQLQuery, Response};
    use std::error::Error;
    use reqwest;
    
    #[derive(GraphQLQuery)]
    #[graphql(
        schema_path = "tests/unions/union_schema.graphql",
        query_path = "tests/unions/union_query.graphql",
        response_derives = "Debug",
    )]
    pub struct UnionQuery;
    
    async fn perform_my_query(variables: union_query::Variables) -> Result<(), Box<dyn Error>> {
        // this is the important line
        let request_body = UnionQuery::build_query(variables);
    
        let client = reqwest::Client::new();
        let mut res = client.post("/graphql").json(&request_body).send().await?;
        let response_body: Response<union_query::ResponseData> = res.json().await?;
        println!("{:#?}", response_body);
        Ok(())
    }
  6. How enums are generated and handled

    main

    When graphql-client generates Rust code from a GraphQL schema, it creates Rust enum types for GraphQL enums.

    Key behaviors include:

    1. Keyword Escaping: Since GraphQL enum variants might use Rust reserved keywords (e.g., self, where), the generator automatically escapes variant names and constructors (e.g., self becomes self_). However, the string representation used for serialization remains the original GraphQL name.
    2. The Other(String) Variant: Every generated enum includes an Other(String) variant. This acts as a fallback to ensure the client doesn't crash if the server returns a new enum value that was not present in the schema used during code generation.
    3. Serialization/Deserialization: Enums implement serde::Serialize and serde::Deserialize.
      • Serialization: Converts the enum variant to its original GraphQL string name.
      • Deserialization: Attempts to match the incoming string to a known variant. If no match is found, it captures the value in the Other(String) variant instead of returning a deserialization error.
  7. Use the GraphQLQuery derive macro to generate query modules

    main

    The primary way to use graphql-client is by using the #[derive(GraphQLQuery)] macro on a unit struct. This macro reads a .graphql query file and a .graphql schema file to generate a module containing the necessary types for variables and response data.

    To use it, you must provide the query_path and schema_path in the attribute. The generated module will contain a Variables struct and a ResponseData struct that match your query's shape.

  8. Force Cargo recompilation on query or schema changes

    main

    To ensure that your Rust code is automatically recompiled whenever your GraphQL queries or schema files are updated, you must provide their file paths to the codegen options. This is done by including the file paths in the generated module, which creates a dependency link for Cargo.

    • Use set_query_file(path: PathBuf) to register a .graphql file.
    • Use set_schema_file(path: PathBuf) to register a schema file.