Repository: apollographql/apollo-rs
Pull request #290 (2022-09-01 13:57:23)

refactor(apollo-compiler): split up db into several components
This PR is a bit of prep as part of making #275 work.

We are splitting up the single `query_group` we had in our db into several `query_group`s that currently just build upon each other, and eventually could support more complex relationships between one another. The current structure:

`Inputs` --> `DocumentParser` --> `Definitions` --> `Document`

All of these `query_group`s make up the `RootDatabase`, i.e. `salsa::database`. 

There is also a temporary change to `selection.ty(db)` method, where it now expects the db argument. This was changed as part of the process of separating out `Definitions` and `Document` query methods. The intention is to abstract out any db arguments from external user usage further down the line.


Patch:
diff --git a/crates/apollo-compiler/README.md b/crates/apollo-compiler/README.md
index 9f31a20..271188c 100644
--- a/crates/apollo-compiler/README.md
+++ b/crates/apollo-compiler/README.md
@@ -86,7 +86,7 @@ assert!(diagnostics.is_empty());
 #### Accessing fragment definition field types
 
 ```rust
-use apollo_compiler::{ApolloCompiler, values, SourceDatabase};
+use apollo_compiler::{ApolloCompiler, values, Document};
 use miette::Result;
 
 fn main() -> Result<()> {
@@ -142,7 +142,7 @@ fn main() -> Result<()> {
     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()))
+        .filter_map(|f| Some(f.ty(&ctx.db)?.name()))
         .collect();
     assert_eq!(field_ty, ["ID", "String", "URL"]);
     Ok(())
diff --git a/crates/apollo-compiler/src/lib.rs b/crates/apollo-compiler/src/lib.rs
index daeaef6..bd267ee 100644
--- a/crates/apollo-compiler/src/lib.rs
+++ b/crates/apollo-compiler/src/lib.rs
@@ -9,16 +9,13 @@ mod validation;
 use std::sync::Arc;
 
 use apollo_parser::{ast, SyntaxTree};
-pub use queries::{
-    database::{Database, SourceDatabase},
-    values,
-};
+pub use queries::{values, Definitions, Document, DocumentParser, Inputs, RootDatabase};
 
 pub use diagnostics::ApolloDiagnostic;
 use validation::Validator;
 
 pub struct ApolloCompiler {
-    pub db: Database,
+    pub db: RootDatabase,
 }
 
 /// Apollo compiler creates a context around your GraphQL. It creates refernces
@@ -69,15 +66,22 @@ pub struct ApolloCompiler {
 impl ApolloCompiler {
     /// Create a new instance of Apollo Compiler.
     pub fn new(input: &str) -> Self {
-        let mut db = Database::default();
+        let mut db = RootDatabase::default();
         let input = input.to_string();
-        db.set_input_string((), Arc::new(input));
+        db.set_input(input);
         Self { db }
     }
 
+    // NOTE @lrlna: uncomment when we are fully thread-safe.
+
+    /// Get a snapshot of the current database.
+    // pub fn snapshot(&self) -> salsa::Storage<RootDatabase> {
+    //     self.db.storage.snapshot()
+    // }
+
     /// Get access to the `apollo-parser's` AST.
