- 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.
Add the dependency to start using apollo-compiler
:
cargo add apollo-compiler
Or add this to your Cargo.toml
for a manual installation:
# Just an example, change to the necessary package version.
[dependencies]
apollo-compiler = "0.9.4"
apollo-compiler
is tested on the latest stable version of Rust.
Older version may or may not be compatible.
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 mut compiler = ApolloCompiler::new();
compiler.add_document(input, "document.graphql");
let diagnostics = compiler.validate();
for diagnostic in &diagnostics {
// this will pretty-print diagnostics using the miette crate.
println!("{}", diagnostic);
}
assert!(diagnostics.is_empty());
use apollo_compiler::{ApolloCompiler, hir, HirDatabase};
use miette::Result;
fn main() -> Result<()> {
let schema_input = r#"
type User {
id: ID
name: String
profilePic(size: Int): URL
}
schema { query: User }
scalar URL @specifiedBy(url: "https://tools.ietf.org/html/rfc3986")
"#;
let query_input = r#"
query getUser {
... vipCustomer
}
#fragment definition where we want to know the field types.
fragment vipCustomer on User {
id
name
profilePic(size: 50)
}
"#;
let mut compiler = ApolloCompiler::new();
let _schema_id = compiler.add_type_system(schema_input, "schema.graphql");
let query_id = compiler.add_executable(query_input, "query.graphql");
let diagnostics = compiler.validate();
for diagnostic in &diagnostics {
println!("{}", diagnostic);
}
assert!(diagnostics.is_empty());
let op = compiler.db.find_operation(query_id, Some("getUser".into()))
.expect("getUser 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(&compiler.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(&compiler.db)?.name()))
.collect();
assert_eq!(field_ty, ["ID", "String", "URL"]);
Ok(())
}
use apollo_compiler::{ApolloCompiler, hir, HirDatabase};
use anyhow::{anyhow, Result};
fn main() -> Result<()> {
let schema_input = r#"
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
}
directive @join__field(graph: join__Graph, requires: join__FieldSet, provides: join__FieldSet) on FIELD_DEFINITION
"#;
let query_input = r#"
query getProduct {
size
topProducts {
name
inStock
}
}
"#;
let mut compiler = ApolloCompiler::new();
compiler.add_type_system(schema_input, "schema.graphql");
let query_id = compiler.add_executable(query_input, "query.graphql");
let diagnostics = compiler.validate();
for diagnostic in &diagnostics {
println!("{}", diagnostic);
}
assert!(diagnostics.is_empty());
let operations = compiler.db.operations(query_id);
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(&compiler.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(&compiler.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(())
}
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 mut compiler = ApolloCompiler::new();
compiler.add_document(input, "document.graphql");
let diagnostics = compiler.validate();
for diagnostic in &diagnostics {
println!("{}", diagnostic)
}
assert_eq!(diagnostics.len(), 9)
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.