apollo-compiler

A query-based compiler for the GraphQL query language.

Crates.io version Download docs.rs docs

Features

Getting started

Add this to your Cargo.toml to start using apollo-compiler: ```toml

Just an example, change to the necessary package version.

[dependencies] apollo-compiler = "0.4.0" ```

Or using [cargo-edit]: bash cargo add apollo-compiler

Usage

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: ```rust 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

```rust use apollo_compiler::{ApolloCompiler, hir, DocumentDatabase}; 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<hir::FragmentDefinition> = op.selection_set().selection().iter().filter_map(|sel| match sel {
    hir::Selection::FragmentSpread(frag) => {
        Some(frag.fragment(&ctx.db)?.as_ref().clone())
    }
    _ => None
}).collect();

let fragment_fields: Vec<hir::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(&ctx.db)?.name()))
    .collect();
assert_eq!(field_ty, ["ID", "String", "URL"]);
Ok(())

} ```

Get a directive defined on a field used in a query operation definition.

```rust use apollo_compiler::{ApolloCompiler, hir, HirDatabase}; 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.db.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

```rust 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

at your option.