Crate apollo_compiler
source · [−]Expand description
Features
- Ergonomic API on top the AST created by apollo-parser
- GraphQL validation and diagnostics reporting
- Validation is a work in progress, stay tuned for further validation rules implementation.
 
Getting started
Add this to your Cargo.toml to start using apollo-compiler:
[dependencies]
apollo-compiler= "0.2.8"Or using cargo-edit:
cargo add apollo-compilerUsage
apollo-compiler is built using salsa to provide a
memoised query system on top of the AST produced by apollo-parser.
The idea is that all relationships between GraphQL types are pre-established and pre-computed, so you are able to always find the reference to say a field Type, or a Directive.
You can get started with apollo-compiler:
use apollo_compiler::ApolloCompiler;
let input = r#"
  interface Pet {
    name: String
  }
  type Dog implements Pet {
    name: String
    nickname: String
    barkVolume: Int
  }
  type Cat implements Pet {
    name: String
    nickname: String
    meowVolume: Int
  }
  union CatOrDog = Cat | Dog
  type Human {
    name: String
    pets: [Pet]
  }
  type Query {
    human: Human
  }
"#;
let ctx = ApolloCompiler::new(input);
let diagnostics = ctx.validate();
for diagnostic in &diagnostics {
    // this will pretty-print diagnostics using the miette crate.
    println!("{}", diagnostic);
}
assert!(diagnostics.is_empty());Examples
Accessing fragment definition field types
use apollo_compiler::{ApolloCompiler, values, SourceDatabase};
use miette::Result;
fn main() -> Result<()> {
    let input = r#"
    query getProduct {
      topProducts {
          type
      }
      ... vipCustomer
    }
    #fragment definition where we want to know the field types.
    fragment vipCustomer on User {
      id
      name
      profilePic(size: 50)
    }
    type Query {
      topProducts: Product
      customer: User
    }
    type Product {
      type: String
      price(setPrice: Int): Int
    }
    type User {
      id: ID
      name: String
      profilePic(size: Int): URL
    }
    scalar URL @specifiedBy(url: "https://tools.ietf.org/html/rfc3986")
    "#;
    let ctx = ApolloCompiler::new(input);
    let diagnostics = ctx.validate();
    for diagnostic in &diagnostics {
        println!("{}", diagnostic);
    }
    assert!(diagnostics.is_empty());
    let op = ctx.db.find_operation_by_name(String::from("getProduct")).expect("getProduct query does not exist");
    let fragment_in_op: Vec<crate::values::FragmentDefinition> = op.selection_set().selection().iter().filter_map(|sel| match sel {
        crate::values::Selection::FragmentSpread(frag) => {
            Some(frag.fragment(&ctx.db)?.as_ref().clone())
        }
        _ => None
    }).collect();
    let fragment_fields: Vec<crate::values::Field> = fragment_in_op.iter().flat_map(|frag| frag.selection_set().fields()).collect();
    let field_ty: Vec<String> = fragment_fields
        .iter()
        .filter_map(|f| Some(f.ty()?.name()))
        .collect();
    assert_eq!(field_ty, ["ID", "String", "URL"]);
    Ok(())
}Get a directive defined on a field used in a query operation definition.
use apollo_compiler::{ApolloCompiler, values};
use anyhow::{anyhow, Result};
fn main() -> Result<()> {
    let input = r#"
    query getProduct {
      size
      topProducts {
        name
        inStock
      }
    }
    type Query {
      topProducts: Product
      name: String
      size: Int
    }
    type Product {
      inStock: Boolean @join__field(graph: INVENTORY)
      name: String @join__field(graph: PRODUCTS)
      price: Int
      shippingEstimate: Int
      upc: String!
      weight: Int
    }
    "#;
    let ctx = ApolloCompiler::new(input);
    let diagnostics = ctx.validate();
    for diagnostic in &diagnostics {
        println!("{}", diagnostic);
    }
    assert!(diagnostics.is_empty());
    let ctx = ApolloCompiler::new(input);
    let diagnostics = ctx.validate();
    for diagnostic in &diagnostics {
        println!("{}", diagnostic);
    }
    assert!(diagnostics.is_empty());
    let operations = ctx.operations();
    let get_product_op = operations
        .iter()
        .find(|op| op.name() == Some("getProduct")).expect("getProduct query does not exist");
    let op_fields = get_product_op.fields(&ctx.db);
    let in_stock_field = op_fields
        .iter()
        .find(|f| f.name() == "topProducts")
        .expect("topProducts field does not exist")
        .selection_set()
        .field("inStock")
        .expect("inStock field does not exist")
        .field_definition(&ctx.db)
        .expect("field definition does not exist");
    let in_stock_directive: Vec<&str> = in_stock_field
        .directives()
        .iter()
        .map(|dir| dir.name())
        .collect();
    assert_eq!(in_stock_directive, ["join__field"]);
    Ok(())
}Printing diagnostics for a faulty GraphQL document
use apollo_compiler::ApolloCompiler;
let input = r#"
query {
  cat {
    name
  }
}
query getPet {
  cat {
    owner {
      name
    }
  }
}
query getPet {
  cat {
    treat
  }
}
subscription sub {
  newMessage {
    body
    sender
  }
  disallowedSecondRootField
}
type Query {
  cat: Pet
}
type Subscription {
  newMessage: Result
}
interface Pet {
  name: String
}
type Dog implements Pet {
  name: String
  nickname: String
  barkVolume: Int
}
type Cat implements Pet {
  name: String
  nickname: String
  meowVolume: Int
}
union CatOrDog = Cat | Dog
"#;
let ctx = ApolloCompiler::new(input);
let diagnostics = ctx.validate();
for diagnostic in &diagnostics {
    println!("{}", diagnostic)
}
assert_eq!(diagnostics.len(), 5)License
Licensed under either of
- Apache License, Version 2.0 (LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or https://opensource.org/licenses/MIT)
at your option.