-    pub fn parse(&self) -> Arc<SyntaxTree> {
-        self.db.parse()
+    pub fn ast(&self) -> SyntaxTree {
+        self.db.ast()
     }
 
     /// Validate your GraphQL input. Returns Diagnostics that you can pretty-print.
@@ -168,7 +172,30 @@ impl ApolloCompiler {
 mod test {
     use std::collections::HashMap;
 
-    use crate::{values::Definition, ApolloCompiler, SourceDatabase};
+    use crate::{values::Definition, ApolloCompiler, Document};
+
+    #[test]
+    fn is_db_send() {
+        let input = r#"
+type Query {
+  website: URL,
+  amount: Int
+}
+
+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());
+
+        // is_send(ctx.snapshot());
+
+        // fn is_send<T: Send>(db: T) {};
+    }
 
     #[test]
     fn it_accesses_operation_definition_parts() {
@@ -326,7 +353,7 @@ union Union = Concrete
         let interface_fields = interface_field.selection_set().fields();
         let interface_selection_fields_types: HashMap<_, _> = interface_fields
             .iter()
-            .map(|f| (f.name(), f.ty().map(|f| f.name())))
+            .map(|f| (f.name(), f.ty(&ctx.db).map(|f| f.name())))
             .collect();
         assert_eq!(
             interface_selection_fields_types,
@@ -342,7 +369,7 @@ union Union = Concrete
         let inline_fragment_fields = inline_fragment.selection_set().fields();
         let inline_fragment_fields_types: HashMap<_, _> = inline_fragment_fields
             .iter()
-            .map(|f| (f.name(), f.ty().map(|ty| ty.name())))
+            .map(|f| (f.name(), f.ty(&ctx.db).map(|ty| ty.name())))
             .collect();
         assert_eq!(
             inline_fragment_fields_types,
@@ -360,7 +387,7 @@ union Union = Concrete
         let union_inline_fragment_fields = union_inline_fragment.selection_set().fields();
         let union_inline_fragment_field_types: HashMap<_, _> = union_inline_fragment_fields
             .iter()
-            .map(|f| (f.name(), f.ty().map(|ty| ty.name())))
+            .map(|f| (f.name(), f.ty(&ctx.db).map(|ty| ty.name())))
             .collect();
         assert_eq!(
             union_inline_fragment_field_types,
@@ -414,7 +441,7 @@ type Product {
         let op_fields = get_product_op.fields(&ctx.db);
         let name_field_def: Vec<String> = op_fields
             .iter()
-            .filter_map(|field| Some(field.ty()?.name()))
+            .filter_map(|field| Some(field.ty(&ctx.db)?.name()))
             .collect();
         assert_eq!(name_field_def, ["Int", "Product"]);
 
@@ -428,7 +455,7 @@ type Product {
 
         let top_product_fields: Vec<String> = top_products
             .iter()
-            .filter_map(|f| Some(f.ty()?.name()))
+            .filter_map(|f| Some(f.ty(&ctx.db)?.name()))
             .collect();
         assert_eq!(top_product_fields, ["String", "Boolean"]);
 
@@ -513,7 +540,7 @@ scalar URL @specifiedBy(url: "https://tools.ietf.org/html/rfc3986")
             .collect();
         let field_ty: Vec<String> = fragment_fields
             .iter()
-            .filter_map(|f| Some(f.ty()?.name()))
+            .filter_map(|f| Some(f.ty(&ctx.db)?.name()))
             .collect();
         assert_eq!(field_ty, ["ID", "String", "URL"])
     }
diff --git a/crates/apollo-compiler/src/queries/database.rs b/crates/apollo-compiler/src/queries/database.rs
index 338e9e4..dc168b5 100644
--- a/crates/apollo-compiler/src/queries/database.rs
+++ b/crates/apollo-compiler/src/queries/database.rs
@@ -1,1777 +1,25 @@
 // All .expect() calls are used for parts of the GraphQL grammar that are
 // non-optional and will have an error produced in the parser if they are missing.
 
-use std::collections::HashSet;
-use std::sync::Arc;
+use crate::queries::{
+    def_db::DefinitionsStorage, document_db::DocumentStorage, inputs_db::InputsStorage,
+    parser_db::ParserStorage,
+};
 
-use apollo_parser::ast::{AstChildren, AstNode, SyntaxNodePtr};
-use apollo_parser::{ast, Parser, SyntaxTree};
-use uuid::Uuid;
-
-use crate::diagnostics::{ApolloDiagnostic, SyntaxError};
-// use crate::diagnostics::{ApolloDiagnostic, ErrorDiagnostic};
-use crate::values::*;
-
-#[salsa::database(ASTDatabase)]
+#[salsa::database(DocumentStorage, InputsStorage, ParserStorage, DefinitionsStorage)]
 #[derive(Default)]
-pub struct Database {
-    storage: salsa::Storage<Database>,
-}
-
-impl salsa::Database for Database {}
-
-// NOTE @lrlna: this is the root database.
-// In the long run we will create child databases based on definitions: i.e.
-// OperationDefinition DB, ObjectTypeDefinition etc. This is mostly going to be
-// useful for readability of this code.
-#[salsa::query_group(ASTDatabase)]
-pub trait SourceDatabase {
-    #[salsa::input]
-    fn input_string(&self, key: ()) -> Arc<String>;
-
-    fn parse(&self) -> Arc<SyntaxTree>;
-
-    fn document(&self) -> Arc<ast::Document>;
-
-    fn syntax_errors(&self) -> Vec<ApolloDiagnostic>;
-
-    fn definitions(&self) -> Arc<Vec<ast::Definition>>;
-
-    fn db_definitions(&self) -> Arc<Vec<Definition>>;
-
-    fn type_system_definitions(&self) -> Arc<Vec<Definition>>;
-
-    fn operations(&self) -> Arc<Vec<OperationDefinition>>;
-
-    fn fragments(&self) -> Arc<Vec<FragmentDefinition>>;
-
-    fn schema(&self) -> Arc<SchemaDefinition>;
-
-    fn object_types(&self) -> Arc<Vec<ObjectTypeDefinition>>;
-
-    fn scalars(&self) -> Arc<Vec<ScalarTypeDefinition>>;
-
-    fn enums(&self) -> Arc<Vec<EnumTypeDefinition>>;
-
-    fn unions(&self) -> Arc<Vec<UnionTypeDefinition>>;
-
-    fn interfaces(&self) -> Arc<Vec<InterfaceTypeDefinition>>;
-
-    fn directive_definitions(&self) -> Arc<Vec<DirectiveDefinition>>;
-
-    fn input_objects(&self) -> Arc<Vec<InputObjectTypeDefinition>>;
-
-    fn query_operations(&self) -> Arc<Vec<OperationDefinition>>;
-
-    fn mutation_operations(&self) -> Arc<Vec<OperationDefinition>>;
-
-    fn subscription_operations(&self) -> Arc<Vec<OperationDefinition>>;
-
-    fn find_operation(&self, id: Uuid) -> Option<Arc<OperationDefinition>>;
-
-    fn find_operation_by_name(&self, name: String) -> Option<Arc<OperationDefinition>>;
-
-    fn find_fragment(&self, id: Uuid) -> Option<Arc<FragmentDefinition>>;
-
-    fn find_fragment_by_name(&self, name: String) -> Option<Arc<FragmentDefinition>>;
-
-    fn find_object_type(&self, id: Uuid) -> Option<Arc<ObjectTypeDefinition>>;
-
-    fn find_object_type_by_name(&self, name: String) -> Option<Arc<ObjectTypeDefinition>>;
-
-    fn find_union_by_name(&self, name: String) -> Option<Arc<UnionTypeDefinition>>;
-
-    fn find_interface(&self, id: Uuid) -> Option<Arc<InterfaceTypeDefinition>>;
-
-    fn find_interface_by_name(&self, name: String) -> Option<Arc<InterfaceTypeDefinition>>;
-
-    fn find_directive_definition(&self, id: Uuid) -> Option<Arc<DirectiveDefinition>>;
-
-    fn find_directive_definition_by_name(&self, name: String) -> Option<Arc<DirectiveDefinition>>;
-
-    fn find_input_object(&self, id: Uuid) -> Option<Arc<InputObjectTypeDefinition>>;
-
-    fn find_input_object_by_name(&self, name: String) -> Option<Arc<InputObjectTypeDefinition>>;
-
-    fn find_definition_by_name(&self, name: String) -> Option<Arc<Definition>>;
-
-    fn find_type_system_definition(&self, id: Uuid) -> Option<Arc<Definition>>;
-
-    fn find_type_system_definition_by_name(&self, name: String) -> Option<Arc<Definition>>;
-
-    fn operation_definition_variables(&self, id: Uuid) -> Arc<HashSet<Variable>>;
-
-    fn selection_variables(&self, id: Uuid) -> Arc<HashSet<Variable>>;
-
-    fn operation_fields(&self, id: Uuid) -> Arc<Vec<Field>>;
-
-    fn operation_inline_fragment_fields(&self, id: Uuid) -> Arc<Vec<Field>>;
-
-    fn operation_fragment_spread_fields(&self, id: Uuid) -> Arc<Vec<Field>>;
-}
-
-// this is top level entry to the source db
-fn parse(db: &dyn SourceDatabase) -> Arc<SyntaxTree> {
-    let input = db.input_string(());
-    let parser = Parser::new(&input);
-    Arc::new(parser.parse())
-}
-
-// TODO @lrlna: a very expensive clone - should more tightly couple the parser and the
-// source database for a cleaner solution
-fn document(db: &dyn SourceDatabase) -> Arc<ast::Document> {
-    Arc::new(db.parse().as_ref().clone().document())
-}
-
-fn syntax_errors(db: &dyn SourceDatabase) -> Vec<ApolloDiagnostic> {
-    db.parse()
-        .errors()
-        .into_iter()
-        .map(|err| {
-            ApolloDiagnostic::SyntaxError(SyntaxError {
-                src: db.input_string(()).to_string(),
-                span: (err.index(), err.data().len()).into(), // (offset, length of error token)
-                message: err.message().into(),
-            })
-        })
-        .collect()
-}
-
-fn definitions(db: &dyn SourceDatabase) -> Arc<Vec<ast::Definition>> {
-    Arc::new(db.document().definitions().into_iter().collect())
-}
-
-fn db_definitions(db: &dyn SourceDatabase) -> Arc<Vec<Definition>> {
-    let mut definitions = Vec::new();
-
-    let operations: Vec<Definition> = db
-        .operations()
-        .iter()
-        .map(|def| Definition::OperationDefinition(def.clone()))
-        .collect();
-    let fragments: Vec<Definition> = db
-        .fragments()
-        .iter()
-        .map(|def| Definition::FragmentDefinition(def.clone()))
-        .collect();
-    let directives: Vec<Definition> = db
-        .directive_definitions()
-        .iter()
-        .map(|def| Definition::DirectiveDefinition(def.clone()))
-        .collect();
-    let scalars: Vec<Definition> = db
-        .scalars()
-        .iter()
-        .map(|def| Definition::ScalarTypeDefinition(def.clone()))
-        .collect();
-    let objects: Vec<Definition> = db
-        .object_types()
-        .iter()
-        .map(|def| Definition::ObjectTypeDefinition(def.clone()))
-        .collect();
-    let interfaces: Vec<Definition> = db
-        .interfaces()
-        .iter()
-        .map(|def| Definition::InterfaceTypeDefinition(def.clone()))
-        .collect();
-    let unions: Vec<Definition> = db
-        .unions()
-        .iter()
-        .map(|def| Definition::UnionTypeDefinition(def.clone()))
-        .collect();
-    let enums: Vec<Definition> = db
-        .enums()
-        .iter()
-        .map(|def| Definition::EnumTypeDefinition(def.clone()))
-        .collect();
-    let input_objects: Vec<Definition> = db
-        .input_objects()
-        .iter()
-        .map(|def| Definition::InputObjectTypeDefinition(def.clone()))
-        .collect();
-    let schema = Definition::SchemaDefinition(db.schema().as_ref().clone());
-
-    definitions.extend(operations);
-    definitions.extend(fragments);
-    definitions.extend(directives);
-    definitions.extend(scalars);
-    definitions.extend(objects);
-    definitions.extend(interfaces);
-    definitions.extend(unions);
-    definitions.extend(enums);
-    definitions.extend(input_objects);
-    definitions.push(schema);
-
-    Arc::new(definitions)
-}
-
-fn type_system_definitions(db: &dyn SourceDatabase) -> Arc<Vec<Definition>> {
-    let mut definitions = Vec::new();
-
-    let directives: Vec<Definition> = db
-        .directive_definitions()
-        .iter()
-        .map(|def| Definition::DirectiveDefinition(def.clone()))
-        .collect();
-    let scalars: Vec<Definition> = db
-        .scalars()
-        .iter()
-        .map(|def| Definition::ScalarTypeDefinition(def.clone()))
-        .collect();
-    let objects: Vec<Definition> = db
-        .object_types()
-        .iter()
-        .map(|def| Definition::ObjectTypeDefinition(def.clone()))
-        .collect();
-    let interfaces: Vec<Definition> = db
-        .interfaces()
-        .iter()
-        .map(|def| Definition::InterfaceTypeDefinition(def.clone()))
-        .collect();
-    let unions: Vec<Definition> = db
-        .unions()
-        .iter()
-        .map(|def| Definition::UnionTypeDefinition(def.clone()))
-        .collect();
-    let enums: Vec<Definition> = db
-        .enums()
-        .iter()
-        .map(|def| Definition::EnumTypeDefinition(def.clone()))
-        .collect();
-    let input_objects: Vec<Definition> = db
-        .input_objects()
-        .iter()
-        .map(|def| Definition::InputObjectTypeDefinition(def.clone()))
-        .collect();
-    let schema = Definition::SchemaDefinition(db.schema().as_ref().clone());
-
-    definitions.extend(directives);
-    definitions.extend(scalars);
-    definitions.extend(objects);
-    definitions.extend(interfaces);
-    definitions.extend(unions);
-    definitions.extend(enums);
-    definitions.extend(input_objects);
-    definitions.push(schema);
-
-    Arc::new(definitions)
-}
-
-fn find_definition_by_name(db: &dyn SourceDatabase, name: String) -> Option<Arc<Definition>> {
-    db.db_definitions().iter().find_map(|def| {
-        if let Some(n) = def.name() {
-            if name == n {
-                return Some(Arc::new(def.clone()));
-            }
-        }
-        None
-    })
-}
-
-fn find_type_system_definition(db: &dyn SourceDatabase, id: Uuid) -> Option<Arc<Definition>> {
-    db.type_system_definitions().iter().find_map(|op| {
-        if let Some(op_id) = op.id() {
-            if op_id == &id {
-                return Some(Arc::new(op.clone()));
-            }
-        }
-        None
-    })
-}
-
-fn find_type_system_definition_by_name(
-    db: &dyn SourceDatabase,
-    name: String,
-) -> Option<Arc<Definition>> {
-    db.type_system_definitions().iter().find_map(|def| {
-        if let Some(n) = def.name() {
-            if name == n {
-                return Some(Arc::new(def.clone()));
-            }
-        }
-        None
-    })
-}
-
-// NOTE: we might want to the values::OperationDefinition creation even further.
-// At the moment all fields in this struct are created here, instead individual
-// queries for selection_set, variables, directives etc can be created.
-fn operations(db: &dyn SourceDatabase) -> Arc<Vec<OperationDefinition>> {
-    let operations = db
-        .definitions()
-        .iter()
-        .filter_map(|definition| match definition {
-            ast::Definition::OperationDefinition(op_def) => {
-                Some(operation_definition(db, op_def.clone()))
-            }
-            _ => None,
-        })
-        .collect();
-    Arc::new(operations)
-}
-
-fn query_operations(db: &dyn SourceDatabase) -> Arc<Vec<OperationDefinition>> {
-    let operations = db
-        .operations()
-        .iter()
-        .filter_map(|op| op.operation_ty.is_query().then(|| op.clone()))
-        .collect();
-    Arc::new(operations)
-}
-
-fn subscription_operations(db: &dyn SourceDatabase) -> Arc<Vec<OperationDefinition>> {
-    let operations = db
-        .operations()
-        .iter()
-        .filter_map(|op| op.operation_ty.is_subscription().then(|| op.clone()))
-        .collect();
-    Arc::new(operations)
-}
-
-fn mutation_operations(db: &dyn SourceDatabase) -> Arc<Vec<OperationDefinition>> {
-    let operations = db
-        .operations()
-        .iter()
-        .filter_map(|op| op.operation_ty.is_mutation().then(|| op.clone()))
-        .collect();
-    Arc::new(operations)
-}
-
-fn find_operation(db: &dyn SourceDatabase, id: Uuid) -> Option<Arc<OperationDefinition>> {
-    db.operations().iter().find_map(|op| {
-        if &id == op.id() {
-            return Some(Arc::new(op.clone()));
-        }
-        None
-    })
-}
-
-fn find_operation_by_name(
-    db: &dyn SourceDatabase,
-    name: String,
-) -> Option<Arc<OperationDefinition>> {
-    db.operations().iter().find_map(|op| {
-        if let Some(n) = op.name() {
-            if n == name {
-                return Some(Arc::new(op.clone()));
-            }
-        }
-        None
-    })
-}
-
-// NOTE: potentially want to return a hashset of variables and their types?
-fn operation_definition_variables(db: &dyn SourceDatabase, id: Uuid) -> Arc<HashSet<Variable>> {
-    let vars: HashSet<Variable> = match db.find_operation(id) {
-        Some(op) => op
-            .variables()
-            .iter()
-            .map(|v| Variable {
-                name: v.name().to_owned(),
-                ast_ptr: v.ast_ptr().clone(),
-            })
-            .collect(),
-        None => HashSet::new(),
-    };
-    Arc::new(vars)
-}
-
-fn operation_fields(db: &dyn SourceDatabase, id: Uuid) -> Arc<Vec<Field>> {
-    let fields = match db.find_operation(id) {
-        Some(op) => op
-            .selection_set()
-            .selection()
-            .iter()
-            .filter_map(|sel| match sel {
-                Selection::Field(field) => Some(field.as_ref().clone()),
-                _ => None,
-            })
-            .collect(),
-        None => Vec::new(),
-    };
-    Arc::new(fields)
-}
-
-fn operation_inline_fragment_fields(db: &dyn SourceDatabase, id: Uuid) -> Arc<Vec<Field>> {
-    let fields: Vec<Field> = match db.find_operation(id) {
-        Some(op) => op
-            .selection_set()
-            .selection()
-            .iter()
-            .filter_map(|sel| match sel {
-                Selection::InlineFragment(fragment) => {
-                    let fields: Vec<Field> = fragment.selection_set().fields();
-                    Some(fields)
-                }
-                _ => None,
-            })
-            .flatten()
-            .collect(),
-        None => Vec::new(),
-    };
-    Arc::new(fields)
-}
-
-fn operation_fragment_spread_fields(db: &dyn SourceDatabase, id: Uuid) -> Arc<Vec<Field>> {
-    let fields: Vec<Field> = match db.find_operation(id) {
-        Some(op) => op
-            .selection_set()
-            .selection()
-            .iter()
-            .filter_map(|sel| match sel {
-                Selection::FragmentSpread(fragment_spread) => {
-                    let fields: Vec<Field> = fragment_spread.fragment(db)?.selection_set().fields();
-                    Some(fields)
-                }
-                _ => None,
-            })
-            .flatten()
-            .collect(),
-        None => Vec::new(),
-    };
-    Arc::new(fields)
-}
-
-// NOTE: potentially want to return a hashmap of variables and their types?
-fn selection_variables(db: &dyn SourceDatabase, id: Uuid) -> Arc<HashSet<Variable>> {
-    let vars = db
-        .operation_fields(id)
-        .iter()
-        .flat_map(|field| {
-            let vars: Vec<_> = field
-                .arguments()
-                .iter()
-                .flat_map(|arg| get_field_variable_value(arg.value.clone()))
-                .collect();
-            vars
-        })
-        .collect();
-    Arc::new(vars)
-}
-
-fn get_field_variable_value(val: Value) -> Vec<Variable> {
-    match val {
-        Value::Variable(var) => vec![var],
-        Value::List(list) => list
-            .iter()
-            .flat_map(|val| get_field_variable_value(val.clone()))
-            .collect(),
-        Value::Object(obj) => obj
-            .iter()
-            .flat_map(|val| get_field_variable_value(val.1.clone()))
-            .collect(),
-        _ => Vec::new(),
-    }
-}
-
-fn fragments(db: &dyn SourceDatabase) -> Arc<Vec<FragmentDefinition>> {
-    let fragments: Vec<FragmentDefinition> = db
-        .definitions()
-        .iter()
-        .filter_map(|definition| match definition {
-            ast::Definition::FragmentDefinition(fragment_def) => {
-                Some(fragment_definition(db, fragment_def.clone()))
-            }
-            _ => None,
-        })
-        .collect();
-    Arc::new(fragments)
-}
-
-fn find_fragment(db: &dyn SourceDatabase, id: Uuid) -> Option<Arc<FragmentDefinition>> {
-    db.fragments().iter().find_map(|fragment| {
-        if &id == fragment.id() {
-            return Some(Arc::new(fragment.clone()));
-        }
-        None
-    })
-}
-
-fn find_fragment_by_name(db: &dyn SourceDatabase, name: String) -> Option<Arc<FragmentDefinition>> {
-    db.fragments().iter().find_map(|fragment| {
-        if name == fragment.name() {
-            return Some(Arc::new(fragment.clone()));
-        }
-        None
-    })
-}
-
-fn schema(db: &dyn SourceDatabase) -> Arc<SchemaDefinition> {
-    let schema = db
-        .definitions()
-        .iter()
-        .find_map(|definition| match definition {
-            ast::Definition::SchemaDefinition(schema) => Some(schema.clone()),
-            _ => None,
-        });
-    let mut schema_def = schema.map_or(SchemaDefinition::default(), |s| schema_definition(db, s));
-
-    // NOTE(@lrlna):
-    //
-    // "Query", "Subscription", "Mutation" object type definitions do not need
-    // to be explicitly defined in a schema definition, but are implicitly
-    // added.
-    //
-    // There will be a time when we need to distinguish between implicit and
-    // explicit definitions for validation purposes.
-    let type_defs = add_object_type_id_to_schema(db);
-    type_defs
-        .iter()
-        .for_each(|ty| schema_def.set_root_operation_type_definition(ty.clone()));
-
-    Arc::new(schema_def)
-}
-
-fn object_types(db: &dyn SourceDatabase) -> Arc<Vec<ObjectTypeDefinition>> {
-    let objects = db
-        .definitions()
-        .iter()
-        .filter_map(|definition| match definition {
-            ast::Definition::ObjectTypeDefinition(obj_def) => {
-                Some(object_type_definition(obj_def.clone()))
-            }
-            _ => None,
-        })
-        .collect();
-    Arc::new(objects)
-}
-
-fn find_object_type(db: &dyn SourceDatabase, id: Uuid) -> Option<Arc<ObjectTypeDefinition>> {
-    db.object_types().iter().find_map(|object_type| {
-        if &id == object_type.id() {
-            return Some(Arc::new(object_type.clone()));
-        }
-        None
-    })
-}
-
-fn find_object_type_by_name(
-    db: &dyn SourceDatabase,
-    name: String,
-) -> Option<Arc<ObjectTypeDefinition>> {
-    db.object_types().iter().find_map(|object_type| {
-        if name == object_type.name() {
-            return Some(Arc::new(object_type.clone()));
-        }
-        None
-    })
-}
-
-fn scalars(db: &dyn SourceDatabase) -> Arc<Vec<ScalarTypeDefinition>> {
-    let scalars = db
-        .definitions()
-        .iter()
-        .filter_map(|definition| match definition {
-            ast::Definition::ScalarTypeDefinition(scalar_def) => {
-                Some(scalar_definition(scalar_def.clone()))
-            }
-            _ => None,
-        })
-        .collect();
-    let scalars = built_in_scalars(scalars);
-
-    Arc::new(scalars)
-}
-
-fn enums(db: &dyn SourceDatabase) -> Arc<Vec<EnumTypeDefinition>> {
-    let enums = db
-        .definitions()
-        .iter()
-        .filter_map(|definition| match definition {
-            ast::Definition::EnumTypeDefinition(enum_def) => {
-                Some(enum_definition(enum_def.clone()))
-            }
-            _ => None,
-        })
-        .collect();
-    Arc::new(enums)
-}
-
-fn unions(db: &dyn SourceDatabase) -> Arc<Vec<UnionTypeDefinition>> {
-    let unions = db
-        .definitions()
-        .iter()
-        .filter_map(|definition| match definition {
-            ast::Definition::UnionTypeDefinition(union_def) => {
-                Some(union_definition(db, union_def.clone()))
-            }
-            _ => None,
-        })
-        .collect();
-    Arc::new(unions)
-}
-
-fn find_union_by_name(db: &dyn SourceDatabase, name: String) -> Option<Arc<UnionTypeDefinition>> {
-    db.unions().iter().find_map(|union| {
-        if name == union.name() {
-            return Some(Arc::new(union.clone()));
-        }
-        None
-    })
-}
-
-fn interfaces(db: &dyn SourceDatabase) -> Arc<Vec<InterfaceTypeDefinition>> {
-    let interfaces = db
-        .definitions()
-        .iter()
-        .filter_map(|definition| match definition {
-            ast::Definition::InterfaceTypeDefinition(interface_def) => {
-                Some(interface_definition(interface_def.clone()))
-            }
-            _ => None,
-        })
-        .collect();
-    Arc::new(interfaces)
-}
-
-fn find_interface(db: &dyn SourceDatabase, id: Uuid) -> Option<Arc<InterfaceTypeDefinition>> {
-    db.interfaces().iter().find_map(|interface| {
-        if &id == interface.id() {
-            return Some(Arc::new(interface.clone()));
-        }
-        None
-    })
-}
-
-fn find_interface_by_name(
-    db: &dyn SourceDatabase,
-    name: String,
-) -> Option<Arc<InterfaceTypeDefinition>> {
-    db.interfaces().iter().find_map(|interface| {
-        if name == interface.name() {
-            return Some(Arc::new(interface.clone()));
-        }
-        None
-    })
-}
-
-fn directive_definitions(db: &dyn SourceDatabase) -> Arc<Vec<DirectiveDefinition>> {
-    let directives = db
-        .definitions()
-        .iter()
-        .filter_map(|definition| match definition {
-            ast::Definition::DirectiveDefinition(directive_def) => {
-                Some(directive_definition(directive_def.clone()))
-            }
-            _ => None,
-        })
-        .collect();
-
-    let directives = built_in_directives(directives);
-
-    Arc::new(directives)
-}
-
-fn find_directive_definition(
-    db: &dyn SourceDatabase,
-    id: Uuid,
-) -> Option<Arc<DirectiveDefinition>> {
-    db.directive_definitions().iter().find_map(|directive_def| {
-        if &id == directive_def.id() {
-            return Some(Arc::new(directive_def.clone()));
-        }
-        None
-    })
-}
-
-fn find_directive_definition_by_name(
-    db: &dyn SourceDatabase,
-    name: String,
-) -> Option<Arc<DirectiveDefinition>> {
-    db.directive_definitions().iter().find_map(|directive_def| {
-        if name == directive_def.name() {
-            return Some(Arc::new(directive_def.clone()));
-        }
-        None
-    })
-}
-
-fn input_objects(db: &dyn SourceDatabase) -> Arc<Vec<InputObjectTypeDefinition>> {
-    let directives = db
-        .definitions()
-        .iter()
-        .filter_map(|definition| match definition {
-            ast::Definition::InputObjectTypeDefinition(input_obj) => {
-                Some(input_object_definition(input_obj.clone()))
-            }
-            _ => None,
-        })
-        .collect();
-
-    Arc::new(directives)
-}
-
-fn find_input_object(db: &dyn SourceDatabase, id: Uuid) -> Option<Arc<InputObjectTypeDefinition>> {
-    db.input_objects().iter().find_map(|input_obj| {
-        if &id == input_obj.id() {
-            return Some(Arc::new(input_obj.clone()));
-        }
-        None
-    })
-}
-
-fn find_input_object_by_name(
-    db: &dyn SourceDatabase,
-    name: String,
-) -> Option<Arc<InputObjectTypeDefinition>> {
-    db.input_objects().iter().find_map(|input_obj| {
-        if name == input_obj.name() {
-            return Some(Arc::new(input_obj.clone()));
-        }
-        None
-    })
-}
-
-fn operation_definition(
-    db: &dyn SourceDatabase,
-    op_def: ast::OperationDefinition,
-) -> OperationDefinition {
-    // check if there are already operations
-    // if there are operations, they must have names
-    // if there are no names, an error must be raised that all operations must have a name
-    let name = op_def.name().map(|name| name.text().to_string());
-    let ty = operation_type(op_def.operation_type());
-    let object_id = object_type_uuid(db, ty);
-    let variables = variable_definitions(op_def.variable_definitions());
-    let selection_set = selection_set(db, op_def.selection_set(), object_id);
-    let directives = directives(op_def.directives());
-    let ast_ptr = SyntaxNodePtr::new(op_def.syntax());
-
-    OperationDefinition {
-        id: Uuid::new_v4(),
-        operation_ty: ty,
-        name,
-        variables,
-        selection_set,
-        directives,
-        object_id,
-        ast_ptr,
-    }
-}
-
-fn fragment_definition(
-    db: &dyn SourceDatabase,
-    fragment_def: ast::FragmentDefinition,
-) -> FragmentDefinition {
-    let name = name(
-        fragment_def
-            .fragment_name()
-            .expect("Fragment Definition must have a name")
-            .name(),
-    );
-    let type_condition = fragment_def
-        .type_condition()
-        .expect("Fragment Definition must have a type condition")
-        .named_type()
-        .expect("Type Condition must have a name")
-        .name()
-        .expect("Name must have text")
-        .text()
-        .to_string();
-    let reference_ty_id = db
-        .find_type_system_definition_by_name(type_condition.clone())
-        .and_then(|def| def.id().cloned());
-    let selection_set = selection_set(db, fragment_def.selection_set(), reference_ty_id);
-    let directives = directives(fragment_def.directives());
-    let ast_ptr = SyntaxNodePtr::new(fragment_def.syntax());
-
-    FragmentDefinition {
-        id: Uuid::new_v4(),
-        name,
-        type_condition,
-        reference_ty_id,
-        selection_set,
-        directives,
-        ast_ptr,
-    }
-}
-
-fn schema_definition(
-    db: &dyn SourceDatabase,
-    schema_def: ast::SchemaDefinition,
-) -> SchemaDefinition {
-    let description = description(schema_def.description());
-    let directives = directives(schema_def.directives());
-    let root_operation_type_definition =
-        root_operation_type_definition(db, schema_def.root_operation_type_definitions());
-    let ast_ptr = SyntaxNodePtr::new(schema_def.syntax());
-
-    SchemaDefinition {
-        description,
-        directives,
-        root_operation_type_definition,
-        ast_ptr: Some(ast_ptr),
-    }
-}
-
-fn object_type_definition(obj_def: ast::ObjectTypeDefinition) -> ObjectTypeDefinition {
-    let id = Uuid::new_v4();
-    let description = description(obj_def.description());
-    let name = name(obj_def.name());
-    let implements_interfaces = implements_interfaces(obj_def.implements_interfaces());
-    let directives = directives(obj_def.directives());
-    let fields_definition = fields_definition(obj_def.fields_definition());
-    let ast_ptr = SyntaxNodePtr::new(obj_def.syntax());
-
-    ObjectTypeDefinition {
-        id,
-        description,
-        name,
-        implements_interfaces,
-        directives,
-        fields_definition,
-        ast_ptr,
-    }
-}
-
-fn scalar_definition(scalar_def: ast::ScalarTypeDefinition) -> ScalarTypeDefinition {
-    let id = Uuid::new_v4();
-    let description = description(scalar_def.description());
-    let name = name(scalar_def.name());
-    let directives = directives(scalar_def.directives());
-    let ast_ptr = SyntaxNodePtr::new(scalar_def.syntax());
-
-    ScalarTypeDefinition {
-        id,
-        description,
-        name,
-        directives,
-        ast_ptr: Some(ast_ptr),
-        built_in: false,
-    }
-}
-
-fn enum_definition(enum_def: ast::EnumTypeDefinition) -> EnumTypeDefinition {
-    let id = Uuid::new_v4();
-    let description = description(enum_def.description());
-    let name = name(enum_def.name());
-    let directives = directives(enum_def.directives());
-    let enum_values_definition = enum_values_definition(enum_def.enum_values_definition());
-    let ast_ptr = SyntaxNodePtr::new(enum_def.syntax());
-
-    EnumTypeDefinition {
-        id,
-        description,
-        name,
-        directives,
-        enum_values_definition,
-        ast_ptr,
-    }
-}
-
-fn enum_values_definition(
-    enum_values_def: Option<ast::EnumValuesDefinition>,
-) -> Arc<Vec<EnumValueDefinition>> {
-    match enum_values_def {
-        Some(enum_values) => {
-            let enum_values = enum_values
-                .enum_value_definitions()
-                .into_iter()
-                .map(enum_value_definition)
-                .collect();
-            Arc::new(enum_values)
-        }
-        None => Arc::new(Vec::new()),
-    }
+pub struct RootDatabase {
+    pub storage: salsa::Storage<RootDatabase>,
 }
 
-fn enum_value_definition(enum_value_def: ast::EnumValueDefinition) -> EnumValueDefinition {
-    let description = description(enum_value_def.description());
-    let enum_value = enum_value(enum_value_def.enum_value());
-    let directives = directives(enum_value_def.directives());
-    let ast_ptr = SyntaxNodePtr::new(enum_value_def.syntax());
-
-    EnumValueDefinition {
-        description,
-        enum_value,
-        directives,
-        ast_ptr,
-    }
-}
-
-fn union_definition(
-    db: &dyn SourceDatabase,
-    union_def: ast::UnionTypeDefinition,
-) -> UnionTypeDefinition {
-    let id = Uuid::new_v4();
-    let description = description(union_def.description());
-    let name = name(union_def.name());
-    let directives = directives(union_def.directives());
-    let union_members = union_members(db, union_def.union_member_types());
-    let ast_ptr = SyntaxNodePtr::new(union_def.syntax());
-
-    UnionTypeDefinition {
-        id,
-        description,
-        name,
-        directives,
-        union_members,
-        ast_ptr,
-    }
-}
-
-fn union_members(
-    db: &dyn SourceDatabase,
-    union_members: Option<ast::UnionMemberTypes>,
-) -> Arc<Vec<UnionMember>> {
-    match union_members {
-        Some(members) => {
-            let mems = members
-                .named_types()
-                .into_iter()
-                .map(|u| union_member(db, u))
-                .collect();
-            Arc::new(mems)
-        }
-        None => Arc::new(Vec::new()),
-    }
-}
-
-fn union_member(db: &dyn SourceDatabase, member: ast::NamedType) -> UnionMember {
-    let name = name(member.name());
-    let object_id = db
-        .find_object_type_by_name(name.clone())
-        .map(|object_type| *object_type.id());
-    let ast_ptr = SyntaxNodePtr::new(member.syntax());
-
-    UnionMember {
-        name,
-        object_id,
-        ast_ptr,
-    }
-}
-
-fn interface_definition(interface_def: ast::InterfaceTypeDefinition) -> InterfaceTypeDefinition {
-    let id = Uuid::new_v4();
-    let description = description(interface_def.description());
-    let name = name(interface_def.name());
-    let implements_interfaces = implements_interfaces(interface_def.implements_interfaces());
-    let directives = directives(interface_def.directives());
-    let fields_definition = fields_definition(interface_def.fields_definition());
-    let ast_ptr = SyntaxNodePtr::new(interface_def.syntax());
-
-    InterfaceTypeDefinition {
-        id,
-        description,
-        name,
-        implements_interfaces,
-        directives,
-        fields_definition,
-        ast_ptr,
-    }
-}
-
-fn directive_definition(directive_def: ast::DirectiveDefinition) -> DirectiveDefinition {
-    let name = name(directive_def.name());
-    let description = description(directive_def.description());
-    let arguments = arguments_definition(directive_def.arguments_definition());
-    let repeatable = directive_def.repeatable_token().is_some();
-    let directive_locations = directive_locations(directive_def.directive_locations());
-    let ast_ptr = SyntaxNodePtr::new(directive_def.syntax());
-
-    DirectiveDefinition {
-        id: Uuid::new_v4(),
-        description,
-        name,
-        arguments,
-        repeatable,
-        directive_locations,
-        ast_ptr: Some(ast_ptr),
-    }
-}
-
-fn input_object_definition(input_obj: ast::InputObjectTypeDefinition) -> InputObjectTypeDefinition {
-    let id = Uuid::new_v4();
-    let description = description(input_obj.description());
-    let name = name(input_obj.name());
-    let directives = directives(input_obj.directives());
-    let input_fields_definition = input_fields_definition(input_obj.input_fields_definition());
-    let ast_ptr = SyntaxNodePtr::new(input_obj.syntax());
-
-    InputObjectTypeDefinition {
-        id,
-        description,
-        name,
-        directives,
-        input_fields_definition,
-        ast_ptr,
-    }
-}
-
-fn add_object_type_id_to_schema(db: &dyn SourceDatabase) -> Arc<Vec<RootOperationTypeDefinition>> {
-    // Schema Definition does not have to be present in the SDL if ObjectType name is
-    // - Query
-    // - Subscription
-    // - Mutation
-    //
-    // Compiler's internal schema, however, should have a reference to these
-    // object types if they are present
-    let type_defs: Vec<RootOperationTypeDefinition> = db
-        .object_types()
-        .iter()
-        .filter_map(|obj_type| {
-            let obj_name = obj_type.name();
-            if matches!(obj_name, "Query" | "Subscription" | "Mutation") {
-                let operation_type = obj_name.into();
-                Some(RootOperationTypeDefinition {
-                    object_type_id: Some(*obj_type.id()),
-                    operation_type,
-                    named_type: Type::Named {
-                        name: obj_name.to_string(),
-                        ast_ptr: None,
-                    },
-                    ast_ptr: None,
-                })
-            } else {
-                None
-            }
-        })
-        .collect();
-
-    Arc::new(type_defs)
-}
-
-fn implements_interfaces(
-    implements_interfaces: Option<ast::ImplementsInterfaces>,
-) -> Arc<Vec<ImplementsInterface>> {
-    let interfaces: Vec<ImplementsInterface> = implements_interfaces
-        .iter()
-        .flat_map(|interfaces| {
-            let types: Vec<ImplementsInterface> = interfaces
-                .named_types()
-                .map(|n| ImplementsInterface {
-                    interface: n.name().expect("Name must have text").text().to_string(),
-                    ast_ptr: SyntaxNodePtr::new(n.syntax()),
-                })
-                .collect();
-            types
-        })
-        .collect();
-
-    Arc::new(interfaces)
-}
-
-fn fields_definition(
-    fields_definition: Option<ast::FieldsDefinition>,
-) -> Arc<Vec<FieldDefinition>> {
-    match fields_definition {
-        Some(fields_def) => {
-            let fields: Vec<FieldDefinition> = fields_def
-                .field_definitions()
-                .map(field_definition)
-                .collect();
-            Arc::new(fields)
-        }
-        None => Arc::new(Vec::new()),
-    }
-}
-
-fn field_definition(field: ast::FieldDefinition) -> FieldDefinition {
-    let description = description(field.description());
-    let name = name(field.name());
-    let arguments = arguments_definition(field.arguments_definition());
-    let ty = ty(field.ty().expect("Field must have a type"));
-    let directives = directives(field.directives());
-    let ast_ptr = SyntaxNodePtr::new(field.syntax());
-
-    FieldDefinition {
-        description,
-        name,
-        arguments,
-        ty,
-        directives,
-        ast_ptr,
-    }
-}
-
-fn arguments_definition(
-    arguments_definition: Option<ast::ArgumentsDefinition>,
-) -> ArgumentsDefinition {
-    match arguments_definition {
-        Some(arguments) => {
-            let input_values = input_value_definitions(arguments.input_value_definitions());
-            let ast_ptr = SyntaxNodePtr::new(arguments.syntax());
-
-            ArgumentsDefinition {
-                input_values,
-                ast_ptr: Some(ast_ptr),
-            }
-        }
-        None => ArgumentsDefinition {
-            input_values: Arc::new(Vec::new()),
-            ast_ptr: None,
-        },
-    }
-}
-
-fn input_fields_definition(
-    input_fields: Option<ast::InputFieldsDefinition>,
-) -> Arc<Vec<InputValueDefinition>> {
-    match input_fields {
-        Some(fields) => input_value_definitions(fields.input_value_definitions()),
-        None => Arc::new(Vec::new()),
-    }
-}
-
-fn input_value_definitions(
-    input_values: AstChildren<ast::InputValueDefinition>,
-) -> Arc<Vec<InputValueDefinition>> {
-    let input_values: Vec<InputValueDefinition> = input_values
-        .map(|input| {
-            let description = description(input.description());
-            let name = name(input.name());
-            let ty = ty(input.ty().expect("Input Definition must have a type"));
-            let default_value = default_value(input.default_value());
-            let directives = directives(input.directives());
-            let ast_ptr = SyntaxNodePtr::new(input.syntax());
-
-            InputValueDefinition {
-                description,
-                name,
-                ty,
-                default_value,
-                directives,
-                ast_ptr: Some(ast_ptr),
-            }
-        })
-        .collect();
-    Arc::new(input_values)
-}
-
-fn default_value(default_value: Option<ast::DefaultValue>) -> Option<DefaultValue> {
-    default_value.map(|val| value(val.value().expect("Default Value must have a value token")))
-}
-
-fn root_operation_type_definition(
-    db: &dyn SourceDatabase,
-    root_type_def: AstChildren<ast::RootOperationTypeDefinition>,
-) -> Arc<Vec<RootOperationTypeDefinition>> {
-    let type_defs: Vec<RootOperationTypeDefinition> = root_type_def
-        .into_iter()
-        .map(|ty| {
-            let operation_type = operation_type(ty.operation_type());
-            let named_type = named_type(
-                ty.named_type()
-                    .expect("Root Operation Type Definition must have Named Type.")
-                    .name(),
-            );
-            let object_type_id = db
-                .find_object_type_by_name(named_type.name())
-                .map(|object_type| *object_type.id());
-            let ast_ptr = SyntaxNodePtr::new(ty.syntax());
-
-            RootOperationTypeDefinition {
-                object_type_id,
-                operation_type,
-                named_type,
-                ast_ptr: Some(ast_ptr),
-            }
-        })
-        .collect();
-
-    Arc::new(type_defs)
-}
-
-fn operation_type(op_type: Option<ast::OperationType>) -> OperationType {
-    match op_type {
-        Some(ty) => {
-            if ty.query_token().is_some() {
-                OperationType::Query
-            } else if ty.mutation_token().is_some() {
-                OperationType::Mutation
-            } else if ty.subscription_token().is_some() {
-                OperationType::Subscription
-            } else {
-                OperationType::Query
-            }
-        }
-        None => OperationType::Query,
-    }
-}
-
-fn variable_definitions(
-    variable_definitions: Option<ast::VariableDefinitions>,
-) -> Arc<Vec<VariableDefinition>> {
-    match variable_definitions {
-        Some(vars) => {
-            let variable_definitions = vars
-                .variable_definitions()
-                .into_iter()
-                .map(variable_definition)
-                .collect();
-            Arc::new(variable_definitions)
-        }
-        None => Arc::new(Vec::new()),
-    }
-}
-
-fn variable_definition(var: ast::VariableDefinition) -> VariableDefinition {
-    let name = name(
-        var.variable()
-            .expect("Variable Definition must have a variable")
-            .name(),
-    );
-    let directives = directives(var.directives());
-    let default_value = default_value(var.default_value());
-    let ty = ty(var.ty().expect("Variable Definition must have a type"));
-    let ast_ptr = SyntaxNodePtr::new(var.syntax());
-
-    VariableDefinition {
-        name,
-        directives,
-        ty,
-        default_value,
-        ast_ptr,
-    }
-}
-
-fn ty(ty_: ast::Type) -> Type {
-    match ty_ {
-        ast::Type::NamedType(name) => named_type(name.name()),
-        ast::Type::ListType(list) => Type::List {
-            ty: Box::new(ty(list.ty().expect("List Type must have a type"))),
-            ast_ptr: Some(SyntaxNodePtr::new(list.syntax())),
-        },
-        ast::Type::NonNullType(non_null) => {
-            if let Some(n) = non_null.named_type() {
-                let named_type = named_type(n.name());
-                Type::NonNull {
-                    ty: Box::new(named_type),
-                    ast_ptr: Some(SyntaxNodePtr::new(n.syntax())),
-                }
-            } else if let Some(list) = non_null.list_type() {
-                let list_type = Type::List {
-                    ty: Box::new(ty(list.ty().expect("List Type must have a type"))),
-                    ast_ptr: Some(SyntaxNodePtr::new(list.syntax())),
-                };
-                Type::NonNull {
-                    ty: Box::new(list_type),
-                    ast_ptr: Some(SyntaxNodePtr::new(list.syntax())),
-                }
-            } else {
-                // TODO: parser should have caught an error if there wasn't
-                // either a named type or list type. Figure out a graceful way
-                // to surface this error from the parser.
-                panic!("Parser should have caught this error");
-            }
-        }
-    }
-}
+impl salsa::Database for RootDatabase {}
 
-fn named_type(n: Option<ast::Name>) -> Type {
-    let name = n.expect("Named Type must have a name");
+// NOTE @lrlna: uncomment when we are fully thread-safe.
 
-    Type::Named {
-        name: name.text().to_string(),
-        ast_ptr: Some(SyntaxNodePtr::new(name.syntax())),
-    }
-}
-
-fn directive_locations(
-    directive_locations: Option<ast::DirectiveLocations>,
-) -> Arc<Vec<DirectiveLocation>> {
-    match directive_locations {
-        Some(directive_loc) => {
-            let locations: Vec<DirectiveLocation> = directive_loc
-                .directive_locations()
-                .into_iter()
-                .map(|loc| loc.into())
-                .collect();
-            Arc::new(locations)
-        }
-        None => Arc::new(Vec::new()),
-    }
-}
-
-fn directives(directives: Option<ast::Directives>) -> Arc<Vec<Directive>> {
-    match directives {
-        Some(directives) => {
-            let directives = directives.directives().into_iter().map(directive).collect();
-            Arc::new(directives)
-        }
-        None => Arc::new(Vec::new()),
-    }
-}
-
-fn directive(directive: ast::Directive) -> Directive {
-    let name = name(directive.name());
-    let arguments = arguments(directive.arguments());
-    let ast_ptr = SyntaxNodePtr::new(directive.syntax());
-
-    Directive {
-        name,
-        arguments,
-        ast_ptr,
-    }
-}
-
-fn arguments(arguments: Option<ast::Arguments>) -> Arc<Vec<Argument>> {
-    match arguments {
-        Some(arguments) => {
-            let arguments = arguments.arguments().into_iter().map(argument).collect();
-            Arc::new(arguments)
-        }
-        None => Arc::new(Vec::new()),
-    }
-}
-
-fn argument(argument: ast::Argument) -> Argument {
-    let name = name(argument.name());
-    let value = value(argument.value().expect("Argument must have a value"));
-    let ast_ptr = SyntaxNodePtr::new(argument.syntax());
-
-    Argument {
-        name,
-        value,
-        ast_ptr,
-    }
-}
-
-fn value(val: ast::Value) -> Value {
-    match val {
-        ast::Value::Variable(var) => Value::Variable(Variable {
-            name: name(var.name()),
-            ast_ptr: SyntaxNodePtr::new(var.syntax()),
-        }),
-        ast::Value::StringValue(string_val) => Value::String(string_val.into()),
-        ast::Value::FloatValue(float) => Value::Float(Float::new(float.into())),
-        ast::Value::IntValue(int) => Value::Int(int.into()),
-        ast::Value::BooleanValue(bool) => Value::Boolean(bool.into()),
-        ast::Value::NullValue(_) => Value::Null,
-        ast::Value::EnumValue(enum_) => Value::Enum(name(enum_.name())),
-        ast::Value::ListValue(list) => {
-            let list: Vec<Value> = list.values().map(value).collect();
-            Value::List(list)
-        }
-        ast::Value::ObjectValue(object) => {
-            let object_values: Vec<(String, Value)> = object
-                .object_fields()
-                .map(|o| {
-                    let name = name(o.name());
-                    let value = value(o.value().expect("Object Value must have a value"));
-                    (name, value)
-                })
-                .collect();
-            Value::Object(object_values)
-        }
-    }
-}
-
-fn selection_set(
-    db: &dyn SourceDatabase,
-    selections: Option<ast::SelectionSet>,
-    object_id: Option<Uuid>,
-) -> SelectionSet {
-    let selection_set = match selections {
-        Some(sel) => sel
-            .selections()
-            .into_iter()
-            .map(|sel| selection(db, sel, object_id))
-            .collect(),
-        None => Vec::new(),
-    };
-
-    SelectionSet {
-        selection: Arc::new(selection_set),
-    }
-}
-
-fn selection(
-    db: &dyn SourceDatabase,
-    selection: ast::Selection,
-    object_id: Option<Uuid>,
-) -> Selection {
-    match selection {
-        ast::Selection::Field(sel_field) => {
-            let field = field(db, sel_field, object_id);
-            Selection::Field(field)
-        }
-        ast::Selection::FragmentSpread(fragment) => {
-            let fragment_spread = fragment_spread(fragment);
-            Selection::FragmentSpread(fragment_spread)
-        }
-        ast::Selection::InlineFragment(fragment) => {
-            let inline_fragment = inline_fragment(db, fragment, object_id);
-            Selection::InlineFragment(inline_fragment)
-        }
-    }
-}
-
-fn inline_fragment(
-    db: &dyn SourceDatabase,
-    fragment: ast::InlineFragment,
-    object_id: Option<Uuid>,
-) -> Arc<InlineFragment> {
-    let type_condition = fragment.type_condition().map(|tc| {
-        tc.named_type()
-            .expect("Type Condition must have a name")
-            .name()
-            .expect("Name must have text")
-            .text()
-            .to_string()
-    });
-    let reference_ty_id = if let Some(type_condition) = type_condition.clone() {
-        db.find_type_system_definition_by_name(type_condition)
-            .and_then(|def| def.id().cloned())
-    } else {
-        object_id
-    };
-    let directives = directives(fragment.directives());
-    let selection_set: SelectionSet = selection_set(db, fragment.selection_set(), reference_ty_id);
-    let ast_ptr = SyntaxNodePtr::new(fragment.syntax());
-
-    let fragment_data = InlineFragment {
-        type_condition,
-        directives,
-        selection_set,
-        ast_ptr,
-    };
-    Arc::new(fragment_data)
-}
-
-fn fragment_spread(fragment: ast::FragmentSpread) -> Arc<FragmentSpread> {
-    let name = name(
-        fragment
-            .fragment_name()
-            .expect("Fragment Spread must have a name")
-            .name(),
-    );
-    let directives = directives(fragment.directives());
-    let ast_ptr = SyntaxNodePtr::new(fragment.syntax());
-
-    let fragment_data = FragmentSpread {
-        name,
-        directives,
-        ast_ptr,
-    };
-    Arc::new(fragment_data)
-}
-
-fn field(db: &dyn SourceDatabase, field: ast::Field, ty_id: Option<Uuid>) -> Arc<Field> {
-    let name = name(field.name());
-    let alias = alias(field.alias());
-    let ty = field_ty(db, &name, ty_id);
-    let new_ty_id = field_ty_id(&field, db, ty.as_ref());
-    let selection_set = selection_set(db, field.selection_set(), new_ty_id);
-    let directives = directives(field.directives());
-    let arguments = arguments(field.arguments());
-    let ast_ptr = SyntaxNodePtr::new(field.syntax());
-
-    let field_data = Field {
-        name,
-        alias,
-        selection_set,
-        ty,
-        reference_ty_id: ty_id,
-        directives,
-        arguments,
-        ast_ptr,
-    };
-    Arc::new(field_data)
-}
-
-fn field_ty(db: &dyn SourceDatabase, field_name: &str, ty_id: Option<Uuid>) -> Option<Type> {
-    if let Some(id) = ty_id {
-        Some(
-            db.find_type_system_definition(id)?
-                .field(field_name)?
-                .ty()
-                .clone(),
-        )
-    } else {
-        None
-    }
-}
-
-fn field_ty_id(
-    field: &ast::Field,
-    db: &dyn SourceDatabase,
-    field_ty: Option<&Type>,
-) -> Option<Uuid> {
-    match field_ty {
-        Some(ty) => {
-            if field.selection_set().is_some() {
-                db.find_type_system_definition_by_name(ty.name())
-                    .and_then(|def| def.id().cloned())
-            } else {
-                None
-            }
-        }
-        None => None,
-    }
-}
-
-fn name(name: Option<ast::Name>) -> String {
-    name.expect("Field must have a name").text().to_string()
-}
-
-fn enum_value(enum_value: Option<ast::EnumValue>) -> String {
-    enum_value
-        .expect("Enum value must have a name")
-        .name()
-        .expect("Name must have text")
-        .text()
-        .to_string()
-}
-
-fn description(description: Option<ast::Description>) -> Option<String> {
-    description.map(|desc| {
-        desc.string_value()
-            .expect("Description must have text")
-            .into()
-    })
-}
-
-fn alias(alias: Option<ast::Alias>) -> Option<Arc<Alias>> {
-    alias.map(|alias| {
-        let name = alias.name().expect("Alias must have a name").to_string();
-        let alias_data = Alias(name);
-        Arc::new(alias_data)
-    })
-}
-
-fn object_type_uuid(db: &dyn SourceDatabase, ty: OperationType) -> Option<Uuid> {
-    match ty {
-        OperationType::Query => db.schema().query(db).map(|query| *query.id()),
-        OperationType::Mutation => db.schema().mutation(db).map(|mutation| *mutation.id()),
-        OperationType::Subscription => db.schema().subscription(db).map(|sub| *sub.id()),
-    }
-}
-
-//  Int, Float, String, Boolean, and ID
-fn built_in_scalars(mut scalars: Vec<ScalarTypeDefinition>) -> Vec<ScalarTypeDefinition> {
-    scalars.push(int_scalar());
-    scalars.push(float_scalar());
-    scalars.push(string_scalar());
-    scalars.push(boolean_scalar());
-    scalars.push(id_scalar());
-
-    scalars
-}
-
-fn int_scalar() -> ScalarTypeDefinition {
-    ScalarTypeDefinition {
-        id: Uuid::new_v4(),
-        description: Some("The `Int` scalar type represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1.".into()),
-        name: "Int".into(),
-        directives: Arc::new(Vec::new()),
-        ast_ptr: None,
-        built_in: true
-    }
-}
-
-fn float_scalar() -> ScalarTypeDefinition {
-    ScalarTypeDefinition {
-        id: Uuid::new_v4(),
-        description: Some("The `Float` scalar type represents signed double-precision fractional values as specified by [IEEE 754](https://en.wikipedia.org/wiki/IEEE_floating_point).".into()),
-        name: "Float".into(),
-        directives: Arc::new(Vec::new()),
-        ast_ptr: None,
-        built_in: true
-    }
-}
-
-fn string_scalar() -> ScalarTypeDefinition {
-    ScalarTypeDefinition {
-        id: Uuid::new_v4(),
-        description: Some("The `String` scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text.".into()),
-        name: "String".into(),
-        directives: Arc::new(Vec::new()),
-        ast_ptr: None,
-        built_in: true
-    }
-}
-
-fn boolean_scalar() -> ScalarTypeDefinition {
-    ScalarTypeDefinition {
-        id: Uuid::new_v4(),
-        description: Some("The `Boolean` scalar type represents `true` or `false`.".into()),
-        name: "Boolean".into(),
-        directives: Arc::new(Vec::new()),
-        ast_ptr: None,
-        built_in: true,
-    }
-}
-
-fn id_scalar() -> ScalarTypeDefinition {
-    ScalarTypeDefinition {
-        id: Uuid::new_v4(),
-        description: Some("The `ID` scalar type represents a unique identifier, often used to refetch an object or as key for a cache. The ID type appears in a JSON response as a String; however, it is not intended to be human-readable. When expected as an input type, any string (such as `\"4\"`) or integer (such as `4`) input value will be accepted as an ID.".into()),
-        name: "ID".into(),
-        directives: Arc::new(Vec::new()),
-        ast_ptr: None,
-        built_in: true
-    }
-}
-
-fn built_in_directives(mut directives: Vec<DirectiveDefinition>) -> Vec<DirectiveDefinition> {
-    if !directives.iter().any(|dir| dir.name == "skip") {
-        directives.push(skip_directive());
-    }
-
-    if !directives.iter().any(|dir| dir.name == "specifiedBy") {
-        directives.push(specified_by_directive());
-    }
-
-    if !directives.iter().any(|dir| dir.name == "deprecated") {
-        directives.push(deprecated_directive());
-    }
-
-    if !directives.iter().any(|dir| dir.name == "include") {
-        directives.push(include_directive());
-    }
-
-    directives
-}
-
-fn skip_directive() -> DirectiveDefinition {
-    // "Directs the executor to skip this field or fragment when the `if` argument is true."
-    // directive @skip(
-    //   "Skipped when true."
-    //   if: Boolean!
-    // ) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT
-    DirectiveDefinition {
-        id: Uuid::new_v4(),
-        description: Some(
-            "Directs the executor to skip this field or fragment when the `if` argument is true."
-                .into(),
-        ),
-        name: "skip".into(),
-        arguments: ArgumentsDefinition {
-            input_values: Arc::new(vec![InputValueDefinition {
-                description: Some("Skipped when true.".into()),
-                name: "if".into(),
-                ty: Type::NonNull {
-                    ty: Box::new(Type::Named {
-                        name: "Boolean".into(),
-                        ast_ptr: None,
-                    }),
-                    ast_ptr: None,
-                },
-                default_value: None,
-                directives: Arc::new(Vec::new()),
-                ast_ptr: None,
-            }]),
-            ast_ptr: None,
-        },
-        repeatable: false,
-        directive_locations: Arc::new(vec![
-            DirectiveLocation::Field,
-            DirectiveLocation::FragmentSpread,
-            DirectiveLocation::InlineFragment,
-        ]),
-        ast_ptr: None,
-    }
-}
-
-fn specified_by_directive() -> DirectiveDefinition {
-    // "Exposes a URL that specifies the behaviour of this scalar."
-    // directive @specifiedBy(
-    //     "The URL that specifies the behaviour of this scalar."
-    //     url: String!
-    // ) on SCALAR
-    DirectiveDefinition {
-        id: Uuid::new_v4(),
-        description: Some("Exposes a URL that specifies the behaviour of this scalar.".into()),
-        name: "specifiedBy".into(),
-        arguments: ArgumentsDefinition {
-            input_values: Arc::new(vec![InputValueDefinition {
-                description: Some("The URL that specifies the behaviour of this scalar.".into()),
-                name: "url".into(),
-                ty: Type::NonNull {
-                    ty: Box::new(Type::Named {
-                        name: "String".into(),
-                        ast_ptr: None,
-                    }),
-                    ast_ptr: None,
-                },
-                default_value: None,
-                directives: Arc::new(Vec::new()),
-                ast_ptr: None,
-            }]),
-            ast_ptr: None,
-        },
-        repeatable: false,
-        directive_locations: Arc::new(vec![DirectiveLocation::Scalar]),
-        ast_ptr: None,
-    }
-}
-
-fn deprecated_directive() -> DirectiveDefinition {
-    // "Marks an element of a GraphQL schema as no longer supported."
-    // directive @deprecated(
-    //   """
-    //   Explains why this element was deprecated, usually also including a
-    //   suggestion for how to access supported similar data. Formatted using
-    //   the Markdown syntax, as specified by
-    //   [CommonMark](https://commonmark.org/).
-    //   """
-    //   reason: String = "No longer supported"
-    // ) on FIELD_DEFINITION | ENUM_VALUE
-    DirectiveDefinition {
-        id: Uuid::new_v4(),
-        description: Some("Marks an element of a GraphQL schema as no longer supported.".into()),
-        name: "deprecated".into(),
-        arguments: ArgumentsDefinition {
-            input_values: Arc::new(vec![InputValueDefinition {
-                description: Some(
-                    "Explains why this element was deprecated, usually also including a suggestion for how to access supported similar data. Formatted using the Markdown syntax, as specified by [CommonMark](https://commonmark.org/).".into(),
-                ),
-                name: "reason".into(),
-                ty: Type::Named {
-                    name: "String".into(),
-                    ast_ptr: None,
-                },
-                default_value: Some(DefaultValue::String("No longer supported".into())),
-                directives: Arc::new(Vec::new()),
-                ast_ptr: None
-            }]),
-            ast_ptr: None
-        },
-        repeatable: false,
-        directive_locations: Arc::new(vec![
-            DirectiveLocation::FieldDefinition,
-            DirectiveLocation::EnumValue
-        ]),
-        ast_ptr: None
-    }
-}
-
-fn include_directive() -> DirectiveDefinition {
-    // "Directs the executor to include this field or fragment only when the `if` argument is true."
-    // directive @include(
-    //   "Included when true."
-    //   if: Boolean!
-    // ) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT
-    DirectiveDefinition {
-        id: Uuid::new_v4(),
-        description: Some("Directs the executor to include this field or fragment only when the `if` argument is true.".into()),
-        name: "include".into(),
-        arguments: ArgumentsDefinition {
-            input_values: Arc::new(vec![InputValueDefinition {
-                description: Some(
-                    "Included when true.".into(),
-                ),
-                name: "if".into(),
-                ty: Type::NonNull {
-                    ty: Box::new(Type::Named {
-                        name: "Boolean".into(),
-                        ast_ptr: None,
-                    }),
-                    ast_ptr: None,
-                },
-                default_value: None,
-                directives: Arc::new(Vec::new()),
-                ast_ptr: None
-            }]),
-            ast_ptr: None
-        },
-        repeatable: false,
-        directive_locations: Arc::new(vec![
-            DirectiveLocation::Field,
-            DirectiveLocation::FragmentDefinition,
-            DirectiveLocation::InlineFragment,
-        ]),
-        ast_ptr: None
-    }
-}
+// impl salsa::ParallelDatabase for RootDatabase {
+//     fn snapshot(&self) -> salsa::Snapshot<DocumentDatabase> {
+//         salsa::Snapshot::new(DocumentDatabase {
+//             storage: self.storage.snapshot(),
+//         })
+//     }
+// }
diff --git a/crates/apollo-compiler/src/queries/def_db.rs b/crates/apollo-compiler/src/queries/def_db.rs
new file mode 100644
index 0000000..ade0fa7
--- /dev/null
+++ b/crates/apollo-compiler/src/queries/def_db.rs
@@ -0,0 +1,1329 @@
+use std::sync::Arc;
+
+use apollo_parser::ast::{self, AstChildren, AstNode, SyntaxNodePtr};
+use uuid::Uuid;
+
+use crate::{values::*, DocumentParser, Inputs};
+
+#[salsa::query_group(DefinitionsStorage)]
+pub trait Definitions: Inputs + DocumentParser {
+    fn definitions(&self) -> Arc<Vec<ast::Definition>>;
+
+    fn db_definitions(&self) -> Arc<Vec<Definition>>;
+
+    fn type_system_definitions(&self) -> Arc<Vec<Definition>>;
+
+    fn operations(&self) -> Arc<Vec<OperationDefinition>>;
+
+    fn fragments(&self) -> Arc<Vec<FragmentDefinition>>;
+
+    fn schema(&self) -> Arc<SchemaDefinition>;
+
+    fn object_types(&self) -> Arc<Vec<ObjectTypeDefinition>>;
+
+    fn scalars(&self) -> Arc<Vec<ScalarTypeDefinition>>;
+
+    fn enums(&self) -> Arc<Vec<EnumTypeDefinition>>;
+
+    fn unions(&self) -> Arc<Vec<UnionTypeDefinition>>;
+
+    fn interfaces(&self) -> Arc<Vec<InterfaceTypeDefinition>>;
+
+    fn directive_definitions(&self) -> Arc<Vec<DirectiveDefinition>>;
+
+    fn input_objects(&self) -> Arc<Vec<InputObjectTypeDefinition>>;
+}
+
+fn definitions(db: &dyn Definitions) -> Arc<Vec<ast::Definition>> {
+    Arc::new(db.document().definitions().into_iter().collect())
+}
+
+fn db_definitions(db: &dyn Definitions) -> Arc<Vec<Definition>> {
+    let mut definitions = Vec::new();
+
+    let operations: Vec<Definition> = db
+        .operations()
+        .iter()
+        .map(|def| Definition::OperationDefinition(def.clone()))
+        .collect();
+    let fragments: Vec<Definition> = db
+        .fragments()
+        .iter()
+        .map(|def| Definition::FragmentDefinition(def.clone()))
+        .collect();
+    let directives: Vec<Definition> = db
+        .directive_definitions()
+        .iter()
+        .map(|def| Definition::DirectiveDefinition(def.clone()))
+        .collect();
+    let scalars: Vec<Definition> = db
+        .scalars()
+        .iter()
+        .map(|def| Definition::ScalarTypeDefinition(def.clone()))
+        .collect();
+    let objects: Vec<Definition> = db
+        .object_types()
+        .iter()
+        .map(|def| Definition::ObjectTypeDefinition(def.clone()))
+        .collect();
+    let interfaces: Vec<Definition> = db
+        .interfaces()
+        .iter()
+        .map(|def| Definition::InterfaceTypeDefinition(def.clone()))
+        .collect();
+    let unions: Vec<Definition> = db
+        .unions()
+        .iter()
+        .map(|def| Definition::UnionTypeDefinition(def.clone()))
+        .collect();
+    let enums: Vec<Definition> = db
+        .enums()
+        .iter()
+        .map(|def| Definition::EnumTypeDefinition(def.clone()))
+        .collect();
+    let input_objects: Vec<Definition> = db
+        .input_objects()
+        .iter()
+        .map(|def| Definition::InputObjectTypeDefinition(def.clone()))
+        .collect();
+    let schema = Definition::SchemaDefinition(db.schema().as_ref().clone());
+
+    definitions.extend(operations);
+    definitions.extend(fragments);
+    definitions.extend(directives);
+    definitions.extend(scalars);
+    definitions.extend(objects);
+    definitions.extend(interfaces);
+    definitions.extend(unions);
+    definitions.extend(enums);
+    definitions.extend(input_objects);
+    definitions.push(schema);
+
+    Arc::new(definitions)
+}
+
+fn type_system_definitions(db: &dyn Definitions) -> Arc<Vec<Definition>> {
+    let mut definitions = Vec::new();
+
+    let directives: Vec<Definition> = db
+        .directive_definitions()
+        .iter()
+        .map(|def| Definition::DirectiveDefinition(def.clone()))
+        .collect();
+    let scalars: Vec<Definition> = db
+        .scalars()
+        .iter()
+        .map(|def| Definition::ScalarTypeDefinition(def.clone()))
+        .collect();
+    let objects: Vec<Definition> = db
+        .object_types()
+        .iter()
+        .map(|def| Definition::ObjectTypeDefinition(def.clone()))
+        .collect();
+    let interfaces: Vec<Definition> = db
+        .interfaces()
+        .iter()
+        .map(|def| Definition::InterfaceTypeDefinition(def.clone()))
+        .collect();
+    let unions: Vec<Definition> = db
+        .unions()
+        .iter()
+        .map(|def| Definition::UnionTypeDefinition(def.clone()))
+        .collect();
+    let enums: Vec<Definition> = db
+        .enums()
+        .iter()
+        .map(|def| Definition::EnumTypeDefinition(def.clone()))
+        .collect();
+    let input_objects: Vec<Definition> = db
+        .input_objects()
+        .iter()
+        .map(|def| Definition::InputObjectTypeDefinition(def.clone()))
+        .collect();
+    let schema = Definition::SchemaDefinition(db.schema().as_ref().clone());
+
+    definitions.extend(directives);
+    definitions.extend(scalars);
+    definitions.extend(objects);
+    definitions.extend(interfaces);
+    definitions.extend(unions);
+    definitions.extend(enums);
+    definitions.extend(input_objects);
+    definitions.push(schema);
+
+    Arc::new(definitions)
+}
+
+fn operations(db: &dyn Definitions) -> Arc<Vec<OperationDefinition>> {
+    let operations = db
+        .definitions()
+        .iter()
+        .filter_map(|definition| match definition {
+            ast::Definition::OperationDefinition(op_def) => {
+                Some(operation_definition(db, op_def.clone()))
+            }
+            _ => None,
+        })
+        .collect();
+    Arc::new(operations)
+}
+
+fn fragments(db: &dyn Definitions) -> Arc<Vec<FragmentDefinition>> {
+    let fragments = db
+        .definitions()
+        .iter()
+        .filter_map(|definition| match definition {
+            ast::Definition::FragmentDefinition(fragment_def) => {
+                Some(fragment_definition(db, fragment_def.clone()))
+            }
+            _ => None,
+        })
+        .collect();
+    Arc::new(fragments)
+}
+
+fn schema(db: &dyn Definitions) -> Arc<SchemaDefinition> {
+    let schema = db
+        .definitions()
+        .iter()
+        .find_map(|definition| match definition {
+            ast::Definition::SchemaDefinition(schema) => Some(schema.clone()),
+            _ => None,
+        });
+    let mut schema_def = schema.map_or(SchemaDefinition::default(), schema_definition);
+
+    // NOTE(@lrlna):
+    //
+    // "Query", "Subscription", "Mutation" object type definitions do not need
+    // to be explicitly defined in a schema definition, but are implicitly
+    // added.
+    //
+    // There will be a time when we need to distinguish between implicit and
+    // explicit definitions for validation purposes.
+    let type_defs = add_object_type_id_to_schema(db);
+    type_defs
+        .iter()
+        .for_each(|ty| schema_def.set_root_operation_type_definition(ty.clone()));
+
+    Arc::new(schema_def)
+}
+
+fn object_types(db: &dyn Definitions) -> Arc<Vec<ObjectTypeDefinition>> {
+    let objects = db
+        .definitions()
+        .iter()
+        .filter_map(|definition| match definition {
+            ast::Definition::ObjectTypeDefinition(obj_def) => {
+                Some(object_type_definition(obj_def.clone()))
+            }
+            _ => None,
+        })
+        .collect();
+    Arc::new(objects)
+}
+
+fn scalars(db: &dyn Definitions) -> Arc<Vec<ScalarTypeDefinition>> {
+    let scalars = db
+        .definitions()
+        .iter()
+        .filter_map(|definition| match definition {
+            ast::Definition::ScalarTypeDefinition(scalar_def) => {
+                Some(scalar_definition(scalar_def.clone()))
+            }
+            _ => None,
+        })
+        .collect();
+    let scalars = built_in_scalars(scalars);
+
+    Arc::new(scalars)
+}
+
+fn enums(db: &dyn Definitions) -> Arc<Vec<EnumTypeDefinition>> {
+    let enums = db
+        .definitions()
+        .iter()
+        .filter_map(|definition| match definition {
+            ast::Definition::EnumTypeDefinition(enum_def) => {
+                Some(enum_definition(enum_def.clone()))
+            }
+            _ => None,
+        })
+        .collect();
+    Arc::new(enums)
+}
+
+fn unions(db: &dyn Definitions) -> Arc<Vec<UnionTypeDefinition>> {
+    let unions = db
+        .definitions()
+        .iter()
+        .filter_map(|definition| match definition {
+            ast::Definition::UnionTypeDefinition(union_def) => {
+                Some(union_definition(union_def.clone()))
+            }
+            _ => None,
+        })
+        .collect();
+    Arc::new(unions)
+}
+
+fn interfaces(db: &dyn Definitions) -> Arc<Vec<InterfaceTypeDefinition>> {
+    let interfaces = db
+        .definitions()
+        .iter()
+        .filter_map(|definition| match definition {
+            ast::Definition::InterfaceTypeDefinition(interface_def) => {
+                Some(interface_definition(interface_def.clone()))
+            }
+            _ => None,
+        })
+        .collect();
+    Arc::new(interfaces)
+}
+
+fn directive_definitions(db: &dyn Definitions) -> Arc<Vec<DirectiveDefinition>> {
+    let directives = db
+        .definitions()
+        .iter()
+        .filter_map(|definition| match definition {
+            ast::Definition::DirectiveDefinition(directive_def) => {
+                Some(directive_definition(directive_def.clone()))
+            }
+            _ => None,
+        })
+        .collect();
+
+    let directives = built_in_directives(directives);
+
+    Arc::new(directives)
+}
+
+fn input_objects(db: &dyn Definitions) -> Arc<Vec<InputObjectTypeDefinition>> {
+    let directives = db
+        .definitions()
+        .iter()
+        .filter_map(|definition| match definition {
+            ast::Definition::InputObjectTypeDefinition(input_obj) => {
+                Some(input_object_definition(input_obj.clone()))
+            }
+            _ => None,
+        })
+        .collect();
+
+    Arc::new(directives)
+}
+
+fn operation_definition(
+    db: &dyn Definitions,
+    op_def: ast::OperationDefinition,
+) -> OperationDefinition {
+    // check if there are already operations
+    // if there are operations, they must have names
+    // if there are no names, an error must be raised that all operations must have a name
+    let name = op_def.name().map(|name| name.text().to_string());
+    let ty = operation_type(op_def.operation_type());
+    let variables = variable_definitions(op_def.variable_definitions());
+    let parent_object_ty = db
+        .schema()
+        .root_operation_type_definition()
+        .iter()
+        .find_map(|op| {
+            if op.operation_type() == ty {
+                Some(op.named_type().name())
+            } else {
+                None
+            }
+        });
+    let selection_set = selection_set(db, op_def.selection_set(), parent_object_ty);
+    let directives = directives(op_def.directives());
+    let ast_ptr = SyntaxNodePtr::new(op_def.syntax());
+
+    OperationDefinition {
+        id: Uuid::new_v4(),
+        operation_ty: ty,
+        name,
+        variables,
+        selection_set,
+        directives,
+        ast_ptr,
+    }
+}
+
+fn fragment_definition(
+    db: &dyn Definitions,
+    fragment_def: ast::FragmentDefinition,
+) -> FragmentDefinition {
+    let name = name(
+        fragment_def
+            .fragment_name()
+            .expect("Fragment Definition must have a name")
+            .name(),
+    );
+    let type_condition = fragment_def
+        .type_condition()
+        .expect("Fragment Definition must have a type condition")
+        .named_type()
+        .expect("Type Condition must have a name")
+        .name()
+        .expect("Name must have text")
+        .text()
+        .to_string();
+    let selection_set = selection_set(
+        db,
+        fragment_def.selection_set(),
+        Some(type_condition.clone()),
+    );
+    let directives = directives(fragment_def.directives());
+    let ast_ptr = SyntaxNodePtr::new(fragment_def.syntax());
+
+    FragmentDefinition {
+        id: Uuid::new_v4(),
+        name,
+        type_condition,
+        selection_set,
+        directives,
+        ast_ptr,
+    }
+}
+
+fn schema_definition(schema_def: ast::SchemaDefinition) -> SchemaDefinition {
+    let description = description(schema_def.description());
+    let directives = directives(schema_def.directives());
+    let root_operation_type_definition =
+        root_operation_type_definition(schema_def.root_operation_type_definitions());
+    let ast_ptr = SyntaxNodePtr::new(schema_def.syntax());
+
+    SchemaDefinition {
+        description,
+        directives,
+        root_operation_type_definition,
+        ast_ptr: Some(ast_ptr),
+    }
+}
+
+fn object_type_definition(obj_def: ast::ObjectTypeDefinition) -> ObjectTypeDefinition {
+    let id = Uuid::new_v4();
+    let description = description(obj_def.description());
+    let name = name(obj_def.name());
+    let implements_interfaces = implements_interfaces(obj_def.implements_interfaces());
+    let directives = directives(obj_def.directives());
+    let fields_definition = fields_definition(obj_def.fields_definition());
+    let ast_ptr = SyntaxNodePtr::new(obj_def.syntax());
+
+    ObjectTypeDefinition {
+        id,
+        description,
+        name,
+        implements_interfaces,
+        directives,
+        fields_definition,
+        ast_ptr,
+    }
+}
+
+fn scalar_definition(scalar_def: ast::ScalarTypeDefinition) -> ScalarTypeDefinition {
+    let id = Uuid::new_v4();
+    let description = description(scalar_def.description());
+    let name = name(scalar_def.name());
+    let directives = directives(scalar_def.directives());
+    let ast_ptr = SyntaxNodePtr::new(scalar_def.syntax());
+
+    ScalarTypeDefinition {
+        id,
+        description,
+        name,
+        directives,
+        ast_ptr: Some(ast_ptr),
+        built_in: false,
+    }
+}
+
+fn enum_definition(enum_def: ast::EnumTypeDefinition) -> EnumTypeDefinition {
+    let id = Uuid::new_v4();
+    let description = description(enum_def.description());
+    let name = name(enum_def.name());
+    let directives = directives(enum_def.directives());
+    let enum_values_definition = enum_values_definition(enum_def.enum_values_definition());
+    let ast_ptr = SyntaxNodePtr::new(enum_def.syntax());
+
+    EnumTypeDefinition {
+        id,
+        description,
+        name,
+        directives,
+        enum_values_definition,
+        ast_ptr,
+    }
+}
+
+fn enum_values_definition(
+    enum_values_def: Option<ast::EnumValuesDefinition>,
+) -> Arc<Vec<EnumValueDefinition>> {
+    match enum_values_def {
+        Some(enum_values) => {
+            let enum_values = enum_values
+                .enum_value_definitions()
+                .into_iter()
+                .map(enum_value_definition)
+                .collect();
+            Arc::new(enum_values)
+        }
+        None => Arc::new(Vec::new()),
+    }
+}
+
+fn enum_value_definition(enum_value_def: ast::EnumValueDefinition) -> EnumValueDefinition {
+    let description = description(enum_value_def.description());
+    let enum_value = enum_value(enum_value_def.enum_value());
+    let directives = directives(enum_value_def.directives());
+    let ast_ptr = SyntaxNodePtr::new(enum_value_def.syntax());
+
+    EnumValueDefinition {
+        description,
+        enum_value,
+        directives,
+        ast_ptr,
+    }
+}
+
+fn union_definition(union_def: ast::UnionTypeDefinition) -> UnionTypeDefinition {
+    let id = Uuid::new_v4();
+    let description = description(union_def.description());
+    let name = name(union_def.name());
+    let directives = directives(union_def.directives());
+    let union_members = union_members(union_def.union_member_types());
+    let ast_ptr = SyntaxNodePtr::new(union_def.syntax());
+
+    UnionTypeDefinition {
+        id,
+        description,
+        name,
+        directives,
+        union_members,
+        ast_ptr,
+    }
+}
+
+fn union_members(union_members: Option<ast::UnionMemberTypes>) -> Arc<Vec<UnionMember>> {
+    match union_members {
+        Some(members) => {
+            let mems = members
+                .named_types()
+                .into_iter()
+                .map(union_member)
+                .collect();
+            Arc::new(mems)
+        }
+        None => Arc::new(Vec::new()),
+    }
+}
+
+fn union_member(member: ast::NamedType) -> UnionMember {
+    let name = name(member.name());
+    let ast_ptr = SyntaxNodePtr::new(member.syntax());
+
+    UnionMember { name, ast_ptr }
+}
+
+fn interface_definition(interface_def: ast::InterfaceTypeDefinition) -> InterfaceTypeDefinition {
+    let id = Uuid::new_v4();
+    let description = description(interface_def.description());
+    let name = name(interface_def.name());
+    let implements_interfaces = implements_interfaces(interface_def.implements_interfaces());
+    let directives = directives(interface_def.directives());
+    let fields_definition = fields_definition(interface_def.fields_definition());
+    let ast_ptr = SyntaxNodePtr::new(interface_def.syntax());
+
+    InterfaceTypeDefinition {
+        id,
+        description,
+        name,
+        implements_interfaces,
+        directives,
+        fields_definition,
+        ast_ptr,
+    }
+}
+
+fn directive_definition(directive_def: ast::DirectiveDefinition) -> DirectiveDefinition {
+    let name = name(directive_def.name());
+    let description = description(directive_def.description());
+    let arguments = arguments_definition(directive_def.arguments_definition());
+    let repeatable = directive_def.repeatable_token().is_some();
+    let directive_locations = directive_locations(directive_def.directive_locations());
+    let ast_ptr = SyntaxNodePtr::new(directive_def.syntax());
+
+    DirectiveDefinition {
+        id: Uuid::new_v4(),
+        description,
+        name,
+        arguments,
+        repeatable,
+        directive_locations,
+        ast_ptr: Some(ast_ptr),
+    }
+}
+
+fn input_object_definition(input_obj: ast::InputObjectTypeDefinition) -> InputObjectTypeDefinition {
+    let id = Uuid::new_v4();
+    let description = description(input_obj.description());
+    let name = name(input_obj.name());
+    let directives = directives(input_obj.directives());
+    let input_fields_definition = input_fields_definition(input_obj.input_fields_definition());
+    let ast_ptr = SyntaxNodePtr::new(input_obj.syntax());
+
+    InputObjectTypeDefinition {
+        id,
+        description,
+        name,
+        directives,
+        input_fields_definition,
+        ast_ptr,
+    }
+}
+
+fn add_object_type_id_to_schema(db: &dyn Definitions) -> Arc<Vec<RootOperationTypeDefinition>> {
+    // Schema Definition does not have to be present in the SDL if ObjectType name is
+    // - Query
+    // - Subscription
+    // - Mutation
+    //
+    // Compiler's internal schema, however, should have a reference to these
+    // object types if they are present
+    let type_defs: Vec<RootOperationTypeDefinition> = db
+        .object_types()
+        .iter()
+        .filter_map(|obj_type| {
+            let obj_name = obj_type.name();
+            if matches!(obj_name, "Query" | "Subscription" | "Mutation") {
+                let operation_type = obj_name.into();
+                Some(RootOperationTypeDefinition {
+                    operation_type,
+                    named_type: Type::Named {
+                        name: obj_name.to_string(),
+                        ast_ptr: None,
+                    },
+                    ast_ptr: None,
+                })
+            } else {
+                None
+            }
+        })
+        .collect();
+
+    Arc::new(type_defs)
+}
+
+fn implements_interfaces(
+    implements_interfaces: Option<ast::ImplementsInterfaces>,
+) -> Arc<Vec<ImplementsInterface>> {
+    let interfaces: Vec<ImplementsInterface> = implements_interfaces
+        .iter()
+        .flat_map(|interfaces| {
+            let types: Vec<ImplementsInterface> = interfaces
+                .named_types()
+                .map(|n| ImplementsInterface {
+                    interface: n.name().expect("Name must have text").text().to_string(),
+                    ast_ptr: SyntaxNodePtr::new(n.syntax()),
+                })
+                .collect();
+            types
+        })
+        .collect();
+
+    Arc::new(interfaces)
+}
+
+fn fields_definition(
+    fields_definition: Option<ast::FieldsDefinition>,
+) -> Arc<Vec<FieldDefinition>> {
+    match fields_definition {
+        Some(fields_def) => {
+            let fields: Vec<FieldDefinition> = fields_def
+                .field_definitions()
+                .map(field_definition)
+                .collect();
+            Arc::new(fields)
+        }
+        None => Arc::new(Vec::new()),
+    }
+}
+
+fn field_definition(field: ast::FieldDefinition) -> FieldDefinition {
+    let description = description(field.description());
+    let name = name(field.name());
+    let arguments = arguments_definition(field.arguments_definition());
+    let ty = ty(field.ty().expect("Field must have a type"));
+    let directives = directives(field.directives());
+    let ast_ptr = SyntaxNodePtr::new(field.syntax());
+
+    FieldDefinition {
+        description,
+        name,
+        arguments,
+        ty,
+        directives,
+        ast_ptr,
+    }
+}
+
+fn arguments_definition(
+    arguments_definition: Option<ast::ArgumentsDefinition>,
+) -> ArgumentsDefinition {
+    match arguments_definition {
+        Some(arguments) => {
+            let input_values = input_value_definitions(arguments.input_value_definitions());
+            let ast_ptr = SyntaxNodePtr::new(arguments.syntax());
+
+            ArgumentsDefinition {
+                input_values,
+                ast_ptr: Some(ast_ptr),
+            }
+        }
+        None => ArgumentsDefinition {
+            input_values: Arc::new(Vec::new()),
+            ast_ptr: None,
+        },
+    }
+}
+
+fn input_fields_definition(
+    input_fields: Option<ast::InputFieldsDefinition>,
+) -> Arc<Vec<InputValueDefinition>> {
+    match input_fields {
+        Some(fields) => input_value_definitions(fields.input_value_definitions()),
+        None => Arc::new(Vec::new()),
+    }
+}
+
+fn input_value_definitions(
+    input_values: AstChildren<ast::InputValueDefinition>,
+) -> Arc<Vec<InputValueDefinition>> {
+    let input_values: Vec<InputValueDefinition> = input_values
+        .map(|input| {
+            let description = description(input.description());
+            let name = name(input.name());
+            let ty = ty(input.ty().expect("Input Definition must have a type"));
+            let default_value = default_value(input.default_value());
+            let directives = directives(input.directives());
+            let ast_ptr = SyntaxNodePtr::new(input.syntax());
+
+            InputValueDefinition {
+                description,
+                name,
+                ty,
+                default_value,
+                directives,
+                ast_ptr: Some(ast_ptr),
+            }
+        })
+        .collect();
+    Arc::new(input_values)
+}
+
+fn default_value(default_value: Option<ast::DefaultValue>) -> Option<DefaultValue> {
+    default_value.map(|val| value(val.value().expect("Default Value must have a value token")))
+}
+
+fn root_operation_type_definition(
+    root_type_def: AstChildren<ast::RootOperationTypeDefinition>,
+) -> Arc<Vec<RootOperationTypeDefinition>> {
+    let type_defs: Vec<RootOperationTypeDefinition> = root_type_def
+        .into_iter()
+        .map(|ty| {
+            let operation_type = operation_type(ty.operation_type());
+            let named_type = named_type(
+                ty.named_type()
+                    .expect("Root Operation Type Definition must have Named Type.")
+                    .name(),
+            );
+            let ast_ptr = SyntaxNodePtr::new(ty.syntax());
+
+            RootOperationTypeDefinition {
+                operation_type,
+                named_type,
+                ast_ptr: Some(ast_ptr),
+            }
+        })
+        .collect();
+
+    Arc::new(type_defs)
+}
+
+fn operation_type(op_type: Option<ast::OperationType>) -> OperationType {
+    match op_type {
+        Some(ty) => {
+            if ty.query_token().is_some() {
+                OperationType::Query
+            } else if ty.mutation_token().is_some() {
+                OperationType::Mutation
+            } else if ty.subscription_token().is_some() {
+                OperationType::Subscription
+            } else {
+                OperationType::Query
+            }
+        }
+        None => OperationType::Query,
+    }
+}
+
+fn variable_definitions(
+    variable_definitions: Option<ast::VariableDefinitions>,
+) -> Arc<Vec<VariableDefinition>> {
+    match variable_definitions {
+        Some(vars) => {
+            let variable_definitions = vars
+                .variable_definitions()
+                .into_iter()
+                .map(variable_definition)
+                .collect();
+            Arc::new(variable_definitions)
+        }
+        None => Arc::new(Vec::new()),
+    }
+}
+
+fn variable_definition(var: ast::VariableDefinition) -> VariableDefinition {
+    let name = name(
+        var.variable()
+            .expect("values::Variable Definition must have a variable")
+            .name(),
+    );
+    let directives = directives(var.directives());
+    let default_value = default_value(var.default_value());
+    let ty = ty(var
+        .ty()
+        .expect("values::Variable Definition must have a type"));
+    let ast_ptr = SyntaxNodePtr::new(var.syntax());
+
+    VariableDefinition {
+        name,
+        directives,
+        ty,
+        default_value,
+        ast_ptr,
+    }
+}
+
+fn ty(ty_: ast::Type) -> Type {
+    match ty_ {
+        ast::Type::NamedType(name) => named_type(name.name()),
+        ast::Type::ListType(list) => Type::List {
+            ty: Box::new(ty(list.ty().expect("List Type must have a type"))),
+            ast_ptr: Some(SyntaxNodePtr::new(list.syntax())),
+        },
+        ast::Type::NonNullType(non_null) => {
+            if let Some(n) = non_null.named_type() {
+                let named_type = named_type(n.name());
+                Type::NonNull {
+                    ty: Box::new(named_type),
+                    ast_ptr: Some(SyntaxNodePtr::new(n.syntax())),
+                }
+            } else if let Some(list) = non_null.list_type() {
+                let list_type = Type::List {
+                    ty: Box::new(ty(list.ty().expect("List Type must have a type"))),
+                    ast_ptr: Some(SyntaxNodePtr::new(list.syntax())),
+                };
+                Type::NonNull {
+                    ty: Box::new(list_type),
+                    ast_ptr: Some(SyntaxNodePtr::new(list.syntax())),
+                }
+            } else {
+                // TODO: parser should have caught an error if there wasn't
+                // either a named type or list type. Figure out a graceful way
+                // to surface this error from the parser.
+                panic!("Parser should have caught this error");
+            }
+        }
+    }
+}
+
+fn named_type(n: Option<ast::Name>) -> Type {
+    let name = n.expect("Named Type must have a name");
+
+    Type::Named {
+        name: name.text().to_string(),
+        ast_ptr: Some(SyntaxNodePtr::new(name.syntax())),
+    }
+}
+
+fn directive_locations(
+    directive_locations: Option<ast::DirectiveLocations>,
+) -> Arc<Vec<DirectiveLocation>> {
+    match directive_locations {
+        Some(directive_loc) => {
+            let locations: Vec<DirectiveLocation> = directive_loc
+                .directive_locations()
+                .into_iter()
+                .map(|loc| loc.into())
+                .collect();
+            Arc::new(locations)
+        }
+        None => Arc::new(Vec::new()),
+    }
+}
+
+fn directives(directives: Option<ast::Directives>) -> Arc<Vec<Directive>> {
+    match directives {
+        Some(directives) => {
+            let directives = directives.directives().into_iter().map(directive).collect();
+            Arc::new(directives)
+        }
+        None => Arc::new(Vec::new()),
+    }
+}
+
+fn directive(directive: ast::Directive) -> Directive {
+    let name = name(directive.name());
+    let arguments = arguments(directive.arguments());
+    let ast_ptr = SyntaxNodePtr::new(directive.syntax());
+
+    Directive {
+        name,
+        arguments,
+        ast_ptr,
+    }
+}
+
+fn arguments(arguments: Option<ast::Arguments>) -> Arc<Vec<Argument>> {
+    match arguments {
+        Some(arguments) => {
+            let arguments = arguments.arguments().into_iter().map(argument).collect();
+            Arc::new(arguments)
+        }
+        None => Arc::new(Vec::new()),
+    }
+}
+
+fn argument(argument: ast::Argument) -> Argument {
+    let name = name(argument.name());
+    let value = value(argument.value().expect("Argument must have a value"));
+    let ast_ptr = SyntaxNodePtr::new(argument.syntax());
+
+    Argument {
+        name,
+        value,
+        ast_ptr,
+    }
+}
+
+fn value(val: ast::Value) -> Value {
+    match val {
+        ast::Value::Variable(var) => Value::Variable(Variable {
+            name: name(var.name()),
+            ast_ptr: SyntaxNodePtr::new(var.syntax()),
+        }),
+        ast::Value::StringValue(string_val) => Value::String(string_val.into()),
+        ast::Value::FloatValue(float) => Value::Float(Float::new(float.into())),
+        ast::Value::IntValue(int) => Value::Int(int.into()),
+        ast::Value::BooleanValue(bool) => Value::Boolean(bool.into()),
+        ast::Value::NullValue(_) => Value::Null,
+        ast::Value::EnumValue(enum_) => Value::Enum(name(enum_.name())),
+        ast::Value::ListValue(list) => {
+            let list: Vec<Value> = list.values().map(value).collect();
+            Value::List(list)
+        }
+        ast::Value::ObjectValue(object) => {
+            let object_values: Vec<(String, Value)> = object
+                .object_fields()
+                .map(|o| {
+                    let name = name(o.name());
+                    let value = value(o.value().expect("Object Value must have a value"));
+                    (name, value)
+                })
+                .collect();
+            Value::Object(object_values)
+        }
+    }
+}
+
+fn selection_set(
+    db: &dyn Definitions,
+    selections: Option<ast::SelectionSet>,
+    parent_obj_ty: Option<String>,
+) -> SelectionSet {
+    let selection_set = match selections {
+        Some(sel) => sel
+            .selections()
+            .into_iter()
+            .map(|sel| selection(db, sel, parent_obj_ty.as_ref().cloned()))
+            .collect(),
+        None => Vec::new(),
+    };
+
+    SelectionSet {
+        selection: Arc::new(selection_set),
+    }
+}
+
+fn selection(
+    db: &dyn Definitions,
+    selection: ast::Selection,
+    parent_obj_ty: Option<String>,
+) -> Selection {
+    match selection {
+        ast::Selection::Field(sel_field) => {
+            let field = field(db, sel_field, parent_obj_ty);
+            Selection::Field(field)
+        }
+        ast::Selection::FragmentSpread(fragment) => {
+            let fragment_spread = fragment_spread(fragment);
+            Selection::FragmentSpread(fragment_spread)
+        }
+        ast::Selection::InlineFragment(fragment) => {
+            let inline_fragment = inline_fragment(db, fragment, parent_obj_ty);
+            Selection::InlineFragment(inline_fragment)
+        }
+    }
+}
+
+fn inline_fragment(
+    db: &dyn Definitions,
+    fragment: ast::InlineFragment,
+    parent_obj: Option<String>,
+) -> Arc<InlineFragment> {
+    let type_condition = fragment.type_condition().map(|tc| {
+        tc.named_type()
+            .expect("Type Condition must have a name")
+            .name()
+            .expect("Name must have text")
+            .text()
+            .to_string()
+    });
+    let directives = directives(fragment.directives());
+    let new_parent_obj = if let Some(type_condition) = type_condition.clone() {
+        Some(type_condition)
+    } else {
+        parent_obj
+    };
+    let selection_set: SelectionSet = selection_set(db, fragment.selection_set(), new_parent_obj);
+    let ast_ptr = SyntaxNodePtr::new(fragment.syntax());
+
+    let fragment_data = InlineFragment {
+        type_condition,
+        directives,
+        selection_set,
+        ast_ptr,
+    };
+    Arc::new(fragment_data)
+}
+
+fn fragment_spread(fragment: ast::FragmentSpread) -> Arc<FragmentSpread> {
+    let name = name(
+        fragment
+            .fragment_name()
+            .expect("Fragment Spread must have a name")
+            .name(),
+    );
+    let directives = directives(fragment.directives());
+    let ast_ptr = SyntaxNodePtr::new(fragment.syntax());
+
+    let fragment_data = FragmentSpread {
+        name,
+        directives,
+        ast_ptr,
+    };
+    Arc::new(fragment_data)
+}
+
+fn field(db: &dyn Definitions, field: ast::Field, parent_obj: Option<String>) -> Arc<Field> {
+    let name = name(field.name());
+    let alias = alias(field.alias());
+    let new_parent_obj = parent_ty(db, &name, parent_obj.clone());
+    let selection_set = selection_set(db, field.selection_set(), new_parent_obj);
+    let directives = directives(field.directives());
+    let arguments = arguments(field.arguments());
+    let ast_ptr = SyntaxNodePtr::new(field.syntax());
+
+    let field_data = Field {
+        name,
+        alias,
+        selection_set,
+        parent_obj,
+        directives,
+        arguments,
+        ast_ptr,
+    };
+    Arc::new(field_data)
+}
+
+fn parent_ty(db: &dyn Definitions, field_name: &str, parent_obj: Option<String>) -> Option<String> {
+    if let Some(name) = parent_obj {
+        db.type_system_definitions().iter().find_map(|def| {
+            if let Some(n) = def.name() {
+                if name == n {
+                    return Some(def.field(field_name)?.ty().name());
+                }
+            }
+            None
+        })
+    } else {
+        None
+    }
+}
+
+fn name(name: Option<ast::Name>) -> String {
+    name.expect("Field must have a name").text().to_string()
+}
+
+fn enum_value(enum_value: Option<ast::EnumValue>) -> String {
+    enum_value
+        .expect("Enum value must have a name")
+        .name()
+        .expect("Name must have text")
+        .text()
+        .to_string()
+}
+
+fn description(description: Option<ast::Description>) -> Option<String> {
+    description.map(|desc| {
+        desc.string_value()
+            .expect("Description must have text")
+            .into()
+    })
+}
+
+fn alias(alias: Option<ast::Alias>) -> Option<Arc<Alias>> {
+    alias.map(|alias| {
+        let name = alias.name().expect("Alias must have a name").to_string();
+        let alias_data = Alias(name);
+        Arc::new(alias_data)
+    })
+}
+
+//  Int, Float, String, Boolean, and ID
+fn built_in_scalars(mut scalars: Vec<ScalarTypeDefinition>) -> Vec<ScalarTypeDefinition> {
+    scalars.push(int_scalar());
+    scalars.push(float_scalar());
+    scalars.push(string_scalar());
+    scalars.push(boolean_scalar());
+    scalars.push(id_scalar());
+
+    scalars
+}
+
+fn int_scalar() -> ScalarTypeDefinition {
+    ScalarTypeDefinition {
+        id: Uuid::new_v4(),
+        description: Some("The `Int` scalar type represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1.".into()),
+        name: "Int".into(),
+        directives: Arc::new(Vec::new()),
+        ast_ptr: None,
+        built_in: true
+    }
+}
+
+fn float_scalar() -> ScalarTypeDefinition {
+    ScalarTypeDefinition {
+        id: Uuid::new_v4(),
+        description: Some("The `Float` scalar type represents signed double-precision fractional values as specified by [IEEE 754](https://en.wikipedia.org/wiki/IEEE_floating_point).".into()),
+        name: "Float".into(),
+        directives: Arc::new(Vec::new()),
+        ast_ptr: None,
+        built_in: true
+    }
+}
+
+fn string_scalar() -> ScalarTypeDefinition {
+    ScalarTypeDefinition {
+        id: Uuid::new_v4(),
+        description: Some("The `String` scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text.".into()),
+        name: "String".into(),
+        directives: Arc::new(Vec::new()),
+        ast_ptr: None,
+        built_in: true
+    }
+}
+
+fn boolean_scalar() -> ScalarTypeDefinition {
+    ScalarTypeDefinition {
+        id: Uuid::new_v4(),
+        description: Some("The `Boolean` scalar type represents `true` or `false`.".into()),
+        name: "Boolean".into(),
+        directives: Arc::new(Vec::new()),
+        ast_ptr: None,
+        built_in: true,
+    }
+}
+
+fn id_scalar() -> ScalarTypeDefinition {
+    ScalarTypeDefinition {
+        id: Uuid::new_v4(),
+        description: Some("The `ID` scalar type represents a unique identifier, often used to refetch an object or as key for a cache. The ID type appears in a JSON response as a String; however, it is not intended to be human-readable. When expected as an input type, any string (such as `\"4\"`) or integer (such as `4`) input value will be accepted as an ID.".into()),
+        name: "ID".into(),
+        directives: Arc::new(Vec::new()),
+        ast_ptr: None,
+        built_in: true
+    }
+}
+
+fn built_in_directives(mut directives: Vec<DirectiveDefinition>) -> Vec<DirectiveDefinition> {
+    if !directives.iter().any(|dir| dir.name == "skip") {
+        directives.push(skip_directive());
+    }
+
+    if !directives.iter().any(|dir| dir.name == "specifiedBy") {
+        directives.push(specified_by_directive());
+    }
+
+    if !directives.iter().any(|dir| dir.name == "deprecated") {
+        directives.push(deprecated_directive());
+    }
+
+    if !directives.iter().any(|dir| dir.name == "include") {
+        directives.push(include_directive());
+    }
+
+    directives
+}
+
+fn skip_directive() -> DirectiveDefinition {
+    // "Directs the executor to skip this field or fragment when the `if` argument is true."
+    // directive @skip(
+    //   "Skipped when true."
+    //   if: Boolean!
+    // ) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT
+    DirectiveDefinition {
+        id: Uuid::new_v4(),
+        description: Some(
+            "Directs the executor to skip this field or fragment when the `if` argument is true."
+                .into(),
+        ),
+        name: "skip".into(),
+        arguments: ArgumentsDefinition {
+            input_values: Arc::new(vec![InputValueDefinition {
+                description: Some("Skipped when true.".into()),
+                name: "if".into(),
+                ty: Type::NonNull {
+                    ty: Box::new(Type::Named {
+                        name: "Boolean".into(),
+                        ast_ptr: None,
+                    }),
+                    ast_ptr: None,
+                },
+                default_value: None,
+                directives: Arc::new(Vec::new()),
+                ast_ptr: None,
+            }]),
+            ast_ptr: None,
+        },
+        repeatable: false,
+        directive_locations: Arc::new(vec![
+            DirectiveLocation::Field,
+            DirectiveLocation::FragmentSpread,
+            DirectiveLocation::InlineFragment,
+        ]),
+        ast_ptr: None,
+    }
+}
+
+fn specified_by_directive() -> DirectiveDefinition {
+    // "Exposes a URL that specifies the behaviour of this scalar."
+    // directive @specifiedBy(
+    //     "The URL that specifies the behaviour of this scalar."
+    //     url: String!
+    // ) on SCALAR
+    DirectiveDefinition {
+        id: Uuid::new_v4(),
+        description: Some("Exposes a URL that specifies the behaviour of this scalar.".into()),
+        name: "specifiedBy".into(),
+        arguments: ArgumentsDefinition {
+            input_values: Arc::new(vec![InputValueDefinition {
+                description: Some("The URL that specifies the behaviour of this scalar.".into()),
+                name: "url".into(),
+                ty: Type::NonNull {
+                    ty: Box::new(Type::Named {
+                        name: "String".into(),
+                        ast_ptr: None,
+                    }),
+                    ast_ptr: None,
+                },
+                default_value: None,
+                directives: Arc::new(Vec::new()),
+                ast_ptr: None,
+            }]),
+            ast_ptr: None,
+        },
+        repeatable: false,
+        directive_locations: Arc::new(vec![DirectiveLocation::Scalar]),
+        ast_ptr: None,
+    }
+}
+
+fn deprecated_directive() -> DirectiveDefinition {
+    // "Marks an element of a GraphQL schema as no longer supported."
+    // directive @deprecated(
+    //   """
+    //   Explains why this element was deprecated, usually also including a
+    //   suggestion for how to access supported similar data. Formatted using
+    //   the Markdown syntax, as specified by
+    //   [CommonMark](https://commonmark.org/).
+    //   """
+    //   reason: String = "No longer supported"
+    // ) on FIELD_DEFINITION | ENUM_VALUE
+    DirectiveDefinition {
+        id: Uuid::new_v4(),
+        description: Some("Marks an element of a GraphQL schema as no longer supported.".into()),
+        name: "deprecated".into(),
+        arguments: ArgumentsDefinition {
+            input_values: Arc::new(vec![InputValueDefinition {
+                description: Some(
+                    "Explains why this element was deprecated, usually also including a suggestion for how to access supported similar data. Formatted using the Markdown syntax, as specified by [CommonMark](https://commonmark.org/).".into(),
+                ),
+                name: "reason".into(),
+                ty: Type::Named {
+                    name: "String".into(),
+                    ast_ptr: None,
+                },
+                default_value: Some(DefaultValue::String("No longer supported".into())),
+                directives: Arc::new(Vec::new()),
+                ast_ptr: None
+            }]),
+            ast_ptr: None
+        },
+        repeatable: false,
+        directive_locations: Arc::new(vec![
+            DirectiveLocation::FieldDefinition,
+            DirectiveLocation::EnumValue
+        ]),
+        ast_ptr: None
+    }
+}
+
+fn include_directive() -> DirectiveDefinition {
+    // "Directs the executor to include this field or fragment only when the `if` argument is true."
+    // directive @include(
+    //   "Included when true."
+    //   if: Boolean!
+    // ) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT
+    DirectiveDefinition {
+        id: Uuid::new_v4(),
+        description: Some("Directs the executor to include this field or fragment only when the `if` argument is true.".into()),
+        name: "include".into(),
+        arguments: ArgumentsDefinition {
+            input_values: Arc::new(vec![InputValueDefinition {
+                description: Some(
+                    "Included when true.".into(),
+                ),
+                name: "if".into(),
+                ty: Type::NonNull {
+                    ty: Box::new(Type::Named {
+                        name: "Boolean".into(),
+                        ast_ptr: None,
+                    }),
+                    ast_ptr: None,
+                },
+                default_value: None,
+                directives: Arc::new(Vec::new()),
+                ast_ptr: None
+            }]),
+            ast_ptr: None
+        },
+        repeatable: false,
+        directive_locations: Arc::new(vec![
+            DirectiveLocation::Field,
+            DirectiveLocation::FragmentDefinition,
+            DirectiveLocation::InlineFragment,
+        ]),
+        ast_ptr: None
+    }
+}
diff --git a/crates/apollo-compiler/src/queries/document_db.rs b/crates/apollo-compiler/src/queries/document_db.rs
new file mode 100644
index 0000000..7b3a096
--- /dev/null
+++ b/crates/apollo-compiler/src/queries/document_db.rs
@@ -0,0 +1,347 @@
+use std::{collections::HashSet, sync::Arc};
+
+use uuid::Uuid;
+
+use crate::{values::*, Definitions, DocumentParser, Inputs};
+
+#[salsa::query_group(DocumentStorage)]
+pub trait Document: Inputs + DocumentParser + Definitions {
+    fn find_operation(&self, id: Uuid) -> Option<Arc<OperationDefinition>>;
+
+    fn find_operation_by_name(&self, name: String) -> Option<Arc<OperationDefinition>>;
+
+    fn find_fragment(&self, id: Uuid) -> Option<Arc<FragmentDefinition>>;
+
+    fn find_fragment_by_name(&self, name: String) -> Option<Arc<FragmentDefinition>>;
+
+    fn find_object_type(&self, id: Uuid) -> Option<Arc<ObjectTypeDefinition>>;
+
+    fn find_object_type_by_name(&self, name: String) -> Option<Arc<ObjectTypeDefinition>>;
+
+    fn find_union_by_name(&self, name: String) -> Option<Arc<UnionTypeDefinition>>;
+
+    fn find_interface(&self, id: Uuid) -> Option<Arc<InterfaceTypeDefinition>>;
+
+    fn find_interface_by_name(&self, name: String) -> Option<Arc<InterfaceTypeDefinition>>;
+
+    fn find_directive_definition(&self, id: Uuid) -> Option<Arc<DirectiveDefinition>>;
+
+    fn find_directive_definition_by_name(&self, name: String) -> Option<Arc<DirectiveDefinition>>;
+
+    fn find_input_object(&self, id: Uuid) -> Option<Arc<InputObjectTypeDefinition>>;
+
+    fn find_input_object_by_name(&self, name: String) -> Option<Arc<InputObjectTypeDefinition>>;
+
+    fn find_definition_by_name(&self, name: String) -> Option<Arc<Definition>>;
+
+    fn find_type_system_definition(&self, id: Uuid) -> Option<Arc<Definition>>;
+
+    fn find_type_system_definition_by_name(&self, name: String) -> Option<Arc<Definition>>;
+
+    fn query_operations(&self) -> Arc<Vec<OperationDefinition>>;
+
+    fn mutation_operations(&self) -> Arc<Vec<OperationDefinition>>;
+
+    fn subscription_operations(&self) -> Arc<Vec<OperationDefinition>>;
+
+    fn operation_fields(&self, id: Uuid) -> Arc<Vec<Field>>;
+
+    fn operation_inline_fragment_fields(&self, id: Uuid) -> Arc<Vec<Field>>;
+
+    fn operation_fragment_spread_fields(&self, id: Uuid) -> Arc<Vec<Field>>;
+
+    fn selection_variables(&self, id: Uuid) -> Arc<HashSet<Variable>>;
+
+    fn operation_definition_variables(&self, id: Uuid) -> Arc<HashSet<Variable>>;
+}
+
+fn find_definition_by_name(db: &dyn Document, name: String) -> Option<Arc<Definition>> {
+    db.db_definitions().iter().find_map(|def| {
+        if let Some(n) = def.name() {
+            if name == n {
+                return Some(Arc::new(def.clone()));
+            }
+        }
+        None
+    })
+}
+
+fn find_type_system_definition(db: &dyn Document, id: Uuid) -> Option<Arc<Definition>> {
+    db.type_system_definitions().iter().find_map(|op| {
+        if let Some(op_id) = op.id() {
+            if op_id == &id {
+                return Some(Arc::new(op.clone()));
+            }
+        }
+        None
+    })
+}
+
+fn find_type_system_definition_by_name(db: &dyn Document, name: String) -> Option<Arc<Definition>> {
+    db.type_system_definitions().iter().find_map(|def| {
+        if let Some(n) = def.name() {
+            if name == n {
+                return Some(Arc::new(def.clone()));
+            }
+        }
+        None
+    })
+}
+
+fn find_operation(db: &dyn Document, id: Uuid) -> Option<Arc<OperationDefinition>> {
+    db.operations().iter().find_map(|op| {
+        if &id == op.id() {
+            return Some(Arc::new(op.clone()));
+        }
+        None
+    })
+}
+
+fn find_operation_by_name(db: &dyn Document, name: String) -> Option<Arc<OperationDefinition>> {
+    db.operations().iter().find_map(|op| {
+        if let Some(n) = op.name() {
+            if n == name {
+                return Some(Arc::new(op.clone()));
+            }
+        }
+        None
+    })
+}
+
+fn find_fragment(db: &dyn Document, id: Uuid) -> Option<Arc<FragmentDefinition>> {
+    db.fragments().iter().find_map(|fragment| {
+        if &id == fragment.id() {
+            return Some(Arc::new(fragment.clone()));
+        }
+        None
+    })
+}
+
+fn find_fragment_by_name(db: &dyn Document, name: String) -> Option<Arc<FragmentDefinition>> {
+    db.fragments().iter().find_map(|fragment| {
+        if name == fragment.name() {
+            return Some(Arc::new(fragment.clone()));
+        }
+        None
+    })
+}
+
+fn find_object_type(db: &dyn Document, id: Uuid) -> Option<Arc<ObjectTypeDefinition>> {
+    db.object_types().iter().find_map(|object_type| {
+        if &id == object_type.id() {
+            return Some(Arc::new(object_type.clone()));
+        }
+        None
+    })
+}
+
+fn find_object_type_by_name(db: &dyn Document, name: String) -> Option<Arc<ObjectTypeDefinition>> {
+    db.object_types().iter().find_map(|object_type| {
+        if name == object_type.name() {
+            return Some(Arc::new(object_type.clone()));
+        }
+        None
+    })
+}
+
+fn find_union_by_name(db: &dyn Document, name: String) -> Option<Arc<UnionTypeDefinition>> {
+    db.unions().iter().find_map(|union| {
+        if name == union.name() {
+            return Some(Arc::new(union.clone()));
+        }
+        None
+    })
+}
+
+fn find_interface(db: &dyn Document, id: Uuid) -> Option<Arc<InterfaceTypeDefinition>> {
+    db.interfaces().iter().find_map(|interface| {
+        if &id == interface.id() {
+            return Some(Arc::new(interface.clone()));
+        }
+        None
+    })
+}
+
+fn find_interface_by_name(db: &dyn Document, name: String) -> Option<Arc<InterfaceTypeDefinition>> {
+    db.interfaces().iter().find_map(|interface| {
+        if name == interface.name() {
+            return Some(Arc::new(interface.clone()));
+        }
+        None
+    })
+}
+
+fn find_directive_definition(db: &dyn Document, id: Uuid) -> Option<Arc<DirectiveDefinition>> {
+    db.directive_definitions().iter().find_map(|directive_def| {
+        if &id == directive_def.id() {
+            return Some(Arc::new(directive_def.clone()));
+        }
+        None
+    })
+}
+
+fn find_directive_definition_by_name(
+    db: &dyn Document,
+    name: String,
+) -> Option<Arc<DirectiveDefinition>> {
+    db.directive_definitions().iter().find_map(|directive_def| {
+        if name == directive_def.name() {
+            return Some(Arc::new(directive_def.clone()));
+        }
+        None
+    })
+}
+
+fn find_input_object(db: &dyn Document, id: Uuid) -> Option<Arc<InputObjectTypeDefinition>> {
+    db.input_objects().iter().find_map(|input_obj| {
+        if &id == input_obj.id() {
+            return Some(Arc::new(input_obj.clone()));
+        }
+        None
+    })
+}
+
+fn find_input_object_by_name(
+    db: &dyn Document,
+    name: String,
+) -> Option<Arc<InputObjectTypeDefinition>> {
+    db.input_objects().iter().find_map(|input_obj| {
+        if name == input_obj.name() {
+            return Some(Arc::new(input_obj.clone()));
+        }
+        None
+    })
+}
+
+fn query_operations(db: &dyn Document) -> Arc<Vec<OperationDefinition>> {
+    let operations = db
+        .operations()
+        .iter()
+        .filter_map(|op| op.operation_ty.is_query().then(|| op.clone()))
+        .collect();
+    Arc::new(operations)
+}
+
+fn subscription_operations(db: &dyn Document) -> Arc<Vec<OperationDefinition>> {
+    let operations = db
+        .operations()
+        .iter()
+        .filter_map(|op| op.operation_ty.is_subscription().then(|| op.clone()))
+        .collect();
+    Arc::new(operations)
+}
+
+fn mutation_operations(db: &dyn Document) -> Arc<Vec<OperationDefinition>> {
+    let operations = db
+        .operations()
+        .iter()
+        .filter_map(|op| op.operation_ty.is_mutation().then(|| op.clone()))
+        .collect();
+    Arc::new(operations)
+}
+
+fn operation_fields(db: &dyn Document, id: Uuid) -> Arc<Vec<Field>> {
+    let fields = match db.find_operation(id) {
+        Some(op) => op
+            .selection_set()
+            .selection()
+            .iter()
+            .filter_map(|sel| match sel {
+                Selection::Field(field) => Some(field.as_ref().clone()),
+                _ => None,
+            })
+            .collect(),
+        None => Vec::new(),
+    };
+    Arc::new(fields)
+}
+
+fn operation_inline_fragment_fields(db: &dyn Document, id: Uuid) -> Arc<Vec<Field>> {
+    let fields: Vec<Field> = match db.find_operation(id) {
+        Some(op) => op
+            .selection_set()
+            .selection()
+            .iter()
+            .filter_map(|sel| match sel {
+                Selection::InlineFragment(fragment) => {
+                    let fields: Vec<Field> = fragment.selection_set().fields();
+                    Some(fields)
+                }
+                _ => None,
+            })
+            .flatten()
+            .collect(),
+        None => Vec::new(),
+    };
+    Arc::new(fields)
+}
+
+fn operation_fragment_spread_fields(db: &dyn Document, id: Uuid) -> Arc<Vec<Field>> {
+    let fields: Vec<Field> = match db.find_operation(id) {
+        Some(op) => op
+            .selection_set()
+            .selection()
+            .iter()
+            .filter_map(|sel| match sel {
+                Selection::FragmentSpread(fragment_spread) => {
+                    let fields: Vec<Field> = fragment_spread.fragment(db)?.selection_set().fields();
+                    Some(fields)
+                }
+                _ => None,
+            })
+            .flatten()
+            .collect(),
+        None => Vec::new(),
+    };
+    Arc::new(fields)
+}
+
+// Should be part of operation's db
+// NOTE: potentially want to return a hashmap of variables and their types?
+fn selection_variables(db: &dyn Document, id: Uuid) -> Arc<HashSet<Variable>> {
+    let vars = db
+        .operation_fields(id)
+        .iter()
+        .flat_map(|field| {
+            let vars: Vec<_> = field
+                .arguments()
+                .iter()
+                .flat_map(|arg| get_field_variable_value(arg.value.clone()))
+                .collect();
+            vars
+        })
+        .collect();
+    Arc::new(vars)
+}
+
+fn get_field_variable_value(val: Value) -> Vec<Variable> {
+    match val {
+        Value::Variable(var) => vec![var],
+        Value::List(list) => list
+            .iter()
+            .flat_map(|val| get_field_variable_value(val.clone()))
+            .collect(),
+        Value::Object(obj) => obj
+            .iter()
+            .flat_map(|val| get_field_variable_value(val.1.clone()))
+            .collect(),
+        _ => Vec::new(),
+    }
+}
+
+// should be part of operation's db
+// NOTE: potentially want to return a hashset of variables and their types?
+fn operation_definition_variables(db: &dyn Document, id: Uuid) -> Arc<HashSet<Variable>> {
+    let vars: HashSet<Variable> = match db.find_operation(id) {
+        Some(op) => op
+            .variables()
+            .iter()
+            .map(|v| Variable {
+                name: v.name().to_owned(),
+                ast_ptr: v.ast_ptr().clone(),
+            })
+            .collect(),
+        None => HashSet::new(),
+    };
+    Arc::new(vars)
+}
diff --git a/crates/apollo-compiler/src/queries/inputs_db.rs b/crates/apollo-compiler/src/queries/inputs_db.rs
new file mode 100644
index 0000000..be217d8
--- /dev/null
+++ b/crates/apollo-compiler/src/queries/inputs_db.rs
@@ -0,0 +1,47 @@
+#[salsa::query_group(InputsStorage)]
+pub trait Inputs {
+    #[salsa::input]
+    fn input(&self) -> String;
+}
+
+// #[derive(Clone, Debug, PartialEq, Eq, Hash, Default)]
+// pub struct Sources {
+//     manifest: Vec<SourceInfo>,
+// }
+//
+// impl Sources {
+//     pub fn new(source: String) -> Self {
+//         Self {
+//             manifest: vec![SourceInfo::new(source)],
+//         }
+//     }
+//
+//     pub fn with_sources(sources: Vec<String>) -> Self {
+//         Self {
+//             manifest: sources.into_iter().map(|s| SourceInfo::new(s)).collect(),
+//         }
+//     }
+//
+//     pub(crate) fn manifest(&self, source: String) {
+//         self.manifest.push(SourceInfo::new(source))
+//     }
+// }
+//
+// #[derive(Clone, Debug, PartialEq, Eq, Hash)]
+// pub struct SourceInfo {
+//     pub(crate) file_id: FileId,
+//     pub(crate) name: String,
+// }
+//
+// impl SourceInfo {
+//     pub fn new(name: String) -> Self {
+//         Self {
+//             file_id: FileId(Uuid::new_v4()),
+//             name,
+//         }
+//     }
+// }
+//
+// #[derive(Clone, Debug, PartialEq, Eq, Hash)]
+// pub struct FileId(Uuid);
+//
diff --git a/crates/apollo-compiler/src/queries/mod.rs b/crates/apollo-compiler/src/queries/mod.rs
index 39c68fa..a33be27 100644
--- a/crates/apollo-compiler/src/queries/mod.rs
+++ b/crates/apollo-compiler/src/queries/mod.rs
@@ -1,2 +1,13 @@
 pub mod database;
 pub mod values;
+
+mod def_db;
+mod document_db;
+mod inputs_db;
+mod parser_db;
+
+pub use database::RootDatabase;
+pub use def_db::Definitions;
+pub use document_db::Document;
+pub use inputs_db::Inputs;
+pub use parser_db::DocumentParser;
diff --git a/crates/apollo-compiler/src/queries/parser_db.rs b/crates/apollo-compiler/src/queries/parser_db.rs
new file mode 100644
index 0000000..c68b0ce
--- /dev/null
+++ b/crates/apollo-compiler/src/queries/parser_db.rs
@@ -0,0 +1,40 @@
+use std::sync::Arc;
+
+use apollo_parser::{ast, Parser as ApolloParser, SyntaxTree};
+
+use crate::{diagnostics::SyntaxError, queries::inputs_db::Inputs, ApolloDiagnostic};
+
+#[salsa::query_group(ParserStorage)]
+pub trait DocumentParser: Inputs {
+    fn ast(&self) -> SyntaxTree;
+
+    // root node
+    fn document(&self) -> Arc<ast::Document>;
+
+    fn syntax_errors(&self) -> Vec<ApolloDiagnostic>;
+}
+
+fn ast(db: &dyn DocumentParser) -> SyntaxTree {
+    let input = db.input();
+
+    let parser = ApolloParser::new(&input);
+    parser.parse()
+}
+
+fn document(db: &dyn DocumentParser) -> Arc<ast::Document> {
+    Arc::new(db.ast().document())
+}
+
+fn syntax_errors(db: &dyn DocumentParser) -> Vec<ApolloDiagnostic> {
+    db.ast()
+        .errors()
+        .into_iter()
+        .map(|err| {
+            ApolloDiagnostic::SyntaxError(SyntaxError {
+                src: db.input(),
+                span: (err.index(), err.data().len()).into(), // (offset, length of error token)
+                message: err.message().into(),
+            })
+        })
+        .collect()
+}
diff --git a/crates/apollo-compiler/src/queries/values.rs b/crates/apollo-compiler/src/queries/values.rs
index d35acb0..824f237 100644
--- a/crates/apollo-compiler/src/queries/values.rs
+++ b/crates/apollo-compiler/src/queries/values.rs
@@ -7,7 +7,7 @@ use apollo_parser::{
 use ordered_float::{self, OrderedFloat};
 use uuid::Uuid;
 
-use crate::SourceDatabase;
+use crate::Document;
 
 #[derive(Clone, Debug, PartialEq, Eq, Hash)]
 pub enum Definition {
@@ -202,7 +202,6 @@ pub struct FragmentDefinition {
     pub(crate) id: Uuid,
     pub(crate) name: String,
     pub(crate) type_condition: String,
-    pub(crate) reference_ty_id: Option<Uuid>,
     pub(crate) directives: Arc<Vec<Directive>>,
     pub(crate) selection_set: SelectionSet,
     pub(crate) ast_ptr: SyntaxNodePtr,
@@ -245,7 +244,7 @@ impl FragmentDefinition {
     // which operation definition the fragment is used in.
 
     /// Get variables used in a fragment definition.
-    pub fn variables(&self, db: &dyn SourceDatabase) -> Vec<Variable> {
+    pub fn variables(&self, db: &dyn Document) -> Vec<Variable> {
         self.selection_set
             .selection()
             .iter()
@@ -253,12 +252,8 @@ impl FragmentDefinition {
             .collect()
     }
 
-    pub fn ty(&self, db: &dyn SourceDatabase) -> Option<Arc<Definition>> {
-        if let Some(id) = self.reference_ty_id {
-            db.find_type_system_definition(id)
-        } else {
-            None
-        }
+    pub fn ty(&self, db: &dyn Document) -> Option<Arc<Definition>> {
+        db.find_type_system_definition_by_name(self.name.clone())
     }
 
     // Get a reference to SyntaxNodePtr of the current HIR node.
@@ -267,7 +262,7 @@ impl FragmentDefinition {
     }
 
     // Get current HIR node's AST node.
-    pub fn ast_node(&self, db: &dyn SourceDatabase) -> SyntaxNode {
+    pub fn ast_node(&self, db: &dyn Document) -> SyntaxNode {
         let syntax_node_ptr = self.ast_ptr();
         syntax_node_ptr.to_node(db.document().deref().syntax())
     }
@@ -279,7 +274,6 @@ pub struct OperationDefinition {
     pub(crate) operation_ty: OperationType,
     pub(crate) name: Option<String>,
     pub(crate) variables: Arc<Vec<VariableDefinition>>,
-    pub(crate) object_id: Option<Uuid>,
     pub(crate) directives: Arc<Vec<Directive>>,
     pub(crate) selection_set: SelectionSet,
     pub(crate) ast_ptr: SyntaxNodePtr,
@@ -297,11 +291,11 @@ impl OperationDefinition {
     }
 
     /// Get operation's definition object type.
-    pub fn object_type(&self, db: &dyn SourceDatabase) -> Option<Arc<ObjectTypeDefinition>> {
-        if let Some(id) = self.object_id {
-            db.find_object_type(id)
-        } else {
-            None
+    pub fn object_type(&self, db: &dyn Document) -> Option<Arc<ObjectTypeDefinition>> {
+        match self.operation_ty {
+            OperationType::Query => db.schema().query(db),
+            OperationType::Mutation => db.schema().mutation(db),
+            OperationType::Subscription => db.schema().subscription(db),
         }
     }
 
@@ -327,7 +321,7 @@ impl OperationDefinition {
 
     /// Get fields in the operation definition (excluding inline fragments and
     /// fragment spreads).
-    pub fn fields(&self, db: &dyn SourceDatabase) -> Arc<Vec<Field>> {
+    pub fn fields(&self, db: &dyn Document) -> Arc<Vec<Field>> {
         db.operation_fields(self.id)
     }
 
@@ -337,11 +331,11 @@ impl OperationDefinition {
     //
     // We will need to figure out how to store operation definition id on its
     // fragment spreads and inline fragments to do this
-    pub fn fields_in_inline_fragments(&self, db: &dyn SourceDatabase) -> Arc<Vec<Field>> {
+    pub fn fields_in_inline_fragments(&self, db: &dyn Document) -> Arc<Vec<Field>> {
         db.operation_inline_fragment_fields(self.id)
     }
 
-    pub fn fields_in_fragment_spread(&self, db: &dyn SourceDatabase) -> Arc<Vec<Field>> {
+    pub fn fields_in_fragment_spread(&self, db: &dyn Document) -> Arc<Vec<Field>> {
         db.operation_fragment_spread_fields(self.id)
     }
 
@@ -351,15 +345,10 @@ impl OperationDefinition {
     }
 
     /// Get current HIR node's AST node.
-    pub fn ast_node(&self, db: &dyn SourceDatabase) -> SyntaxNode {
+    pub fn ast_node(&self, db: &dyn Document) -> SyntaxNode {
         let syntax_node_ptr = self.ast_ptr();
         syntax_node_ptr.to_node(db.document().deref().syntax())
     }
-
-    /// Get a reference to operation definition's object type id.
-    pub fn object_id(&self) -> Option<&Uuid> {
-        self.object_id.as_ref()
-    }
 }
 
 #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
@@ -465,7 +454,7 @@ impl VariableDefinition {
     }
 
     /// Get current HIR node's AST node.
-    pub fn ast_node(&self, db: &dyn SourceDatabase) -> SyntaxNode {
+    pub fn ast_node(&self, db: &dyn Document) -> SyntaxNode {
         let syntax_node_ptr = self.ast_ptr();
         syntax_node_ptr.to_node(db.document().deref().syntax())
     }
@@ -522,7 +511,7 @@ impl Type {
     /// [`UnionTypeDefinition`]: Definition::UnionTypeDefinition
     /// [`EnumTypeDefinition`]: Definition::EnumTypeDefinition
     #[must_use]
-    pub fn is_output_type(&self, db: &dyn SourceDatabase) -> bool {
+    pub fn is_output_type(&self, db: &dyn Document) -> bool {
         if let Some(ty) = self.ty(db) {
             ty.as_ref().is_output_definition()
         } else {
@@ -537,7 +526,7 @@ impl Type {
     /// [`EnumTypeDefinition`]: Definition::EnumTypeDefinition
     /// [`InputObjectTypeDefinition`]: Definition::ObjectTypeDefinition
     #[must_use]
-    pub fn is_input_type(&self, db: &dyn SourceDatabase) -> bool {
+    pub fn is_input_type(&self, db: &dyn Document) -> bool {
         if let Some(ty) = self.ty(db) {
             ty.as_ref().is_input_definition()
         } else {
@@ -554,12 +543,12 @@ impl Type {
         }
     }
 
-    pub fn ty(&self, db: &dyn SourceDatabase) -> Option<Arc<Definition>> {
+    pub fn ty(&self, db: &dyn Document) -> Option<Arc<Definition>> {
         db.find_definition_by_name(self.name())
     }
 
     /// Get current HIR node's AST node.
-    pub fn ast_node(&self, db: &dyn SourceDatabase) -> Option<SyntaxNode> {
+    pub fn ast_node(&self, db: &dyn Document) -> Option<SyntaxNode> {
         self.ast_ptr()
             .map(|ptr| ptr.to_node(db.document().deref().syntax()))
     }
@@ -608,7 +597,7 @@ impl Directive {
     }
 
     // Get directive definition of the currently used directive
-    pub fn directive(&self, db: &dyn SourceDatabase) -> Option<Arc<DirectiveDefinition>> {
+    pub fn directive(&self, db: &dyn Document) -> Option<Arc<DirectiveDefinition>> {
         db.find_directive_definition_by_name(self.name().to_string())
     }
 
@@ -618,7 +607,7 @@ impl Directive {
     }
 
     /// Get current HIR node's AST node.
-    pub fn ast_node(&self, db: &dyn SourceDatabase) -> SyntaxNode {
+    pub fn ast_node(&self, db: &dyn Document) -> SyntaxNode {
         let syntax_node_ptr = self.ast_ptr();
         syntax_node_ptr.to_node(db.document().deref().syntax())
     }
@@ -667,7 +656,7 @@ impl DirectiveDefinition {
     }
 
     /// Get current HIR node's AST node.
-    pub fn ast_node(&self, db: &dyn SourceDatabase) -> Option<SyntaxNode> {
+    pub fn ast_node(&self, db: &dyn Document) -> Option<SyntaxNode> {
         self.ast_ptr()
             .map(|ptr| ptr.to_node(db.document().deref().syntax()))
     }
@@ -790,7 +779,7 @@ impl Argument {
     }
 
     /// Get current HIR node's AST node.
-    pub fn ast_node(&self, db: &dyn SourceDatabase) -> SyntaxNode {
+    pub fn ast_node(&self, db: &dyn Document) -> SyntaxNode {
         let syntax_node_ptr = self.ast_ptr();
         syntax_node_ptr.to_node(db.document().deref().syntax())
     }
@@ -839,7 +828,7 @@ impl Variable {
     }
 
     /// Get current HIR node's AST node.
-    pub fn ast_node(&self, db: &dyn SourceDatabase) -> SyntaxNode {
+    pub fn ast_node(&self, db: &dyn Document) -> SyntaxNode {
         let syntax_node_ptr = self.ast_ptr();
         syntax_node_ptr.to_node(db.document().deref().syntax())
     }
@@ -924,7 +913,7 @@ pub enum Selection {
 }
 impl Selection {
     /// Get variables used in the selection set.
-    pub fn variables(&self, db: &dyn SourceDatabase) -> Vec<Variable> {
+    pub fn variables(&self, db: &dyn Document) -> Vec<Variable> {
         match self {
             Selection::Field(field) => field.variables(db),
             Selection::FragmentSpread(fragment_spread) => fragment_spread.variables(db),
@@ -962,8 +951,7 @@ pub struct Field {
     pub(crate) alias: Option<Arc<Alias>>,
     pub(crate) name: String,
     pub(crate) arguments: Arc<Vec<Argument>>,
-    pub(crate) ty: Option<Type>,
-    pub(crate) reference_ty_id: Option<Uuid>,
+    pub(crate) parent_obj: Option<String>,
     pub(crate) directives: Arc<Vec<Directive>>,
     pub(crate) selection_set: SelectionSet,
     pub(crate) ast_ptr: SyntaxNodePtr,
@@ -984,21 +972,24 @@ impl Field {
     }
 
     // Get a reference to field's type.
-    pub fn ty(&self) -> Option<&Type> {
-        self.ty.as_ref()
+    pub fn ty(&self, db: &dyn Document) -> Option<Type> {
+        dbg!(&self.parent_obj);
+
+        let def = db
+            .find_type_system_definition_by_name(self.parent_obj.as_ref()?.to_string())?
+            .field(self.name())?
+            .ty()
+            .to_owned();
+        Some(def)
     }
 
     // Get field's original field definition.
-    pub fn field_definition(&self, db: &dyn SourceDatabase) -> Option<FieldDefinition> {
-        if let Some(object_id) = self.reference_ty_id {
-            db.find_object_type(object_id)?
-                .fields_definition()
-                .iter()
-                .find(|field| field.name() == self.name)
-                .cloned()
-        } else {
-            None
-        }
+    pub fn field_definition(&self, db: &dyn Document) -> Option<FieldDefinition> {
+        db.find_object_type_by_name(self.parent_obj.as_ref()?.to_string())?
+            .fields_definition()
+            .iter()
+            .find(|field| field.name() == self.name)
+            .cloned()
     }
 
     /// Get a reference to the field's arguments.
@@ -1017,7 +1008,7 @@ impl Field {
     }
 
     /// Get variables used in the field.
-    pub fn variables(&self, db: &dyn SourceDatabase) -> Vec<Variable> {
+    pub fn variables(&self, db: &dyn Document) -> Vec<Variable> {
         let mut vars: Vec<_> = self
             .arguments
             .iter()
@@ -1041,12 +1032,12 @@ impl Field {
     }
 
     /// Get current HIR node's AST node.
-    pub fn ast_node(&self, db: &dyn SourceDatabase) -> SyntaxNode {
+    pub fn ast_node(&self, db: &dyn Document) -> SyntaxNode {
         let syntax_node_ptr = self.ast_ptr();
         syntax_node_ptr.to_node(db.document().deref().syntax())
     }
 
-    // pub fn field_definition(&self, db: &dyn SourceDatabase) -> Option<Arc<FieldDefinition>> {
+    // pub fn field_definition(&self, db: &dyn Document) -> Option<Arc<FieldDefinition>> {
     //     db.get
     // }
 }
@@ -1075,7 +1066,7 @@ impl InlineFragment {
         &self.selection_set
     }
 
-    pub fn variables(&self, db: &dyn SourceDatabase) -> Vec<Variable> {
+    pub fn variables(&self, db: &dyn Document) -> Vec<Variable> {
         let vars = self
             .selection_set
             .selection()
@@ -1091,7 +1082,7 @@ impl InlineFragment {
     }
 
     /// Get current HIR node's AST node.
-    pub fn ast_node(&self, db: &dyn SourceDatabase) -> SyntaxNode {
+    pub fn ast_node(&self, db: &dyn Document) -> SyntaxNode {
         let syntax_node_ptr = self.ast_ptr();
         syntax_node_ptr.to_node(db.document().deref().syntax())
     }
@@ -1105,11 +1096,11 @@ pub struct FragmentSpread {
 }
 
 impl FragmentSpread {
-    pub fn fragment(&self, db: &dyn SourceDatabase) -> Option<Arc<FragmentDefinition>> {
+    pub fn fragment(&self, db: &dyn Document) -> Option<Arc<FragmentDefinition>> {
         db.find_fragment_by_name(self.name.clone())
     }
 
-    pub fn variables(&self, db: &dyn SourceDatabase) -> Vec<Variable> {
+    pub fn variables(&self, db: &dyn Document) -> Vec<Variable> {
         let vars = match self.fragment(db) {
             Some(fragment) => fragment
                 .selection_set
@@ -1128,7 +1119,7 @@ impl FragmentSpread {
     }
 
     /// Get current HIR node's AST node.
-    pub fn ast_node(&self, db: &dyn SourceDatabase) -> SyntaxNode {
+    pub fn ast_node(&self, db: &dyn Document) -> SyntaxNode {
         let syntax_node_ptr = self.ast_ptr();
         syntax_node_ptr.to_node(db.document().deref().syntax())
     }
@@ -1177,7 +1168,7 @@ impl SchemaDefinition {
     }
 
     // Get current HIR node's AST node.
-    pub fn ast_node(&self, db: &dyn SourceDatabase) -> Option<SyntaxNode> {
+    pub fn ast_node(&self, db: &dyn Document) -> Option<SyntaxNode> {
         self.ast_ptr()
             .map(|ptr| ptr.to_node(db.document().deref().syntax()))
     }
@@ -1186,10 +1177,10 @@ impl SchemaDefinition {
     // so they are memoised as well
 
     /// Get Schema's query object type definition.
-    pub fn query(&self, db: &dyn SourceDatabase) -> Option<Arc<ObjectTypeDefinition>> {
+    pub fn query(&self, db: &dyn Document) -> Option<Arc<ObjectTypeDefinition>> {
         self.root_operation_type_definition().iter().find_map(|op| {
             if op.operation_type.is_query() {
-                db.find_object_type(op.object_type_id?)
+                db.find_object_type(op.object_type_id(db)?)
             } else {
                 None
             }
@@ -1197,10 +1188,10 @@ impl SchemaDefinition {
     }
 
     /// Get Schema's mutation object type definition.
-    pub fn mutation(&self, db: &dyn SourceDatabase) -> Option<Arc<ObjectTypeDefinition>> {
+    pub fn mutation(&self, db: &dyn Document) -> Option<Arc<ObjectTypeDefinition>> {
         self.root_operation_type_definition().iter().find_map(|op| {
             if op.operation_type.is_mutation() {
-                db.find_object_type(op.object_type_id?)
+                db.find_object_type(op.object_type_id(db)?)
             } else {
                 None
             }
@@ -1208,10 +1199,10 @@ impl SchemaDefinition {
     }
 
     /// Get Schema's subscription object type definition.
-    pub fn subscription(&self, db: &dyn SourceDatabase) -> Option<Arc<ObjectTypeDefinition>> {
+    pub fn subscription(&self, db: &dyn Document) -> Option<Arc<ObjectTypeDefinition>> {
         self.root_operation_type_definition().iter().find_map(|op| {
             if op.operation_type.is_subscription() {
-                db.find_object_type(op.object_type_id?)
+                db.find_object_type(op.object_type_id(db)?)
             } else {
                 None
             }
@@ -1221,7 +1212,6 @@ impl SchemaDefinition {
 
 #[derive(Clone, Debug, Hash, PartialEq, Eq)]
 pub struct RootOperationTypeDefinition {
-    pub(crate) object_type_id: Option<Uuid>,
     pub(crate) operation_type: OperationType,
     pub(crate) named_type: Type,
     pub(crate) ast_ptr: Option<SyntaxNodePtr>,
@@ -1238,13 +1228,18 @@ impl RootOperationTypeDefinition {
         self.operation_type
     }
 
+    pub fn object_type_id(&self, db: &dyn Document) -> Option<Uuid> {
+        db.find_object_type_by_name(self.named_type().name())
+            .map(|object_type| *object_type.id())
+    }
+
     // Get a reference to SyntaxNodePtr of the current HIR node.
     pub fn ast_ptr(&self) -> Option<&SyntaxNodePtr> {
         self.ast_ptr.as_ref()
     }
 
     // Get current HIR node's AST node.
-    pub fn ast_node(&self, db: &dyn SourceDatabase) -> Option<SyntaxNode> {
+    pub fn ast_node(&self, db: &dyn Document) -> Option<SyntaxNode> {
         self.ast_ptr()
             .map(|ptr| ptr.to_node(db.document().deref().syntax()))
     }
@@ -1253,7 +1248,6 @@ impl RootOperationTypeDefinition {
 impl Default for RootOperationTypeDefinition {
     fn default() -> Self {
         Self {
-            object_type_id: None,
             operation_type: OperationType::Query,
             named_type: Type::Named {
                 name: "Query".to_string(),
@@ -1317,7 +1311,7 @@ impl ObjectTypeDefinition {
     }
 
     // Get current HIR node's AST node.
-    pub fn ast_node(&self, db: &dyn SourceDatabase) -> SyntaxNode {
+    pub fn ast_node(&self, db: &dyn Document) -> SyntaxNode {
         let syntax_node_ptr = self.ast_ptr();
         syntax_node_ptr.to_node(db.document().deref().syntax())
     }
@@ -1330,10 +1324,7 @@ pub struct ImplementsInterface {
 }
 
 impl ImplementsInterface {
-    pub fn interface_definition(
-        &self,
-        db: &dyn SourceDatabase,
-    ) -> Option<Arc<InterfaceTypeDefinition>> {
+    pub fn interface_definition(&self, db: &dyn Document) -> Option<Arc<InterfaceTypeDefinition>> {
         db.find_interface_by_name(self.interface.clone())
     }
 
@@ -1347,7 +1338,7 @@ impl ImplementsInterface {
     }
 
     // Get current HIR node's AST node.
-    pub fn ast_node(&self, db: &dyn SourceDatabase) -> SyntaxNode {
+    pub fn ast_node(&self, db: &dyn Document) -> SyntaxNode {
         let syntax_node_ptr = self.ast_ptr();
         syntax_node_ptr.to_node(db.document().deref().syntax())
     }
@@ -1385,7 +1376,7 @@ impl FieldDefinition {
     }
 
     // Get current HIR node's AST node.
-    pub fn ast_node(&self, db: &dyn SourceDatabase) -> SyntaxNode {
+    pub fn ast_node(&self, db: &dyn Document) -> SyntaxNode {
         let syntax_node_ptr = self.ast_ptr();
         syntax_node_ptr.to_node(db.document().deref().syntax())
     }
@@ -1419,7 +1410,7 @@ impl ArgumentsDefinition {
     }
 
     // Get current HIR node's AST node.
-    pub fn ast_node(&self, db: &dyn SourceDatabase) -> Option<SyntaxNode> {
+    pub fn ast_node(&self, db: &dyn Document) -> Option<SyntaxNode> {
         self.ast_ptr()
             .map(|ptr| ptr.to_node(db.document().deref().syntax()))
     }
@@ -1457,7 +1448,7 @@ impl InputValueDefinition {
     }
 
     /// Get current HIR node's AST node.
-    pub fn ast_node(&self, db: &dyn SourceDatabase) -> Option<SyntaxNode> {
+    pub fn ast_node(&self, db: &dyn Document) -> Option<SyntaxNode> {
         self.ast_ptr()
             .map(|ptr| ptr.to_node(db.document().deref().syntax()))
     }
@@ -1510,7 +1501,7 @@ impl ScalarTypeDefinition {
     }
 
     // Get current HIR node's AST node.
-    pub fn ast_node(&self, db: &dyn SourceDatabase) -> Option<SyntaxNode> {
+    pub fn ast_node(&self, db: &dyn Document) -> Option<SyntaxNode> {
         self.ast_ptr()
             .map(|ptr| ptr.to_node(db.document().deref().syntax()))
     }
@@ -1563,7 +1554,7 @@ impl EnumTypeDefinition {
     }
 
     // Get current HIR node's AST node.
-    pub fn ast_node(&self, db: &dyn SourceDatabase) -> SyntaxNode {
+    pub fn ast_node(&self, db: &dyn Document) -> SyntaxNode {
         let syntax_node_ptr = self.ast_ptr();
         syntax_node_ptr.to_node(db.document().deref().syntax())
     }
@@ -1588,7 +1579,7 @@ impl EnumValueDefinition {
     }
 
     // Get current HIR node's AST node.
-    pub fn ast_node(&self, db: &dyn SourceDatabase) -> SyntaxNode {
+    pub fn ast_node(&self, db: &dyn Document) -> SyntaxNode {
         let syntax_node_ptr = self.ast_ptr();
         syntax_node_ptr.to_node(db.document().deref().syntax())
     }
@@ -1636,7 +1627,7 @@ impl UnionTypeDefinition {
     }
 
     // Get current HIR node's AST node.
-    pub fn ast_node(&self, db: &dyn SourceDatabase) -> SyntaxNode {
+    pub fn ast_node(&self, db: &dyn Document) -> SyntaxNode {
         let syntax_node_ptr = self.ast_ptr();
         syntax_node_ptr.to_node(db.document().deref().syntax())
     }
@@ -1645,7 +1636,6 @@ impl UnionTypeDefinition {
 #[derive(Clone, Debug, Hash, PartialEq, Eq)]
 pub struct UnionMember {
     pub(crate) name: String,
-    pub(crate) object_id: Option<Uuid>,
     pub(crate) ast_ptr: SyntaxNodePtr,
 }
 
@@ -1655,8 +1645,8 @@ impl UnionMember {
         self.name.as_ref()
     }
 
-    pub fn object(&self, db: &dyn SourceDatabase) -> Option<Arc<ObjectTypeDefinition>> {
-        db.find_object_type(self.object_id?)
+    pub fn object(&self, db: &dyn Document) -> Option<Arc<ObjectTypeDefinition>> {
+        db.find_object_type_by_name(self.name.clone())
     }
 
     // Get a reference to SyntaxNodePtr of the current HIR node.
@@ -1665,7 +1655,7 @@ impl UnionMember {
     }
 
     // Get current HIR node's AST node.
-    pub fn ast_node(&self, db: &dyn SourceDatabase) -> SyntaxNode {
+    pub fn ast_node(&self, db: &dyn Document) -> SyntaxNode {
         let syntax_node_ptr = self.ast_ptr();
         syntax_node_ptr.to_node(db.document().deref().syntax())
     }
@@ -1719,7 +1709,7 @@ impl InterfaceTypeDefinition {
     }
 
     // Get current HIR node's AST node.
-    pub fn ast_node(&self, db: &dyn SourceDatabase) -> SyntaxNode {
+    pub fn ast_node(&self, db: &dyn Document) -> SyntaxNode {
         let syntax_node_ptr = self.ast_ptr();
         syntax_node_ptr.to_node(db.document().deref().syntax())
     }
@@ -1766,7 +1756,7 @@ impl InputObjectTypeDefinition {
     }
 
     // Get current HIR node's AST node.
-    pub fn ast_node(&self, db: &dyn SourceDatabase) -> SyntaxNode {
+    pub fn ast_node(&self, db: &dyn Document) -> SyntaxNode {
         let syntax_node_ptr = self.ast_ptr();
         syntax_node_ptr.to_node(db.document().deref().syntax())
     }
diff --git a/crates/apollo-compiler/src/validation/directives.rs b/crates/apollo-compiler/src/validation/directives.rs
index 3d92b8c..a82b492 100644
--- a/crates/apollo-compiler/src/validation/directives.rs
+++ b/crates/apollo-compiler/src/validation/directives.rs
@@ -3,10 +3,10 @@ use std::collections::HashMap;
 use crate::{
     diagnostics::{RecursiveDefinition, UniqueDefinition},
     values::DirectiveDefinition,
-    ApolloDiagnostic, SourceDatabase,
+    ApolloDiagnostic, Document,
 };
 
-pub fn check(db: &dyn SourceDatabase) -> Vec<ApolloDiagnostic> {
+pub fn check(db: &dyn Document) -> Vec<ApolloDiagnostic> {
     let mut errors = Vec::new();
 
     // Directive definitions must have unique names.
@@ -27,7 +27,7 @@ pub fn check(db: &dyn SourceDatabase) -> Vec<ApolloDiagnostic> {
                 errors.push(ApolloDiagnostic::UniqueDefinition(UniqueDefinition {
                     ty: "directive".into(),
                     name: name.into(),
-                    src: db.input_string(()).to_string(),
+                    src: db.input(),
                     original_definition: (prev_offset, prev_node_len).into(),
                     redefined_definition: (current_offset, current_node_len).into(),
                     help: Some(format!(
@@ -55,7 +55,7 @@ pub fn check(db: &dyn SourceDatabase) -> Vec<ApolloDiagnostic> {
                     errors.push(ApolloDiagnostic::RecursiveDefinition(RecursiveDefinition {
                         message: format!("{} directive definition cannot reference itself", name),
                         definition: (offset, len).into(),
-                        src: db.input_string(()).to_string(),
+                        src: db.input(),
                         definition_label: "recursive directive definition".into(),
                     }));
                 }
diff --git a/crates/apollo-compiler/src/validation/enums.rs b/crates/apollo-compiler/src/validation/enums.rs
index 7980774..93be4c1 100644
--- a/crates/apollo-compiler/src/validation/enums.rs
+++ b/crates/apollo-compiler/src/validation/enums.rs
@@ -3,10 +3,10 @@ use std::collections::HashMap;
 use crate::{
     diagnostics::{CapitalizedValue, UniqueDefinition},
     values::EnumValueDefinition,
-    ApolloDiagnostic, SourceDatabase,
+    ApolloDiagnostic, Document,
 };
 
-pub fn check(db: &dyn SourceDatabase) -> Vec<ApolloDiagnostic> {
+pub fn check(db: &dyn Document) -> Vec<ApolloDiagnostic> {
     let mut diagnostics = Vec::new();
 
     // An Enum type must define one or more unique enum values.
@@ -25,7 +25,7 @@ pub fn check(db: &dyn SourceDatabase) -> Vec<ApolloDiagnostic> {
                 diagnostics.push(ApolloDiagnostic::UniqueDefinition(UniqueDefinition {
                     ty: "enum".into(),
                     name: value.into(),
-                    src: db.input_string(()).to_string(),
+                    src: db.input(),
                     original_definition: (prev_offset, prev_node_len).into(),
                     redefined_definition: (current_offset, current_node_len).into(),
                     help: Some(format!("{value} must only be defined once in this enum.")),
@@ -48,7 +48,7 @@ pub fn check(db: &dyn SourceDatabase) -> Vec<ApolloDiagnostic> {
             if value.to_uppercase() != value {
                 diagnostics.push(ApolloDiagnostic::CapitalizedValue(CapitalizedValue {
                     ty: value.into(),
-                    src: db.input_string(()).to_string(),
+                    src: db.input(),
                     value: (offset, len).into(),
                 }));
             }
diff --git a/crates/apollo-compiler/src/validation/input_objects.rs b/crates/apollo-compiler/src/validation/input_objects.rs
index 6dcfc04..7c8e2de 100644
--- a/crates/apollo-compiler/src/validation/input_objects.rs
+++ b/crates/apollo-compiler/src/validation/input_objects.rs
@@ -3,10 +3,10 @@ use std::collections::HashMap;
 use crate::{
     diagnostics::{UniqueDefinition, UniqueField},
     values::{InputObjectTypeDefinition, InputValueDefinition},
-    ApolloDiagnostic, SourceDatabase,
+    ApolloDiagnostic, Document,
 };
 
-pub fn check(db: &dyn SourceDatabase) -> Vec<ApolloDiagnostic> {
+pub fn check(db: &dyn Document) -> Vec<ApolloDiagnostic> {
     let mut diagnostics = Vec::new();
 
     // Input Object Definitions must have unique names.
@@ -24,7 +24,7 @@ pub fn check(db: &dyn SourceDatabase) -> Vec<ApolloDiagnostic> {
             diagnostics.push(ApolloDiagnostic::UniqueDefinition(UniqueDefinition {
                 ty: "input object".into(),
                 name: name.into(),
-                src: db.input_string(()).to_string(),
+                src: db.input(),
                 original_definition: (prev_offset, prev_node_len).into(),
                 redefined_definition: (current_offset, current_node_len).into(),
                 help: Some(format!(
@@ -60,7 +60,7 @@ pub fn check(db: &dyn SourceDatabase) -> Vec<ApolloDiagnostic> {
 
                     diagnostics.push(ApolloDiagnostic::UniqueField(UniqueField {
                         field: field_name.into(),
-                        src: db.input_string(()).to_string(),
+                        src: db.input(),
                         original_field: (prev_offset, prev_node_len).into(),
                         redefined_field: (current_offset, current_node_len).into(),
                         help: Some(format!(
diff --git a/crates/apollo-compiler/src/validation/interfaces.rs b/crates/apollo-compiler/src/validation/interfaces.rs
index f700edc..bdd4629 100644
--- a/crates/apollo-compiler/src/validation/interfaces.rs
+++ b/crates/apollo-compiler/src/validation/interfaces.rs
@@ -7,10 +7,10 @@ use crate::{
     },
     validation::ValidationSet,
     values::{FieldDefinition, InterfaceTypeDefinition},
-    ApolloDiagnostic, SourceDatabase,
+    ApolloDiagnostic, Document,
 };
 
-pub fn check(db: &dyn SourceDatabase) -> Vec<ApolloDiagnostic> {
+pub fn check(db: &dyn Document) -> Vec<ApolloDiagnostic> {
     let mut diagnostics = Vec::new();
 
     // Interface definitions must have unique names.
@@ -28,7 +28,7 @@ pub fn check(db: &dyn SourceDatabase) -> Vec<ApolloDiagnostic> {
             diagnostics.push(ApolloDiagnostic::UniqueDefinition(UniqueDefinition {
                 ty: "interface".into(),
                 name: name.into(),
-                src: db.input_string(()).to_string(),
+                src: db.input(),
                 original_definition: (prev_offset, prev_node_len).into(),
                 redefined_definition: (current_offset, current_node_len).into(),
                 help: Some(format!(
@@ -70,7 +70,7 @@ pub fn check(db: &dyn SourceDatabase) -> Vec<ApolloDiagnostic> {
                     diagnostics.push(ApolloDiagnostic::RecursiveDefinition(RecursiveDefinition {
                         message: format!("{} interface cannot implement itself", i_name),
                         definition: (offset, len).into(),
-                        src: db.input_string(()).to_string(),
+                        src: db.input(),
                         definition_label: "recursive implements interfaces".into(),
                     }));
                 }
@@ -98,7 +98,7 @@ pub fn check(db: &dyn SourceDatabase) -> Vec<ApolloDiagnostic> {
 
                 diagnostics.push(ApolloDiagnostic::UniqueField(UniqueField {
                     field: field_name.into(),
-                    src: db.input_string(()).to_string(),
+                    src: db.input(),
                     original_field: (prev_offset, prev_node_len).into(),
                     redefined_field: (offset, len).into(),
                     help: Some(format!(
@@ -115,7 +115,7 @@ pub fn check(db: &dyn SourceDatabase) -> Vec<ApolloDiagnostic> {
                     diagnostics.push(ApolloDiagnostic::OutputType(OutputType {
                         name: field.name().into(),
                         ty: field_ty.ty(),
-                        src: db.input_string(()).to_string(),
+                        src: db.input(),
                         definition: (offset, len).into(),
                     }))
                 }
@@ -124,13 +124,13 @@ pub fn check(db: &dyn SourceDatabase) -> Vec<ApolloDiagnostic> {
                 let field_ty_len: usize = node.text_range().len().into();
                 diagnostics.push(ApolloDiagnostic::UndefinedDefinition(UndefinedDefinition {
                     ty: field.ty().name(),
-                    src: db.input_string(()).to_string(),
+                    src: db.input(),
                     definition: (field_ty_offset, field_ty_len).into(),
                 }))
             } else {
                 diagnostics.push(ApolloDiagnostic::UndefinedDefinition(UndefinedDefinition {
                     ty: field.ty().name(),
-                    src: db.input_string(()).to_string(),
+                    src: db.input(),
                     definition: (offset, len).into(),
                 }))
             }
@@ -163,7 +163,7 @@ pub fn check(db: &dyn SourceDatabase) -> Vec<ApolloDiagnostic> {
             let len: usize = undefined.node.text_range().len().into();
             diagnostics.push(ApolloDiagnostic::UndefinedDefinition(UndefinedDefinition {
                 ty: undefined.name.clone(),
-                src: db.input_string(()).to_string(),
+                src: db.input(),
                 definition: (offset, len).into(),
             }))
         }
@@ -199,7 +199,7 @@ pub fn check(db: &dyn SourceDatabase) -> Vec<ApolloDiagnostic> {
             diagnostics.push(ApolloDiagnostic::TransitiveImplementedInterfaces(
                 TransitiveImplementedInterfaces {
                     missing_interface: undefined.name.clone(),
-                    src: db.input_string(()).to_string(),
+                    src: db.input(),
                     definition: (offset, len).into(),
                 },
             ))
@@ -241,7 +241,7 @@ pub fn check(db: &dyn SourceDatabase) -> Vec<ApolloDiagnostic> {
 
                     diagnostics.push(ApolloDiagnostic::MissingField(MissingField {
                         ty: missing_field.name.clone(),
-                        src: db.input_string(()).to_string(),
+                        src: db.input(),
                         current_definition: (current_offset, current_len).into(),
                         super_definition: (super_offset, super_len).into(),
                         help: Some(
diff --git a/crates/apollo-compiler/src/validation/mod.rs b/crates/apollo-compiler/src/validation/mod.rs
index dc26d6a..c0c4cbe 100644
--- a/crates/apollo-compiler/src/validation/mod.rs
+++ b/crates/apollo-compiler/src/validation/mod.rs
@@ -1,6 +1,6 @@
 use apollo_parser::SyntaxNode;
 
-use crate::{ApolloDiagnostic, SourceDatabase};
+use crate::{ApolloDiagnostic, Document};
 
 // schema
 pub mod schema;
@@ -22,12 +22,12 @@ pub mod operations;
 pub mod unused_variables;
 
 pub struct Validator<'a> {
-    db: &'a dyn SourceDatabase,
+    db: &'a dyn Document,
     diagnostics: Vec<ApolloDiagnostic>,
 }
 
 impl<'a> Validator<'a> {
-    pub fn new(db: &'a dyn SourceDatabase) -> Self {
+    pub fn new(db: &'a dyn Document) -> Self {
         Self {
             db,
             diagnostics: Vec::new(),
diff --git a/crates/apollo-compiler/src/validation/objects.rs b/crates/apollo-compiler/src/validation/objects.rs
index d409928..5e447af 100644
--- a/crates/apollo-compiler/src/validation/objects.rs
+++ b/crates/apollo-compiler/src/validation/objects.rs
@@ -7,10 +7,10 @@ use crate::{
     },
     validation::ValidationSet,
     values::{FieldDefinition, ObjectTypeDefinition},
-    ApolloDiagnostic, SourceDatabase,
+    ApolloDiagnostic, Document,
 };
 
-pub fn check(db: &dyn SourceDatabase) -> Vec<ApolloDiagnostic> {
+pub fn check(db: &dyn Document) -> Vec<ApolloDiagnostic> {
     let mut diagnostics = Vec::new();
 
     // Object Type definitions must have unique names.
@@ -28,7 +28,7 @@ pub fn check(db: &dyn SourceDatabase) -> Vec<ApolloDiagnostic> {
             diagnostics.push(ApolloDiagnostic::UniqueDefinition(UniqueDefinition {
                 ty: "object type".into(),
                 name: name.into(),
-                src: db.input_string(()).to_string(),
+                src: db.input(),
                 original_definition: (prev_offset, prev_node_len).into(),
                 redefined_definition: (current_offset, current_node_len).into(),
                 help: Some(format!(
@@ -59,7 +59,7 @@ pub fn check(db: &dyn SourceDatabase) -> Vec<ApolloDiagnostic> {
 
                 diagnostics.push(ApolloDiagnostic::UniqueField(UniqueField {
                     field: field_name.into(),
-                    src: db.input_string(()).to_string(),
+                    src: db.input(),
                     original_field: (prev_offset, prev_node_len).into(),
                     redefined_field: (offset, len).into(),
                     help: Some(format!(
@@ -76,7 +76,7 @@ pub fn check(db: &dyn SourceDatabase) -> Vec<ApolloDiagnostic> {
                     diagnostics.push(ApolloDiagnostic::OutputType(OutputType {
                         name: field.name().into(),
                         ty: field_ty.ty(),
-                        src: db.input_string(()).to_string(),
+                        src: db.input(),
                         definition: (offset, len).into(),
                     }))
                 }
@@ -85,13 +85,13 @@ pub fn check(db: &dyn SourceDatabase) -> Vec<ApolloDiagnostic> {
                 let field_ty_len: usize = node.text_range().len().into();
                 diagnostics.push(ApolloDiagnostic::UndefinedDefinition(UndefinedDefinition {
                     ty: field.ty().name(),
-                    src: db.input_string(()).to_string(),
+                    src: db.input(),
                     definition: (field_ty_offset, field_ty_len).into(),
                 }))
             } else {
                 diagnostics.push(ApolloDiagnostic::UndefinedDefinition(UndefinedDefinition {
                     ty: field.ty().name(),
-                    src: db.input_string(()).to_string(),
+                    src: db.input(),
                     definition: (offset, len).into(),
                 }))
             }
@@ -125,7 +125,7 @@ pub fn check(db: &dyn SourceDatabase) -> Vec<ApolloDiagnostic> {
             let len: usize = undefined.node.text_range().len().into();
             diagnostics.push(ApolloDiagnostic::UndefinedDefinition(UndefinedDefinition {
                 ty: undefined.name.clone(),
-                src: db.input_string(()).to_string(),
+                src: db.input(),
                 definition: (offset, len).into(),
             }))
         }
@@ -161,7 +161,7 @@ pub fn check(db: &dyn SourceDatabase) -> Vec<ApolloDiagnostic> {
             diagnostics.push(ApolloDiagnostic::TransitiveImplementedInterfaces(
                 TransitiveImplementedInterfaces {
                     missing_interface: undefined.name.clone(),
-                    src: db.input_string(()).to_string(),
+                    src: db.input(),
                     definition: (offset, len).into(),
                 },
             ))
@@ -202,7 +202,7 @@ pub fn check(db: &dyn SourceDatabase) -> Vec<ApolloDiagnostic> {
 
                     diagnostics.push(ApolloDiagnostic::MissingField(MissingField {
                         ty: missing_field.name.clone(),
-                        src: db.input_string(()).to_string(),
+                        src: db.input(),
                         current_definition: (current_offset, current_len).into(),
                         super_definition: (super_offset, super_len).into(),
                         help: Some(
diff --git a/crates/apollo-compiler/src/validation/operations.rs b/crates/apollo-compiler/src/validation/operations.rs
index 33d0623..07c0eb9 100644
--- a/crates/apollo-compiler/src/validation/operations.rs
+++ b/crates/apollo-compiler/src/validation/operations.rs
@@ -5,11 +5,11 @@ use crate::{
         MissingIdent, SingleRootField, UndefinedField, UniqueDefinition, UnsupportedOperation,
     },
     values::{OperationDefinition, Selection},
-    ApolloDiagnostic, SourceDatabase,
+    ApolloDiagnostic, Document,
 };
-// use crate::{diagnostics::ErrorDiagnostic, ApolloDiagnostic, SourceDatabase};
+// use crate::{diagnostics::ErrorDiagnostic, ApolloDiagnostic, Document};
 
-pub fn check(db: &dyn SourceDatabase) -> Vec<ApolloDiagnostic> {
+pub fn check(db: &dyn Document) -> Vec<ApolloDiagnostic> {
     let mut diagnostics = Vec::new();
     // It is possible to have an unnamed (anonymous) operation definition only
     // if there is **one** operation definition.
@@ -26,7 +26,7 @@ pub fn check(db: &dyn SourceDatabase) -> Vec<ApolloDiagnostic> {
                     let offset = op.ast_node(db).text_range().start().into();
                     let len: usize = op.ast_node(db).text_range().len().into();
                     return Some(ApolloDiagnostic::MissingIdent(MissingIdent {
-                        src: db.input_string(()).to_string(),
+                        src: db.input(),
                         definition: (offset, len).into(),
                         help: Some(format!("GraphQL allows a short-hand form for defining query operations when only that one operation exists in the document. There are {op_len} operations in this document."))
                     }));
@@ -52,7 +52,7 @@ pub fn check(db: &dyn SourceDatabase) -> Vec<ApolloDiagnostic> {
                 diagnostics.push(ApolloDiagnostic::UniqueDefinition(UniqueDefinition {
                     ty: "operation".into(),
                     name: name.into(),
-                    src: db.input_string(()).to_string(),
+                    src: db.input(),
                     original_definition: (prev_offset, prev_node_len).into(),
                     redefined_definition: (current_offset, current_node_len).into(),
                     help: Some(format!(
@@ -81,7 +81,7 @@ pub fn check(db: &dyn SourceDatabase) -> Vec<ApolloDiagnostic> {
                     let len: usize = op.ast_node(db).text_range().len().into();
                     Some(ApolloDiagnostic::SingleRootField(SingleRootField {
                         fields: fields.len(),
-                        src: db.input_string(()).to_string(),
+                        src: db.input(),
                         subscription: (offset, len).into(),
                         help: Some(format!(
                             "There are {} root fields: {}. This is not allowed.",
@@ -115,7 +115,7 @@ pub fn check(db: &dyn SourceDatabase) -> Vec<ApolloDiagnostic> {
                     ApolloDiagnostic::UnsupportedOperation(UnsupportedOperation {
                         ty: "Subscription".into(),
                         operation: (op_offset, op_len).into(),
-                        src: db.input_string(()).to_string(),
+                        src: db.input(),
                         schema: Some((schema_offset, schema_len).into()),
                         help: None,
                     })
@@ -123,7 +123,7 @@ pub fn check(db: &dyn SourceDatabase) -> Vec<ApolloDiagnostic> {
                     ApolloDiagnostic::UnsupportedOperation(UnsupportedOperation {
                         ty: "Subscription".into(),
                         operation: (op_offset, op_len).into(),
-                        src: db.input_string(()).to_string(),
+                        src: db.input(),
                         schema: None,
                         help: Some(
                             "consider defining a `subscription` root operation type in your schema"
@@ -151,7 +151,7 @@ pub fn check(db: &dyn SourceDatabase) -> Vec<ApolloDiagnostic> {
                     ApolloDiagnostic::UnsupportedOperation(UnsupportedOperation {
                         ty: "Query".into(),
                         operation: (op_offset, op_len).into(),
-                        src: db.input_string(()).to_string(),
+                        src: db.input(),
                         schema: Some((schema_offset, schema_len).into()),
                         help: None,
                     })
@@ -159,7 +159,7 @@ pub fn check(db: &dyn SourceDatabase) -> Vec<ApolloDiagnostic> {
                     ApolloDiagnostic::UnsupportedOperation(UnsupportedOperation {
                         ty: "Query".into(),
                         operation: (op_offset, op_len).into(),
-                        src: db.input_string(()).to_string(),
+                        src: db.input(),
                         schema: None,
                         help: Some(
                             "consider defining a `query` root operation type in your schema".into(),
@@ -186,7 +186,7 @@ pub fn check(db: &dyn SourceDatabase) -> Vec<ApolloDiagnostic> {
                     ApolloDiagnostic::UnsupportedOperation(UnsupportedOperation {
                         ty: "Mutation".into(),
                         operation: (op_offset, op_len).into(),
-                        src: db.input_string(()).to_string(),
+                        src: db.input(),
                         schema: Some((schema_offset, schema_len).into()),
                         help: None,
                     })
@@ -194,7 +194,7 @@ pub fn check(db: &dyn SourceDatabase) -> Vec<ApolloDiagnostic> {
                     ApolloDiagnostic::UnsupportedOperation(UnsupportedOperation {
                         ty: "Mutation".into(),
                         operation: (op_offset, op_len).into(),
-                        src: db.input_string(()).to_string(),
+                        src: db.input(),
                         schema: None,
                         help: Some(
                             "consider defining a `mutation` root operation type in your schema"
@@ -210,11 +210,9 @@ pub fn check(db: &dyn SourceDatabase) -> Vec<ApolloDiagnostic> {
     // Fields must exist on the type being queried.
     for op in db.operations().iter() {
         for selection in op.selection_set().selection() {
-            let obj_name = op
-                .object_id()
-                .and_then(|id| db.find_object_type(*id).map(|obj| obj.name().to_owned()));
+            let obj_name = op.object_type(db).map(|obj| obj.name().to_owned());
             if let Selection::Field(field) = selection {
-                if field.ty().is_none() {
+                if field.ty(db).is_none() {
                     let offset: usize = field.ast_node(db).text_range().start().into();
                     let len: usize = field.ast_node(db).text_range().len().into();
                     let field_name = field.name().into();
@@ -229,7 +227,7 @@ pub fn check(db: &dyn SourceDatabase) -> Vec<ApolloDiagnostic> {
                     };
                     diagnostics.push(ApolloDiagnostic::UndefinedField(UndefinedField {
                         field: field_name,
-                        src: db.input_string(()).to_string(),
+                        src: db.input(),
                         definition: (offset, len).into(),
                         help,
                     }))
diff --git a/crates/apollo-compiler/src/validation/scalars.rs b/crates/apollo-compiler/src/validation/scalars.rs
index 82d4bb2..9a146de 100644
--- a/crates/apollo-compiler/src/validation/scalars.rs
+++ b/crates/apollo-compiler/src/validation/scalars.rs
@@ -1,11 +1,11 @@
 use crate::{
     diagnostics::{BuiltInScalarDefinition, ScalarSpecificationURL},
-    ApolloDiagnostic, SourceDatabase,
+    ApolloDiagnostic, Document,
 };
 
 const BUILT_IN_SCALARS: [&str; 5] = ["Int", "Float", "Boolean", "String", "ID"];
 
-pub fn check(db: &dyn SourceDatabase) -> Vec<ApolloDiagnostic> {
+pub fn check(db: &dyn Document) -> Vec<ApolloDiagnostic> {
     let mut diagnostics = Vec::new();
 
     for scalar in db.scalars().iter() {
@@ -19,7 +19,7 @@ pub fn check(db: &dyn SourceDatabase) -> Vec<ApolloDiagnostic> {
                 diagnostics.push(ApolloDiagnostic::BuiltInScalarDefinition(
                     BuiltInScalarDefinition {
                         scalar: (offset, len).into(),
-                        src: db.input_string(()).to_string(),
+                        src: db.input(),
                     },
                 ));
             } else if !scalar.is_built_in() {
@@ -33,7 +33,7 @@ pub fn check(db: &dyn SourceDatabase) -> Vec<ApolloDiagnostic> {
                     diagnostics.push(ApolloDiagnostic::ScalarSpecificationURL(
                         ScalarSpecificationURL {
                             scalar: (offset, len).into(),
-                            src: db.input_string(()).to_string(),
+                            src: db.input(),
                         },
                     ))
                 }
@@ -42,4 +42,4 @@ pub fn check(db: &dyn SourceDatabase) -> Vec<ApolloDiagnostic> {
     }
 
     diagnostics
-}
\ No newline at end of file
+}
diff --git a/crates/apollo-compiler/src/validation/schema.rs b/crates/apollo-compiler/src/validation/schema.rs
index d814997..b24c28e 100644
--- a/crates/apollo-compiler/src/validation/schema.rs
+++ b/crates/apollo-compiler/src/validation/schema.rs
@@ -3,10 +3,10 @@ use std::collections::HashMap;
 use crate::{
     diagnostics::{QueryRootOperationType, UniqueDefinition},
     values::RootOperationTypeDefinition,
-    ApolloDiagnostic, SourceDatabase,
+    ApolloDiagnostic, Document,
 };
 
-pub fn check(db: &dyn SourceDatabase) -> Vec<ApolloDiagnostic> {
+pub fn check(db: &dyn Document) -> Vec<ApolloDiagnostic> {
     let mut diagnostics = Vec::new();
 
     // A GraphQL schema must have a Query root operation.
@@ -16,7 +16,7 @@ pub fn check(db: &dyn SourceDatabase) -> Vec<ApolloDiagnostic> {
             let len: usize = node.text_range().len().into();
             diagnostics.push(ApolloDiagnostic::QueryRootOperationType(
                 QueryRootOperationType {
-                    src: db.input_string(()).to_string(),
+                    src: db.input(),
                     schema: (offset, len).into(),
                 },
             ));
@@ -41,7 +41,7 @@ pub fn check(db: &dyn SourceDatabase) -> Vec<ApolloDiagnostic> {
                 diagnostics.push(ApolloDiagnostic::UniqueDefinition(UniqueDefinition {
                     name: name.clone(),
                     ty: "root operation type definition".into(),
-                    src: db.input_string(()).to_string(),
+                    src: db.input(),
                     original_definition: (prev_offset, prev_node_len).into(),
                     redefined_definition: (current_offset, current_node_len).into(),
                     help: Some(format!(
diff --git a/crates/apollo-compiler/src/validation/unions.rs b/crates/apollo-compiler/src/validation/unions.rs
index 225c2cc..4a8d4f3 100644
--- a/crates/apollo-compiler/src/validation/unions.rs
+++ b/crates/apollo-compiler/src/validation/unions.rs
@@ -3,10 +3,10 @@ use std::collections::HashMap;
 use crate::{
     diagnostics::{ObjectType, UndefinedDefinition, UniqueDefinition},
     values::UnionMember,
-    ApolloDiagnostic, SourceDatabase,
+    ApolloDiagnostic, Document,
 };
 
-pub fn check(db: &dyn SourceDatabase) -> Vec<ApolloDiagnostic> {
+pub fn check(db: &dyn Document) -> Vec<ApolloDiagnostic> {
     let mut diagnostics = Vec::new();
 
     for union_def in db.unions().iter() {
@@ -25,7 +25,7 @@ pub fn check(db: &dyn SourceDatabase) -> Vec<ApolloDiagnostic> {
                 diagnostics.push(ApolloDiagnostic::UniqueDefinition(UniqueDefinition {
                     name: name.into(),
                     ty: "union member".into(),
-                    src: db.input_string(()).to_string(),
+                    src: db.input(),
                     original_definition: (prev_offset, prev_node_len).into(),
                     redefined_definition: (offset, len).into(),
                     help: Some(format!(
@@ -41,7 +41,7 @@ pub fn check(db: &dyn SourceDatabase) -> Vec<ApolloDiagnostic> {
             if union_member_type.is_none() {
                 diagnostics.push(ApolloDiagnostic::UndefinedDefinition(UndefinedDefinition {
                     ty: name.into(),
-                    src: db.input_string(()).to_string(),
+                    src: db.input(),
                     definition: (offset, len).into(),
                 }))
             } else if let Some(ty) = union_member_type {
@@ -50,7 +50,7 @@ pub fn check(db: &dyn SourceDatabase) -> Vec<ApolloDiagnostic> {
                     diagnostics.push(ApolloDiagnostic::ObjectType(ObjectType {
                         name: name.into(),
                         ty: ty.ty(),
-                        src: db.input_string(()).to_string(),
+                        src: db.input(),
                         definition: (offset, len).into(),
                     }))
                 }
diff --git a/crates/apollo-compiler/src/validation/unused_variables.rs b/crates/apollo-compiler/src/validation/unused_variables.rs
index 9a26977..d7cfb2f 100644
--- a/crates/apollo-compiler/src/validation/unused_variables.rs
+++ b/crates/apollo-compiler/src/validation/unused_variables.rs
@@ -3,13 +3,13 @@ use std::collections::HashSet;
 use crate::{
     diagnostics::{ApolloDiagnostic, UndefinedDefinition, UnusedVariable},
     validation::ValidationSet,
-    SourceDatabase,
+    Document,
 };
 
 // check in scope
 // check in use
 // compare the two
-pub fn check(db: &dyn SourceDatabase) -> Vec<ApolloDiagnostic> {
+pub fn check(db: &dyn Document) -> Vec<ApolloDiagnostic> {
     db.operations()
         .iter()
         .flat_map(|op| {
@@ -45,7 +45,7 @@ pub fn check(db: &dyn SourceDatabase) -> Vec<ApolloDiagnostic> {
                     let len: usize = undefined_var.node.text_range().len().into();
                     ApolloDiagnostic::UndefinedDefinition(UndefinedDefinition {
                         ty: undefined_var.name.clone(),
-                        src: db.input_string(()).to_string(),
+                        src: db.input(),
                         definition: (offset, len).into(),
                     })
                 })
@@ -57,7 +57,7 @@ pub fn check(db: &dyn SourceDatabase) -> Vec<ApolloDiagnostic> {
                 let len: usize = unused_var.node.text_range().len().into();
                 ApolloDiagnostic::UnusedVariable(UnusedVariable {
                     ty: unused_var.name.clone(),
-                    src: db.input_string(()).to_string(),
+                    src: db.input(),
                     definition: (offset, len).into(),
                 })
             });


Test patch:
diff --git a/crates/apollo-compiler/src/tests.rs b/crates/apollo-compiler/src/tests.rs
index fb3073c..f6f0cb6 100644
--- a/crates/apollo-compiler/src/tests.rs
+++ b/crates/apollo-compiler/src/tests.rs
@@ -27,7 +27,7 @@ fn compiler_tests() {
     dir_tests(&test_data_dir(), &["ok"], "txt", |text, path| {
         let ctx = ApolloCompiler::new(text);
         let errors = ctx.validate();
-        let ast = ctx.parse();
+        let ast = ctx.ast();
         assert_diagnostics_are_absent(&errors, path);
         format!("{:?}", ast)
     });
@@ -52,7 +52,10 @@ fn assert_diagnostics_are_absent(errors: &[ApolloDiagnostic], path: &Path) {
     if !errors.is_empty() {
         let formatted: Vec<String> = errors.iter().map(|e| format!("{:?}", e)).collect();
         println!("{:?}", formatted.join("\n"));
-        panic!("There should be no diagnostics in the file {:?}", path.display(),);
+        panic!(
+            "There should be no diagnostics in the file {:?}",
+            path.display(),
+        );
     }
 }