Repository: apollographql/apollo-rs
Pull request #162 (2022-02-07 16:26:17)

apollo-encoder: align encoder closer to graphql specs #161
close  #161


Patch:
diff --git a/crates/apollo-encoder/CHANGELOG.md b/crates/apollo-encoder/CHANGELOG.md
new file mode 100644
index 0000000..59ce4ce
--- /dev/null
+++ b/crates/apollo-encoder/CHANGELOG.md
@@ -0,0 +1,19 @@
+# Changelog
+
+All notable changes to `apollo-encoder` will be documented in this file.
+
+This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+<!-- # [x.x.x] (unreleased) - 2021-mm-dd
+
+> Important: X breaking changes below, indicated by **BREAKING**
+
+## BREAKING
+
+## Features
+
+## Fixes
+
+## Maintenance
+
+## Documentation -->
\ No newline at end of file
diff --git a/crates/apollo-encoder/README.md b/crates/apollo-encoder/README.md
index 12340db..12c55df 100644
--- a/crates/apollo-encoder/README.md
+++ b/crates/apollo-encoder/README.md
@@ -18,7 +18,9 @@
 </div>
 
 ## Getting started
+
 Add this to your `Cargo.toml` to start using `apollo-encoder`:
+
 ```toml
 # Just an example, change to the necessary package version.
 [dependencies]
@@ -26,19 +28,19 @@ apollo-encoder = "0.1.0"
 ```
 
 Or using [cargo-edit]:
+
 ```bash
 cargo add apollo-encoder
 ```
 
 ## Example
+
 ```rust
-use apollo_encoder::{Schema, Field, UnionDef, EnumValue, Directive, EnumDef, Type_};
+use apollo_encoder::{Schema, Field, UnionDef, EnumValue, DirectiveDef, EnumDef, Type_};
 use indoc::indoc;
-
 let mut schema = Schema::new();
-
 // Create a Directive Definition.
-let mut directive = Directive::new("provideTreat".to_string());
+let mut directive = DirectiveDef::new("provideTreat".to_string());
 directive.description(Some("Ensures cats get treats.".to_string()));
 directive.location("OBJECT".to_string());
 directive.location("FIELD_DEFINITION".to_string());
@@ -50,8 +52,6 @@ let mut enum_ty_1 = EnumValue::new("CatTree".to_string());
 enum_ty_1.description(Some("Top bunk of a cat tree.".to_string()));
 let enum_ty_2 = EnumValue::new("Bed".to_string());
 let mut enum_ty_3 = EnumValue::new("CardboardBox".to_string());
-enum_ty_3.deprecated(Some("Box was recycled.".to_string()));
-
 let mut enum_def = EnumDef::new("NapSpots".to_string());
 enum_def.description(Some("Favourite cat\nnap spots.".to_string()));
 enum_def.value(enum_ty_1);
@@ -66,7 +66,6 @@ union_def.description(Some(
 union_def.member("NORI".to_string());
 union_def.member("CHASHU".to_string());
 schema.union(union_def);
-
 assert_eq!(
     schema.finish(),
     indoc! { r#"
@@ -80,14 +79,16 @@ assert_eq!(
           "Top bunk of a cat tree."
           CatTree
           Bed
-          CardboardBox @deprecated(reason: "Box was recycled.")
+          CardboardBox
         }
         "A union of all cats represented within a household."
         union Cat = NORI | CHASHU
     "# }
 );
 ```
+
 ## License
+
 Licensed under either of
 
 - Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or https://www.apache.org/licenses/LICENSE-2.0)
@@ -95,4 +96,4 @@ Licensed under either of
 
 at your option.
 
-[cargo-edit]: https://github.com/killercup/cargo-edit
\ No newline at end of file
+[cargo-edit]: https://github.com/killercup/cargo-edit
diff --git a/crates/apollo-encoder/src/argument.rs b/crates/apollo-encoder/src/argument.rs
new file mode 100644
index 0000000..5fcf087
--- /dev/null
+++ b/crates/apollo-encoder/src/argument.rs
@@ -0,0 +1,137 @@
+use std::fmt;
+
+use crate::{InputValueDef, Value};
+
+/// The `__ArgumentsDef` type represents an arguments definition
+///
+/// *ArgumentsDefinition*:
+///     ( InputValueDefinition* )
+///
+/// Detailed documentation can be found in [GraphQL spec](https://spec.graphql.org/October2021/#ArgumentsDefinition).
+///
+/// ### Example
+/// ```rust
+/// use apollo_encoder::{ArgumentsDef, InputValueDef, Type_};
+/// use indoc::indoc;
+///
+/// let input_value_defs = vec![
+///     InputValueDef::new(
+///         String::from("first"),
+///         Type_::NamedType {
+///             name: String::from("Int"),
+///         },
+///     ),
+///     InputValueDef::new(
+///         String::from("second"),
+///         Type_::List {
+///             ty: Box::new(Type_::NamedType {
+///                 name: String::from("Int"),
+///             }),
+///         },
+///     ),
+/// ];
+/// let arguments_def = ArgumentsDef::new(input_value_defs);
+///
+/// assert_eq!(arguments_def.to_string(), r#"(first: Int, second: [Int])"#);
+/// ```
+#[derive(Debug)]
+pub struct ArgumentsDef {
+    input_value_definitions: Vec<InputValueDef>,
+}
+
+impl ArgumentsDef {
+    /// Create a new instance of Argument definition.
+    pub fn new(input_value_definitions: Vec<InputValueDef>) -> Self {
+        Self {
+            input_value_definitions,
+        }
+    }
+}
+
+impl fmt::Display for ArgumentsDef {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f, "(")?;
+        if self.input_value_definitions.len() == 1 {
+            write!(f, "{}, ", self.input_value_definitions[0])?;
+        } else {
+            for (i, input_val_def) in self.input_value_definitions.iter().enumerate() {
+                if i != self.input_value_definitions.len() - 1 {
+                    write!(f, "{}, ", input_val_def)?;
+                } else {
+                    write!(f, "{}", input_val_def)?;
+                }
+            }
+        }
+        write!(f, ")")
+    }
+}
+
+/// The `__Argument` type represents an argument
+///
+/// *Argument*:
+///     Name: Value
+///
+/// Detailed documentation can be found in [GraphQL spec](https://spec.graphql.org/October2021/#sec-Language.Arguments).
+///
+/// ### Example
+/// ```rust
+/// use apollo_encoder::{Argument, Value};
+///
+/// let argument = Argument::new(String::from("argName"), Value::String("value".to_string()));
+/// assert_eq!(argument.to_string(), r#"argName: "value""#);
+/// ```
+#[derive(Debug, PartialEq, Clone)]
+pub struct Argument {
+    name: String,
+    value: Value,
+}
+
+impl Argument {
+    /// Create a new instance of Argument.
+    pub fn new(name: String, value: Value) -> Self {
+        Self { name, value }
+    }
+}
+
+impl fmt::Display for Argument {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f, "{}: {}", self.name, self.value)
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::Type_;
+
+    use super::*;
+
+    #[test]
+    fn it_encodes_argument() {
+        let argument = Argument::new(String::from("argName"), Value::String("value".to_string()));
+
+        assert_eq!(argument.to_string(), r#"argName: "value""#);
+    }
+
+    #[test]
+    fn it_encodes_arguments_definitions() {
+        let input_value_defs = vec![
+            InputValueDef::new(
+                String::from("first"),
+                Type_::NamedType {
+                    name: String::from("Int"),
+                },
+            ),
+            InputValueDef::new(
+                String::from("second"),
+                Type_::List {
+                    ty: Box::new(Type_::NamedType {
+                        name: String::from("Int"),
+                    }),
+                },
+            ),
+        ];
+        let arguments_def = ArgumentsDef::new(input_value_defs);
+
+        assert_eq!(arguments_def.to_string(), r#"(first: Int, second: [Int])"#);
+    }
+}
diff --git a/crates/apollo-encoder/src/directive.rs b/crates/apollo-encoder/src/directive.rs
new file mode 100644
index 0000000..78caec0
--- /dev/null
+++ b/crates/apollo-encoder/src/directive.rs
@@ -0,0 +1,73 @@
+use std::fmt;
+
+use crate::Argument;
+
+/// The `__Directive` type represents a Directive, it provides a way to describe alternate runtime execution and type validation behavior in a GraphQL document.
+///
+/// *Directive*:
+///     @ Name Arguments?
+///
+/// Detailed documentation can be found in [GraphQL spec](https://spec.graphql.org/October2021/#sec-Language.Directives).
+///
+/// ### Example
+/// ```rust
+/// use apollo_encoder::{Argument, Directive, Value};
+///
+/// let mut directive = Directive::new(String::from("myDirective"));
+/// directive.arg(Argument::new(String::from("first"), Value::Int(5)));
+///
+/// assert_eq!(directive.to_string(), "@myDirective(first: 5)");
+/// ```
+#[derive(Debug, PartialEq, Clone)]
+pub struct Directive {
+    name: String,
+    args: Vec<Argument>,
+}
+
+impl Directive {
+    /// Create an instance of Directive
+    pub fn new(name: String) -> Self {
+        Self {
+            name,
+            args: Vec::new(),
+        }
+    }
+
+    /// Add an argument to the directive
+    pub fn arg(&mut self, arg: Argument) {
+        self.args.push(arg);
+    }
+}
+
+impl fmt::Display for Directive {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f, "@{}", self.name)?;
+
+        if !self.args.is_empty() {
+            for (i, arg) in self.args.iter().enumerate() {
+                match i {
+                    0 => write!(f, "({}", arg)?,
+                    _ => write!(f, ", {}", arg)?,
+                }
+            }
+            write!(f, ")")?;
+        }
+
+        Ok(())
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::Value;
+
+    use super::*;
+
+    #[test]
+    fn it_encodes_directive() {
+        let mut directive = Directive::new(String::from("myDirective"));
+        directive.arg(Argument::new(String::from("first"), Value::Int(5)));
+
+        assert_eq!(directive.to_string(), "@myDirective(first: 5)");
+    }
+}
diff --git a/crates/apollo-encoder/src/directive_def.rs b/crates/apollo-encoder/src/directive_def.rs
index 28888a7..315956d 100644
--- a/crates/apollo-encoder/src/directive_def.rs
+++ b/crates/apollo-encoder/src/directive_def.rs
@@ -1,8 +1,8 @@
 use std::fmt;
 
-use crate::{InputValue, StringValue};
+use crate::{InputValueDef, StringValue};
 
-/// The `__Directive` type represents a Directive that a service supports.
+/// The `__DirectiveDef` type represents a Directive definition.
 ///
 /// *DirectiveDefinition*:
 ///     Description? **directive @** Name Arguments Definition? **repeatable**? **on** DirectiveLocations
@@ -11,10 +11,10 @@ use crate::{InputValue, StringValue};
 ///
 /// ### Example
 /// ```rust
-/// use apollo_encoder::{Directive};
+/// use apollo_encoder::{DirectiveDef};
 /// use indoc::indoc;
 ///
-/// let mut directive = Directive::new("infer".to_string());
+/// let mut directive = DirectiveDef::new("infer".to_string());
 /// directive.description(Some("Infer field types\nfrom field values.".to_string()));
 /// directive.location("OBJECT".to_string());
 /// directive.location("FIELD_DEFINITION".to_string());
@@ -31,20 +31,22 @@ use crate::{InputValue, StringValue};
 /// );
 /// ```
 #[derive(Debug)]
-pub struct Directive {
+pub struct DirectiveDef {
     // Name must return a String.
     name: String,
     // Description may return a String or null.
     description: StringValue,
     // Args returns a Vector of __InputValue representing the arguments this
     // directive accepts.
-    args: Vec<InputValue>,
+    args: Vec<InputValueDef>,
     // Locations returns a List of __DirectiveLocation representing the valid
     // locations this directive may be placed.
     locations: Vec<String>,
+    // If the directive is repeatable
+    repeatable: bool,
 }
 
-impl Directive {
+impl DirectiveDef {
     /// Create a new instance of Directive definition.
     pub fn new(name: String) -> Self {
         Self {
@@ -52,6 +54,7 @@ impl Directive {
             description: StringValue::Top { source: None },
             args: Vec::new(),
             locations: Vec::new(),
+            repeatable: false,
         }
     }
 
@@ -68,12 +71,17 @@ impl Directive {
     }
 
     /// Set the Directive's args.
-    pub fn arg(&mut self, arg: InputValue) {
+    pub fn arg(&mut self, arg: InputValueDef) {
         self.args.push(arg);
     }
+
+    /// Set the Directive's repeatable
+    pub fn repeatable(&mut self) {
+        self.repeatable = true;
+    }
 }
 
-impl fmt::Display for Directive {
+impl fmt::Display for DirectiveDef {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         write!(f, "{}", self.description)?;
         write!(f, "directive @{}", self.name)?;
@@ -88,6 +96,10 @@ impl fmt::Display for Directive {
             write!(f, ")")?;
         }
 
+        if self.repeatable {
+            write!(f, " repeatable")?;
+        }
+
         for (i, location) in self.locations.iter().enumerate() {
             match i {
                 0 => write!(f, " on {}", location)?,
@@ -103,13 +115,12 @@ impl fmt::Display for Directive {
 #[cfg(test)]
 mod tests {
     use super::*;
-    // use indoc::indoc;
     use crate::Type_;
     use pretty_assertions::assert_eq;
 
     #[test]
     fn it_encodes_directives_for_a_single_location() {
-        let mut directive = Directive::new("infer".to_string());
+        let mut directive = DirectiveDef::new("infer".to_string());
         directive.description(Some("Infer field types from field values.".to_string()));
         directive.location("OBJECT".to_string());
 
@@ -123,7 +134,7 @@ directive @infer on OBJECT
 
     #[test]
     fn it_encodes_directives_for_multiple_location() {
-        let mut directive = Directive::new("infer".to_string());
+        let mut directive = DirectiveDef::new("infer".to_string());
         directive.description(Some("Infer field types\nfrom field values.".to_string()));
         directive.location("OBJECT".to_string());
         directive.location("FIELD_DEFINITION".to_string());
@@ -142,7 +153,7 @@ directive @infer on OBJECT | FIELD_DEFINITION | INPUT_FIELD_DEFINITION
 
     #[test]
     fn it_encodes_directives_with_arguments() {
-        let mut directive = Directive::new("infer".to_string());
+        let mut directive = DirectiveDef::new("infer".to_string());
         directive.description(Some("Infer field types from field values.".to_string()));
         directive.location("OBJECT".to_string());
 
@@ -151,7 +162,7 @@ directive @infer on OBJECT | FIELD_DEFINITION | INPUT_FIELD_DEFINITION
         };
 
         let ty_2 = Type_::List { ty: Box::new(ty_1) };
-        let arg = InputValue::new("cat".to_string(), ty_2);
+        let arg = InputValueDef::new("cat".to_string(), ty_2);
         directive.arg(arg);
 
         assert_eq!(
diff --git a/crates/apollo-encoder/src/document.rs b/crates/apollo-encoder/src/document.rs
new file mode 100644
index 0000000..211b6a1
--- /dev/null
+++ b/crates/apollo-encoder/src/document.rs
@@ -0,0 +1,234 @@
+use std::fmt;
+
+use crate::{
+    DirectiveDef, EnumDef, FragmentDef, InputObjectDef, InterfaceDef, ObjectDef, OperationDef,
+    ScalarDef, SchemaDef, UnionDef,
+};
+
+/// The `__Document` type represents a GraphQL document.A GraphQL Document describes a complete file or request string operated on by a GraphQL service or client.
+/// A document contains multiple definitions, either executable or representative of a GraphQL type system.
+///
+/// *Document*:
+///     OperationDefinition*
+///     FragmentDefinition*
+///     SchemaDefinition*
+///     ScalarTypeDefinition*
+///     ObjectTypeDefinition*
+///     InterfaceTypeDefinition*
+///     UnionTypeDefinition*
+///     EnumTypeDefinition*
+///     InputObjectDefinition*
+///     DirectiveDefinition*
+///
+/// Detailed documentation can be found in [GraphQL spec](https://spec.graphql.org/October2021/#sec-Document).
+///
+/// ### Example
+/// ```rust
+/// use apollo_encoder::{
+///     Argument, Directive, Document, Field, OperationDef, OperationType, Selection, SelectionSet, Type_, Value,
+///     VariableDef,
+/// };
+/// use indoc::indoc;
+///
+/// let mut document = Document::new();
+/// let selection_set = {
+///     let sels = vec![
+///         Selection::Field(Field::new(String::from("first"))),
+///         Selection::Field(Field::new(String::from("second"))),
+///     ];
+///     let mut sel_set = SelectionSet::new();
+///     sels.into_iter().for_each(|sel| sel_set.selection(sel));
+///
+///     sel_set
+/// };
+/// let var_def = VariableDef::new(
+///     String::from("variable_def"),
+///     Type_::List {
+///         ty: Box::new(Type_::NamedType {
+///             name: String::from("Int"),
+///         }),
+///     },
+/// );
+/// let mut new_op = OperationDef::new(OperationType::Query, selection_set);
+/// let mut directive = Directive::new(String::from("testDirective"));
+/// directive.arg(Argument::new(
+///     String::from("first"),
+///     Value::String("one".to_string()),
+/// ));
+/// new_op.variable_definition(var_def);
+/// new_op.directive(directive);
+///
+/// document.operation(new_op);
+///
+/// assert_eq!(
+///     document.to_string(),
+///     indoc! { r#"
+///         query($variable_def: [Int]) @testDirective(first: "one") {
+///           first
+///           second
+///         }
+///     "#}
+/// );
+/// ```
+#[derive(Debug, Default)]
+pub struct Document {
+    operation_definitions: Vec<OperationDef>,
+    fragment_definitions: Vec<FragmentDef>,
+    schema_definitions: Vec<SchemaDef>,
+    // Type definitions
+    scalar_type_definitions: Vec<ScalarDef>,
+    object_type_definitions: Vec<ObjectDef>,
+    interface_type_definitions: Vec<InterfaceDef>,
+    union_type_definitions: Vec<UnionDef>,
+    enum_type_definitions: Vec<EnumDef>,
+    input_object_type_definitions: Vec<InputObjectDef>,
+    // DirectiveDefs
+    directive_definitions: Vec<DirectiveDef>,
+}
+
+impl Document {
+    /// Create a new instance of Document
+    pub fn new() -> Self {
+        Self::default()
+    }
+
+    /// Add operation
+    pub fn operation(&mut self, operation_definition: OperationDef) {
+        self.operation_definitions.push(operation_definition);
+    }
+
+    /// Add fragment
+    pub fn fragment(&mut self, fragment_definition: FragmentDef) {
+        self.fragment_definitions.push(fragment_definition);
+    }
+
+    /// Add schema
+    pub fn schema(&mut self, schema_definition: SchemaDef) {
+        self.schema_definitions.push(schema_definition);
+    }
+    /// Add scalar
+    pub fn scalar(&mut self, scalar_type_definition: ScalarDef) {
+        self.scalar_type_definitions.push(scalar_type_definition);
+    }
+    /// Add object
+    pub fn object(&mut self, object_type_definition: ObjectDef) {
+        self.object_type_definitions.push(object_type_definition);
+    }
+    /// Add interface
+    pub fn interface(&mut self, interface_type_definition: InterfaceDef) {
+        self.interface_type_definitions
+            .push(interface_type_definition);
+    }
+    /// Add union
+    pub fn union(&mut self, union_type_definition: UnionDef) {
+        self.union_type_definitions.push(union_type_definition);
+    }
+    /// Add enum
+    pub fn enum_(&mut self, enum_type_definition: EnumDef) {
+        self.enum_type_definitions.push(enum_type_definition);
+    }
+
+    /// Add input_object
+    pub fn input_object_(&mut self, input_object_type_definition: InputObjectDef) {
+        self.input_object_type_definitions
+            .push(input_object_type_definition);
+    }
+    /// Add directive
+    pub fn directive(&mut self, directive_definition: DirectiveDef) {
+        self.directive_definitions.push(directive_definition);
+    }
+}
+
+impl fmt::Display for Document {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        for operation_def in &self.operation_definitions {
+            write!(f, "{}", operation_def)?;
+        }
+        for fragment_def in &self.fragment_definitions {
+            write!(f, "{}", fragment_def)?;
+        }
+        for schema_def in &self.schema_definitions {
+            write!(f, "{}", schema_def)?;
+        }
+        for scalar_type_def in &self.scalar_type_definitions {
+            write!(f, "{}", scalar_type_def)?;
+        }
+        for object_type_def in &self.object_type_definitions {
+            write!(f, "{}", object_type_def)?;
+        }
+        for interface_type_def in &self.interface_type_definitions {
+            write!(f, "{}", interface_type_def)?;
+        }
+        for union_type_def in &self.union_type_definitions {
+            write!(f, "{}", union_type_def)?;
+        }
+        for enum_type_def in &self.enum_type_definitions {
+            write!(f, "{}", enum_type_def)?;
+        }
+        for union_type_def in &self.union_type_definitions {
+            write!(f, "{}", union_type_def)?;
+        }
+        for input_object_type_def in &self.input_object_type_definitions {
+            write!(f, "{}", input_object_type_def)?;
+        }
+        for directive_def in &self.directive_definitions {
+            write!(f, "{}", directive_def)?;
+        }
+
+        Ok(())
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::{
+        Argument, Directive, Field, OperationType, Selection, SelectionSet, Type_, Value,
+        VariableDef,
+    };
+
+    use super::*;
+    use indoc::indoc;
+
+    #[test]
+    fn it_encodes_a_document_with_operation() {
+        let mut document = Document::new();
+        let selection_set = {
+            let sels = vec![
+                Selection::Field(Field::new(String::from("first"))),
+                Selection::Field(Field::new(String::from("second"))),
+            ];
+            let mut sel_set = SelectionSet::new();
+            sels.into_iter().for_each(|sel| sel_set.selection(sel));
+
+            sel_set
+        };
+        let var_def = VariableDef::new(
+            String::from("variable_def"),
+            Type_::List {
+                ty: Box::new(Type_::NamedType {
+                    name: String::from("Int"),
+                }),
+            },
+        );
+        let mut new_op = OperationDef::new(OperationType::Query, selection_set);
+        let mut directive = Directive::new(String::from("testDirective"));
+        directive.arg(Argument::new(
+            String::from("first"),
+            Value::String("one".to_string()),
+        ));
+        new_op.variable_definition(var_def);
+        new_op.directive(directive);
+
+        document.operation(new_op);
+
+        assert_eq!(
+            document.to_string(),
+            indoc! { r#"
+                query($variable_def: [Int]) @testDirective(first: "one") {
+                  first
+                  second
+                }
+            "#}
+        );
+    }
+}
diff --git a/crates/apollo-encoder/src/enum_def.rs b/crates/apollo-encoder/src/enum_def.rs
index 2fceaf0..f9ec2fb 100644
--- a/crates/apollo-encoder/src/enum_def.rs
+++ b/crates/apollo-encoder/src/enum_def.rs
@@ -1,6 +1,6 @@
 use std::fmt;
 
-use crate::{EnumValue, StringValue};
+use crate::{Directive, EnumValue, StringValue};
 
 /// Enums are special scalars that can only have a defined set of values.
 ///
@@ -11,13 +11,15 @@ use crate::{EnumValue, StringValue};
 ///
 /// ### Example
 /// ```rust
-/// use apollo_encoder::{EnumValue, EnumDef};
+/// use apollo_encoder::{Argument, Directive, EnumValue, EnumDef, Value};
 ///
 /// let mut enum_ty_1 = EnumValue::new("CAT_TREE".to_string());
 /// enum_ty_1.description(Some("Top bunk of a cat tree.".to_string()));
 /// let enum_ty_2 = EnumValue::new("BED".to_string());
+/// let mut deprecated_directive = Directive::new(String::from("deprecated"));
+/// deprecated_directive.arg(Argument::new(String::from("reason"), Value::String(String::from("Box was recycled."))));
 /// let mut enum_ty_3 = EnumValue::new("CARDBOARD_BOX".to_string());
-/// enum_ty_3.deprecated(Some("Box was recycled.".to_string()));
+/// enum_ty_3.directive(deprecated_directive);
 ///
 /// let mut enum_ = EnumDef::new("NapSpots".to_string());
 /// enum_.description(Some("Favourite cat nap spots.".to_string()));
@@ -46,6 +48,9 @@ pub struct EnumDef {
     // A vector of EnumValue. There must be at least one and they must have
     // unique names.
     values: Vec<EnumValue>,
+    /// The vector of directives
+    directives: Vec<Directive>,
+    extend: bool,
 }
 
 impl EnumDef {
@@ -55,9 +60,16 @@ impl EnumDef {
             name,
             description: StringValue::Top { source: None },
             values: Vec::new(),
+            directives: Vec::new(),
+            extend: false,
         }
     }
 
+    /// Set the enum type as an extension
+    pub fn extend(&mut self) {
+        self.extend = true;
+    }
+
     /// Set the Enum Definition's description.
     pub fn description(&mut self, description: Option<String>) {
         self.description = StringValue::Top {
@@ -69,12 +81,28 @@ impl EnumDef {
     pub fn value(&mut self, value: EnumValue) {
         self.values.push(value)
     }
+
+    /// Add a directive.
+    pub fn directive(&mut self, directive: Directive) {
+        self.directives.push(directive)
+    }
 }
 
 impl fmt::Display for EnumDef {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(f, "{}", self.description)?;
-        write!(f, "enum {} {{", self.name)?;
+        if self.extend {
+            write!(f, "extend ")?;
+        } else {
+            // No description when it's a extension
+            write!(f, "{}", self.description)?;
+        }
+
+        write!(f, "enum {}", self.name)?;
+        for directive in &self.directives {
+            write!(f, " {}", directive)?;
+        }
+
+        write!(f, " {{")?;
         for value in &self.values {
             write!(f, "\n{}", value)?;
         }
@@ -84,6 +112,8 @@ impl fmt::Display for EnumDef {
 
 #[cfg(test)]
 mod tests {
+    use crate::{Argument, Value};
+
     use super::*;
     use pretty_assertions::assert_eq;
 
@@ -114,18 +144,67 @@ mod tests {
         enum_ty_1.description(Some("Top bunk of a cat tree.".to_string()));
         let enum_ty_2 = EnumValue::new("BED".to_string());
         let mut enum_ty_3 = EnumValue::new("CARDBOARD_BOX".to_string());
-        enum_ty_3.deprecated(Some("Box was recycled.".to_string()));
+        let mut deprecated_directive = Directive::new(String::from("deprecated"));
+        deprecated_directive.arg(Argument::new(
+            String::from("reason"),
+            Value::String(String::from("Box was recycled.")),
+        ));
+        enum_ty_3.directive(deprecated_directive);
+        let mut directive = Directive::new(String::from("testDirective"));
+        directive.arg(Argument::new(
+            String::from("first"),
+            Value::String("one".to_string()),
+        ));
 
         let mut enum_ = EnumDef::new("NapSpots".to_string());
         enum_.description(Some("Favourite cat nap spots.".to_string()));
         enum_.value(enum_ty_1);
         enum_.value(enum_ty_2);
         enum_.value(enum_ty_3);
+        enum_.directive(directive);
 
         assert_eq!(
             enum_.to_string(),
             r#""Favourite cat nap spots."
-enum NapSpots {
+enum NapSpots @testDirective(first: "one") {
+  "Top bunk of a cat tree."
+  CAT_TREE
+  BED
+  CARDBOARD_BOX @deprecated(reason: "Box was recycled.")
+}
+"#
+        );
+    }
+
+    #[test]
+    fn it_encodes_enum_extension() {
+        let mut enum_ty_1 = EnumValue::new("CAT_TREE".to_string());
+        enum_ty_1.description(Some("Top bunk of a cat tree.".to_string()));
+        let enum_ty_2 = EnumValue::new("BED".to_string());
+        let mut enum_ty_3 = EnumValue::new("CARDBOARD_BOX".to_string());
+        let mut deprecated_directive = Directive::new(String::from("deprecated"));
+        deprecated_directive.arg(Argument::new(
+            String::from("reason"),
+            Value::String(String::from("Box was recycled.")),
+        ));
+        enum_ty_3.directive(deprecated_directive);
+        let mut directive = Directive::new(String::from("testDirective"));
+        directive.arg(Argument::new(
+            String::from("first"),
+            Value::String("one".to_string()),
+        ));
+
+        let mut enum_ = EnumDef::new("NapSpots".to_string());
+        enum_.description(Some("Favourite cat nap spots.".to_string()));
+        enum_.value(enum_ty_1);
+        enum_.value(enum_ty_2);
+        enum_.value(enum_ty_3);
+        enum_.directive(directive);
+        enum_.extend();
+
+        assert_eq!(
+            enum_.to_string(),
+            r#"extend enum NapSpots @testDirective(first: "one") {
   "Top bunk of a cat tree."
   CAT_TREE
   BED
diff --git a/crates/apollo-encoder/src/enum_value.rs b/crates/apollo-encoder/src/enum_value.rs
index 730c4aa..98a442f 100644
--- a/crates/apollo-encoder/src/enum_value.rs
+++ b/crates/apollo-encoder/src/enum_value.rs
@@ -1,6 +1,6 @@
 use std::fmt;
 
-use crate::StringValue;
+use crate::{Directive, StringValue};
 
 /// The __EnumValue type represents one of possible values of an enum.
 ///
@@ -11,11 +11,18 @@ use crate::StringValue;
 ///
 /// ### Example
 /// ```rust
-/// use apollo_encoder::{EnumValue};
+/// use apollo_encoder::{Argument, Directive, EnumValue, Value};
 ///
 /// let mut enum_ty = EnumValue::new("CARDBOARD_BOX".to_string());
 /// enum_ty.description(Some("Box nap spot.".to_string()));
-/// enum_ty.deprecated(Some("Box was recycled.".to_string()));
+/// let mut deprecated_directive = Directive::new(String::from("deprecated"));
+/// deprecated_directive.arg(Argument::new(
+///     String::from("reason"),
+///     Value::String(String::from(
+///         "Box was recycled.",
+///     )),
+/// ));
+/// enum_ty.directive(deprecated_directive);
 ///
 /// assert_eq!(
 ///     enum_ty.to_string(),
@@ -29,10 +36,8 @@ pub struct EnumValue {
     name: String,
     // Description may return a String or null.
     description: StringValue,
-    // Deprecated returns true if this enum value should no longer be used, otherwise false.
-    is_deprecated: bool,
-    // Deprecation reason optionally provides a reason why this enum value is deprecated.
-    deprecation_reason: StringValue,
+    /// The vector of directives
+    directives: Vec<Directive>,
 }
 
 impl EnumValue {
@@ -40,9 +45,8 @@ impl EnumValue {
     pub fn new(name: String) -> Self {
         Self {
             name,
-            is_deprecated: false,
             description: StringValue::Field { source: None },
-            deprecation_reason: StringValue::Reason { source: None },
+            directives: Vec::new(),
         }
     }
 
@@ -53,10 +57,9 @@ impl EnumValue {
         };
     }
 
-    /// Set the Enum Value's deprecation properties.
-    pub fn deprecated(&mut self, reason: Option<String>) {
-        self.is_deprecated = true;
-        self.deprecation_reason = StringValue::Reason { source: reason };
+    /// Add a directive.
+    pub fn directive(&mut self, directive: Directive) {
+        self.directives.push(directive)
     }
 }
 
@@ -65,13 +68,8 @@ impl fmt::Display for EnumValue {
         write!(f, "{}", self.description)?;
         write!(f, "  {}", self.name)?;
 
-        if self.is_deprecated {
-            write!(f, " @deprecated")?;
-            if let StringValue::Reason { source: _ } = &self.deprecation_reason {
-                write!(f, "(reason:")?;
-                write!(f, "{}", self.deprecation_reason)?;
-                write!(f, ")")?
-            }
+        for directive in &self.directives {
+            write!(f, " {}", directive)?;
         }
 
         Ok(())
@@ -80,6 +78,8 @@ impl fmt::Display for EnumValue {
 
 #[cfg(test)]
 mod tests {
+    use crate::{Argument, Value};
+
     use super::*;
     use pretty_assertions::assert_eq;
 
@@ -99,11 +99,17 @@ mod tests {
   CAT_TREE"#
         );
     }
+
     #[test]
-    fn it_encodes_an_enum_value_with_deprecated() {
+    fn it_encodes_an_enum_value_with_directive() {
         let mut enum_ty = EnumValue::new("CARDBOARD_BOX".to_string());
+        let mut directive = Directive::new(String::from("testDirective"));
+        directive.arg(Argument::new(
+            String::from("first"),
+            Value::List(vec![Value::Int(1), Value::Int(2)]),
+        ));
         enum_ty.description(Some("Box nap\nspot.".to_string()));
-        enum_ty.deprecated(Some("Box was recycled.".to_string()));
+        enum_ty.directive(directive);
 
         assert_eq!(
             enum_ty.to_string(),
@@ -111,7 +117,7 @@ mod tests {
   Box nap
   spot.
   """
-  CARDBOARD_BOX @deprecated(reason: "Box was recycled.")"#
+  CARDBOARD_BOX @testDirective(first: [1, 2])"#
         );
     }
 
@@ -119,7 +125,12 @@ mod tests {
     fn it_encodes_an_enum_value_with_deprecated_block_string_value() {
         let mut enum_ty = EnumValue::new("CARDBOARD_BOX".to_string());
         enum_ty.description(Some("Box nap\nspot.".to_string()));
-        enum_ty.deprecated(Some("Box was \"recycled\".".to_string()));
+        let mut deprecated_directive = Directive::new(String::from("deprecated"));
+        deprecated_directive.arg(Argument::new(
+            String::from("reason"),
+            Value::String(String::from(r#"Box was "recycled"."#)),
+        ));
+        enum_ty.directive(deprecated_directive);
 
         assert_eq!(
             enum_ty.to_string(),
@@ -127,11 +138,7 @@ mod tests {
   Box nap
   spot.
   """
-  CARDBOARD_BOX @deprecated(reason:
-  """
-  Box was "recycled".
-  """
-  )"#
+  CARDBOARD_BOX @deprecated(reason: """Box was "recycled".""")"#
         );
     }
 }
diff --git a/crates/apollo-encoder/src/field.rs b/crates/apollo-encoder/src/field.rs
index 97b5102..21626bd 100644
--- a/crates/apollo-encoder/src/field.rs
+++ b/crates/apollo-encoder/src/field.rs
@@ -1,28 +1,28 @@
 use std::fmt;
 
-use crate::{InputValue, StringValue, Type_};
-/// The __Field type represents each field in an Object or Interface type.
+use crate::{Argument, Directive, InputValueDef, SelectionSet, StringValue, Type_};
+/// The __FieldDef type represents each field definition in an Object definition or Interface type definition.
 ///
 /// *FieldDefinition*:
-///     Description? Name ArgumentsDefinition? **:** TypeDirectives?
+///     Description? Name ArgumentsDefinition? **:** Type Directives?
 ///
-/// Detailed documentation can be found in [GraphQL spec](https://spec.graphql.org/October2021/#sec-The-__Field-Type).
+/// Detailed documentation can be found in [GraphQL spec](https://spec.graphql.org/October2021/#FieldDefinition).
 ///
 /// ### Example
 /// ```rust
-/// use apollo_encoder::{Type_, Field, InputValue};
+/// use apollo_encoder::{Type_, FieldDef, InputValueDef};
 ///
 /// let ty_1 = Type_::NamedType {
 ///     name: "CatBreed".to_string(),
 /// };
 ///
-/// let mut field = Field::new("cat".to_string(), ty_1);
+/// let mut field = FieldDef::new("cat".to_string(), ty_1);
 ///
 /// let value_1 = Type_::NamedType {
 ///     name: "CatBreed".to_string(),
 /// };
 ///
-/// let arg = InputValue::new("breed".to_string(), value_1);
+/// let arg = InputValueDef::new("breed".to_string(), value_1);
 ///
 /// field.arg(arg);
 ///
@@ -32,22 +32,20 @@ use crate::{InputValue, StringValue, Type_};
 /// );
 /// ```
 #[derive(Debug, PartialEq, Clone)]
-pub struct Field {
+pub struct FieldDef {
     // Name must return a String.
     name: String,
     // Description may return a String.
     description: StringValue,
     // Args returns a List of __InputValue representing the arguments this field accepts.
-    args: Vec<InputValue>,
+    args: Vec<InputValueDef>,
     // Type must return a __Type that represents the type of value returned by this field.
     type_: Type_,
-    // Deprecated returns true if this field should no longer be used, otherwise false.
-    is_deprecated: bool,
-    // Deprecation reason optionally provides a reason why this field is deprecated.
-    deprecation_reason: StringValue,
+    /// Contains all directives.
+    directives: Vec<Directive>,
 }
 
-impl Field {
+impl FieldDef {
     /// Create a new instance of Field.
     pub fn new(name: String, type_: Type_) -> Self {
         Self {
@@ -55,8 +53,7 @@ impl Field {
             name,
             type_,
             args: Vec::new(),
-            is_deprecated: false,
-            deprecation_reason: StringValue::Reason { source: None },
+            directives: Vec::new(),
         }
     }
 
@@ -67,19 +64,18 @@ impl Field {
         };
     }
 
-    /// Set the Field's deprecation properties.
-    pub fn deprecated(&mut self, reason: Option<String>) {
-        self.is_deprecated = true;
-        self.deprecation_reason = StringValue::Reason { source: reason };
-    }
-
     /// Set the Field's arguments.
-    pub fn arg(&mut self, arg: InputValue) {
+    pub fn arg(&mut self, arg: InputValueDef) {
         self.args.push(arg);
     }
+
+    /// Add a directive.
+    pub fn directive(&mut self, directive: Directive) {
+        self.directives.push(directive)
+    }
 }
 
-impl fmt::Display for Field {
+impl fmt::Display for FieldDef {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         write!(f, "{}", self.description)?;
         write!(f, "  {}", self.name)?;
@@ -96,14 +92,127 @@ impl fmt::Display for Field {
 
         write!(f, ": {}", self.type_)?;
 
-        if self.is_deprecated {
-            write!(f, " @deprecated")?;
+        for directive in &self.directives {
+            write!(f, " {}", directive)?;
+        }
+
+        Ok(())
+    }
+}
+
+/// The __Field type represents each field in an Object or Interface type.
+///
+/// *Field*:
+///     Alias? Name Arguments? Directives? SelectionSet?
+///
+/// Detailed documentation can be found in [GraphQL spec](https://spec.graphql.org/October2021/#sec-Language.Fields).
+///
+/// ### Example
+/// ```rust
+/// use apollo_encoder::Field;
+///
+/// let mut field = Field::new("myField".to_string());
+/// field.alias(Some("myAlias".to_string()));
+///
+/// assert_eq!(field.to_string(), r#"myAlias: myField"#);
+/// ```
+#[derive(Debug, PartialEq, Clone)]
+pub struct Field {
+    // Alias must be a String.
+    alias: Option<String>,
+    // Name must return a String.
+    name: String,
+    // Args returns a List of Argument representing the arguments this field accepts.
+    args: Vec<Argument>,
+    /// Contains all directives.
+    directives: Vec<Directive>,
+    selection_set: Option<SelectionSet>,
+}
+
+impl Field {
+    /// Create an instance of Field
+    pub fn new(name: String) -> Self {
+        Self {
+            name,
+            selection_set: None,
+            alias: None,
+            args: Vec::new(),
+            directives: Vec::new(),
+        }
+    }
+
+    /// Set an alias to a field name
+    pub fn alias(&mut self, alias: Option<String>) {
+        self.alias = alias;
+    }
+
+    /// Add a directive to a field
+    pub fn directive(&mut self, directive: Directive) {
+        self.directives.push(directive);
+    }
 
-            if let StringValue::Reason { source: _ } = &self.deprecation_reason {
-                write!(f, "(reason:")?;
-                write!(f, "{}", self.deprecation_reason)?;
-                write!(f, ")")?
+    /// Add an argument to a field
+    pub fn argument(&mut self, argument: Argument) {
+        self.args.push(argument);
+    }
+
+    /// Set a selection set to a field
+    pub fn selection_set(&mut self, selection_set: Option<SelectionSet>) {
+        self.selection_set = selection_set;
+    }
+
+    /// Should be used everywhere in this crate instead of the Display implementation
+    /// Display implementation is only useful as a public api
+    pub(crate) fn format_with_indent(&self, indent_level: usize) -> String {
+        let mut text = String::from(&self.name);
+
+        if !self.args.is_empty() {
+            for (i, arg) in self.args.iter().enumerate() {
+                match i {
+                    0 => {
+                        text.push_str(&format!("({arg}"));
+                    }
+                    _ => text.push_str(&format!(", {arg}")),
+                }
+            }
+            text.push(')');
+        }
+
+        for directive in &self.directives {
+            text.push_str(&format!(" {directive}"));
+        }
+        if let Some(sel_set) = &self.selection_set {
+            text.push_str(&format!(" {}", sel_set.format_with_indent(indent_level)));
+        }
+
+        text
+    }
+}
+
+impl fmt::Display for Field {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        let indent_level = 0;
+
+        if let Some(alias) = &self.alias {
+            write!(f, "{}: ", alias)?;
+        }
+        write!(f, "{}", self.name)?;
+
+        if !self.args.is_empty() {
+            for (i, arg) in self.args.iter().enumerate() {
+                match i {
+                    0 => write!(f, "({}", arg)?,
+                    _ => write!(f, ", {}", arg)?,
+                }
             }
+            write!(f, ")")?;
+        }
+
+        for directive in &self.directives {
+            write!(f, " {}", directive)?;
+        }
+        if let Some(sel_set) = &self.selection_set {
+            write!(f, " {}", sel_set.format_with_indent(indent_level))?;
         }
 
         Ok(())
@@ -112,37 +221,49 @@ impl fmt::Display for Field {
 
 #[cfg(test)]
 mod tests {
+    use crate::{Argument, Value};
+
     use super::*;
     use pretty_assertions::assert_eq;
 
     #[test]
     fn it_encodes_simple_fields() {
+        let mut field = Field::new("myField".to_string());
+        field.alias(Some("myAlias".to_string()));
+
+        assert_eq!(field.to_string(), r#"myAlias: myField"#);
+    }
+
+    #[test]
+    fn it_encodes_simple_fields_def() {
         let ty_1 = Type_::NamedType {
             name: "SpaceProgram".to_string(),
         };
 
         let ty_2 = Type_::List { ty: Box::new(ty_1) };
         let ty_3 = Type_::NonNull { ty: Box::new(ty_2) };
-        let field = Field::new("spaceCat".to_string(), ty_3);
+        let field = FieldDef::new("spaceCat".to_string(), ty_3);
 
         assert_eq!(field.to_string(), r#"  spaceCat: [SpaceProgram]!"#);
     }
 
     #[test]
-    fn it_encodes_fields_with_deprecation() {
+    fn it_encodes_fields_with_directive() {
         let ty_1 = Type_::NamedType {
             name: "SpaceProgram".to_string(),
         };
 
         let ty_2 = Type_::List { ty: Box::new(ty_1) };
-        let mut field = Field::new("cat".to_string(), ty_2);
+        let mut field = FieldDef::new("cat".to_string(), ty_2);
+        let mut directive = Directive::new(String::from("testDirective"));
+        directive.arg(Argument::new(String::from("first"), Value::Int(1)));
         field.description(Some("Very good cats".to_string()));
-        field.deprecated(Some("Cats are no longer sent to space.".to_string()));
+        field.directive(directive);
 
         assert_eq!(
             field.to_string(),
             r#"  "Very good cats"
-  cat: [SpaceProgram] @deprecated(reason: "Cats are no longer sent to space.")"#
+  cat: [SpaceProgram] @testDirective(first: 1)"#
         );
     }
 
@@ -155,7 +276,7 @@ mod tests {
         let ty_2 = Type_::NonNull { ty: Box::new(ty_1) };
         let ty_3 = Type_::List { ty: Box::new(ty_2) };
         let ty_4 = Type_::NonNull { ty: Box::new(ty_3) };
-        let mut field = Field::new("spaceCat".to_string(), ty_4);
+        let mut field = FieldDef::new("spaceCat".to_string(), ty_4);
         field.description(Some("Very good space cats".to_string()));
 
         assert_eq!(
@@ -174,7 +295,7 @@ mod tests {
         let ty_2 = Type_::NonNull { ty: Box::new(ty_1) };
         let ty_3 = Type_::List { ty: Box::new(ty_2) };
         let ty_4 = Type_::NonNull { ty: Box::new(ty_3) };
-        let mut field = Field::new("spaceCat".to_string(), ty_4);
+        let mut field = FieldDef::new("spaceCat".to_string(), ty_4);
         field.description(Some("Very good space cats".to_string()));
 
         let value_1 = Type_::NamedType {
@@ -184,8 +305,13 @@ mod tests {
         let value_2 = Type_::List {
             ty: Box::new(value_1),
         };
-        let mut arg = InputValue::new("cat".to_string(), value_2);
-        arg.deprecated(Some("Cats are no longer sent to space.".to_string()));
+        let mut arg = InputValueDef::new("cat".to_string(), value_2);
+        let mut deprecated_directive = Directive::new(String::from("deprecated"));
+        deprecated_directive.arg(Argument::new(
+            String::from("reason"),
+            Value::String(String::from("Cats are no longer sent to space.")),
+        ));
+        arg.directive(deprecated_directive);
         field.arg(arg);
 
         assert_eq!(
diff --git a/crates/apollo-encoder/src/fragment.rs b/crates/apollo-encoder/src/fragment.rs
new file mode 100644
index 0000000..ad0e13d
--- /dev/null
+++ b/crates/apollo-encoder/src/fragment.rs
@@ -0,0 +1,368 @@
+use std::fmt;
+
+use crate::{Directive, SelectionSet};
+
+/// The __fragmentDef type represents a fragment definition
+///
+/// *FragmentDefinition*:
+///     fragment FragmentName TypeCondition Directives? SelectionSet
+///
+/// Detailed documentation can be found in [GraphQL spec](https://spec.graphql.org/October2021/#FragmentDefinition).
+///
+/// ### Example
+/// ```rust
+/// use apollo_encoder::{Field, FragmentDef, Selection, SelectionSet, TypeCondition};
+/// use indoc::indoc;
+///
+/// let selections = vec![Selection::Field(Field::new(String::from("myField")))];
+/// let mut selection_set = SelectionSet::new();
+/// selections
+///     .into_iter()
+///     .for_each(|s| selection_set.selection(s));
+/// let mut fragment_def = FragmentDef::new(
+///     String::from("myFragment"),
+///     TypeCondition::new(String::from("User")),
+///     selection_set,
+/// );
+///
+/// assert_eq!(
+///     fragment_def.to_string(),
+///     indoc! {r#"
+///         fragment myFragment on User {
+///           myField
+///         }
+///     "#}
+/// );
+/// ```
+#[derive(Debug)]
+pub struct FragmentDef {
+    name: String,
+    type_condition: TypeCondition,
+    directives: Vec<Directive>,
+    selection_set: SelectionSet,
+}
+
+impl FragmentDef {
+    /// Create an instance of FragmentDeg
+    pub fn new(name: String, type_condition: TypeCondition, selection_set: SelectionSet) -> Self {
+        Self {
+            name,
+            type_condition,
+            selection_set,
+            directives: Vec::new(),
+        }
+    }
+
+    /// Add a directive.
+    pub fn directive(&mut self, directive: Directive) {
+        self.directives.push(directive)
+    }
+}
+
+impl fmt::Display for FragmentDef {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        let indent_level = 0;
+        write!(f, "fragment {} {}", self.name, self.type_condition)?;
+        for directive in &self.directives {
+            write!(f, " {}", directive)?;
+        }
+        write!(
+            f,
+            " {}",
+            self.selection_set.format_with_indent(indent_level)
+        )?;
+
+        Ok(())
+    }
+}
+
+/// The __fragmentSpread type represents a named fragment used in a selection set.
+///
+/// *FragmentSpread*:
+///     ... FragmentName Directives?
+///
+/// Detailed documentation can be found in [GraphQL spec](https://spec.graphql.org/October2021/#FragmentSpread).
+///
+/// ### Example
+/// ```rust
+/// use apollo_encoder::FragmentSpread;
+///
+/// let fragment = FragmentSpread::new(String::from("myFragment"));
+/// assert_eq!(fragment.to_string(), r#"...myFragment"#);
+/// ```
+#[derive(Debug, Clone, PartialEq)]
+pub struct FragmentSpread {
+    name: String,
+    directives: Vec<Directive>,
+}
+
+impl FragmentSpread {
+    /// Create a new instance of FragmentSpread
+    pub fn new(name: String) -> Self {
+        Self {
+            name,
+            directives: Vec::new(),
+        }
+    }
+
+    /// Add a directive.
+    pub fn directive(&mut self, directive: Directive) {
+        self.directives.push(directive)
+    }
+}
+
+impl fmt::Display for FragmentSpread {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f, "...{}", self.name)?;
+        for directive in &self.directives {
+            write!(f, " {}", directive)?;
+        }
+
+        Ok(())
+    }
+}
+
+/// The __inlineFragment type represents an inline fragment in a selection set that could be used as a field
+///
+/// *InlineFragment*:
+///     ... TypeCondition? Directives? SelectionSet
+///
+/// Detailed documentation can be found in [GraphQL spec](https://spec.graphql.org/October2021/#sec-Inline-Fragments).
+///
+/// ### Example
+/// ```rust
+/// use apollo_encoder::{Field, InlineFragment, Selection, SelectionSet, TypeCondition};
+/// use indoc::indoc;
+///
+/// let selections = vec![Selection::Field(Field::new(String::from("myField")))];
+/// let mut selection_set = SelectionSet::new();
+/// selections
+///     .into_iter()
+///     .for_each(|s| selection_set.selection(s));
+///
+/// let mut inline_fragment = InlineFragment::new(selection_set);
+/// inline_fragment.type_condition(Some(TypeCondition::new(String::from("User"))));
+///
+/// assert_eq!(
+///     inline_fragment.to_string(),
+///     indoc! {r#"
+///         ... on User {
+///           myField
+///         }
+///     "#}
+/// );
+/// ```
+#[derive(Debug, Clone, PartialEq)]
+pub struct InlineFragment {
+    type_condition: Option<TypeCondition>,
+    directives: Vec<Directive>,
+    selection_set: SelectionSet,
+    pub(crate) indent_level: usize,
+}
+
+impl InlineFragment {
+    /// Create an instance of InlineFragment
+    pub fn new(selection_set: SelectionSet) -> Self {
+        Self {
+            selection_set,
+            type_condition: Option::default(),
+            directives: Vec::new(),
+            indent_level: 0,
+        }
+    }
+
+    /// Add a directive.
+    pub fn directive(&mut self, directive: Directive) {
+        self.directives.push(directive)
+    }
+
+    /// Set the inline fragment's type condition.
+    pub fn type_condition(&mut self, type_condition: Option<TypeCondition>) {
+        self.type_condition = type_condition;
+    }
+
+    /// Should be used everywhere in this crate isntead of the Display implementation
+    /// Display implementation is only useful as a public api
+    pub(crate) fn format_with_indent(&self, indent_level: usize) -> String {
+        let mut text = String::from("...");
+
+        if let Some(type_condition) = &self.type_condition {
+            text.push_str(&format!(" {}", type_condition));
+        }
+        for directive in &self.directives {
+            text.push_str(&format!(" {}", directive));
+        }
+
+        text.push_str(&format!(
+            " {}",
+            self.selection_set.format_with_indent(indent_level),
+        ));
+
+        text
+    }
+}
+
+// This impl is only useful when we generate only an InlineFragment
+// If it's used from a parent element, we call `format_with_indent`
+impl fmt::Display for InlineFragment {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        let indent_level = 0;
+        write!(f, "...")?;
+        if let Some(type_condition) = &self.type_condition {
+            write!(f, " {}", type_condition)?;
+        }
+        for directive in &self.directives {
+            write!(f, " {}", directive)?;
+        }
+
+        write!(
+            f,
+            " {}",
+            self.selection_set.format_with_indent(indent_level)
+        )?;
+
+        Ok(())
+    }
+}
+
+/// The __typeCondition type represents where a fragment could be applied
+///
+/// *TypeCondition*:
+///     on NamedType
+///
+/// Detailed documentation can be found in [GraphQL spec](https://spec.graphql.org/October2021/#TypeCondition).
+#[derive(Debug, Clone, PartialEq)]
+pub struct TypeCondition {
+    name: String,
+}
+
+impl TypeCondition {
+    /// Create an instance of TypeCondition
+    pub fn new(name: String) -> Self {
+        Self { name }
+    }
+}
+
+impl fmt::Display for TypeCondition {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f, "on {}", self.name)
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use indoc::indoc;
+
+    use crate::{field::Field, Argument, Selection, Value};
+
+    use super::*;
+
+    #[test]
+    fn it_encodes_simple_inline_fragment() {
+        let selections = vec![Selection::Field(Field::new(String::from("myField")))];
+        let mut selection_set = SelectionSet::new();
+        selections
+            .into_iter()
+            .for_each(|s| selection_set.selection(s));
+
+        let mut inline_fragment = InlineFragment::new(selection_set);
+        inline_fragment.type_condition(Some(TypeCondition::new(String::from("User"))));
+
+        assert_eq!(
+            inline_fragment.to_string(),
+            indoc! {r#"
+                ... on User {
+                  myField
+                }
+            "#}
+        );
+    }
+
+    #[test]
+    fn it_encodes_simple_fragment_spread() {
+        let fragment = FragmentSpread::new(String::from("myFragment"));
+
+        assert_eq!(fragment.to_string(), indoc! {r#"...myFragment"#});
+    }
+
+    #[test]
+    fn it_encodes_deeper_inline_fragment() {
+        let another_nested_field_bis = Field::new(String::from("anotherNestedBisField"));
+
+        let mut another_nested_field = Field::new(String::from("anotherNestedField"));
+        let mut selection_set = SelectionSet::new();
+        selection_set.selection(Selection::Field(another_nested_field_bis));
+        another_nested_field.selection_set(selection_set.into());
+
+        let mut selection_set = SelectionSet::new();
+        selection_set.selection(Selection::Field(another_nested_field.clone()));
+        another_nested_field.selection_set(selection_set.into());
+
+        let nested_selections = vec![
+            Selection::Field(Field::new(String::from("nestedField"))),
+            Selection::Field(another_nested_field),
+        ];
+        let mut nested_selection_set = SelectionSet::new();
+        nested_selections
+            .into_iter()
+            .for_each(|s| nested_selection_set.selection(s));
+
+        let other_inline_fragment = InlineFragment::new(nested_selection_set);
+        let selections = vec![
+            Selection::Field(Field::new(String::from("myField"))),
+            Selection::FragmentSpread(FragmentSpread::new(String::from("myFragment"))),
+            Selection::InlineFragment(other_inline_fragment),
+        ];
+        let mut selection_set = SelectionSet::new();
+        selections
+            .into_iter()
+            .for_each(|s| selection_set.selection(s));
+
+        let mut inline_fragment = InlineFragment::new(selection_set);
+        inline_fragment.type_condition(Some(TypeCondition::new(String::from("User"))));
+
+        assert_eq!(
+            inline_fragment.to_string(),
+            indoc! {r#"
+                ... on User {
+                  myField
+                  ...myFragment
+                  ... {
+                    nestedField
+                    anotherNestedField {
+                      anotherNestedField {
+                        anotherNestedBisField
+                      }
+                    }
+                  }
+                }
+            "#}
+        );
+    }
+
+    #[test]
+    fn it_encodes_fragment_definition() {
+        let selections = vec![Selection::Field(Field::new(String::from("myField")))];
+        let mut selection_set = SelectionSet::new();
+        selections
+            .into_iter()
+            .for_each(|s| selection_set.selection(s));
+        let mut fragment_def = FragmentDef::new(
+            String::from("myFragment"),
+            TypeCondition::new(String::from("User")),
+            selection_set,
+        );
+        let mut directive = Directive::new(String::from("myDirective"));
+        directive.arg(Argument::new(String::from("first"), Value::Int(5)));
+        fragment_def.directive(directive);
+
+        assert_eq!(
+            fragment_def.to_string(),
+            indoc! {r#"
+                fragment myFragment on User @myDirective(first: 5) {
+                  myField
+                }
+            "#}
+        );
+    }
+}
diff --git a/crates/apollo-encoder/src/input_field.rs b/crates/apollo-encoder/src/input_field.rs
index 44473ce..07a8fc5 100644
--- a/crates/apollo-encoder/src/input_field.rs
+++ b/crates/apollo-encoder/src/input_field.rs
@@ -1,6 +1,6 @@
 use std::fmt;
 
-use crate::{StringValue, Type_};
+use crate::{Directive, StringValue, Type_};
 
 #[derive(Debug, PartialEq, Clone)]
 /// Input Field in a given Input Object.
@@ -30,6 +30,8 @@ pub struct InputField {
     type_: Type_,
     // Default value for this input field.
     default_value: Option<String>,
+    /// Contains all directives for this input value definition
+    directives: Vec<Directive>,
 }
 
 impl InputField {
@@ -40,6 +42,7 @@ impl InputField {
             name,
             type_,
             default_value: None,
+            directives: Vec::new(),
         }
     }
 
@@ -54,6 +57,11 @@ impl InputField {
     pub fn default(&mut self, default: Option<String>) {
         self.default_value = default;
     }
+
+    /// Add a directive.
+    pub fn directive(&mut self, directive: Directive) {
+        self.directives.push(directive)
+    }
 }
 
 impl fmt::Display for InputField {
@@ -65,12 +73,18 @@ impl fmt::Display for InputField {
             write!(f, " = {}", default)?;
         }
 
+        for directive in &self.directives {
+            write!(f, " {}", directive)?;
+        }
+
         Ok(())
     }
 }
 
 #[cfg(test)]
 mod tests {
+    use crate::{Argument, Value};
+
     use super::*;
     use pretty_assertions::assert_eq;
 
@@ -85,4 +99,21 @@ mod tests {
 
         assert_eq!(field.to_string(), r#"  cat: CatBreed = "Norwegian Forest""#);
     }
+    #[test]
+    fn it_encodes_fields_with_directives() {
+        let ty_1 = Type_::NamedType {
+            name: "CatBreed".to_string(),
+        };
+        let mut directive = Directive::new(String::from("testDirective"));
+        directive.arg(Argument::new(String::from("first"), Value::Int(1)));
+
+        let mut field = InputField::new("cat".to_string(), ty_1);
+        field.default(Some("\"Norwegian Forest\"".to_string()));
+        field.directive(directive);
+
+        assert_eq!(
+            field.to_string(),
+            r#"  cat: CatBreed = "Norwegian Forest" @testDirective(first: 1)"#
+        );
+    }
 }
diff --git a/crates/apollo-encoder/src/input_object_def.rs b/crates/apollo-encoder/src/input_object_def.rs
index 3100c7a..dfb0826 100644
--- a/crates/apollo-encoder/src/input_object_def.rs
+++ b/crates/apollo-encoder/src/input_object_def.rs
@@ -1,6 +1,6 @@
 use std::fmt;
 
-use crate::{InputField, StringValue};
+use crate::{Directive, InputField, StringValue};
 
 /// Input objects are composite types used as inputs into queries defined as a list of named input values..
 ///
@@ -56,6 +56,9 @@ pub struct InputObjectDef {
     description: StringValue,
     // A vector of fields
     fields: Vec<InputField>,
+    /// Contains all directives.
+    directives: Vec<Directive>,
+    extend: bool,
 }
 
 impl InputObjectDef {
@@ -65,9 +68,16 @@ impl InputObjectDef {
             name,
             description: StringValue::Top { source: None },
             fields: Vec::new(),
+            directives: Vec::new(),
+            extend: false,
         }
     }
 
+    /// Set the input object type as an extension
+    pub fn extend(&mut self) {
+        self.extend = true;
+    }
+
     /// Set the InputObjectDef's description field.
     pub fn description(&mut self, description: Option<String>) {
         self.description = StringValue::Top {
@@ -79,13 +89,28 @@ impl InputObjectDef {
     pub fn field(&mut self, field: InputField) {
         self.fields.push(field)
     }
+
+    /// Add a directive.
+    pub fn directive(&mut self, directive: Directive) {
+        self.directives.push(directive)
+    }
 }
 
 impl fmt::Display for InputObjectDef {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(f, "{}", self.description)?;
+        if self.extend {
+            write!(f, "extend ")?;
+        } else {
+            // No description when it's a extension
+            write!(f, "{}", self.description)?;
+        }
 
-        write!(f, "input {} {{", &self.name)?;
+        write!(f, "input {}", &self.name)?;
+
+        for directive in &self.directives {
+            write!(f, " {}", directive)?;
+        }
+        write!(f, " {{")?;
 
         for field in &self.fields {
             write!(f, "\n{}", field)?;
@@ -97,7 +122,7 @@ impl fmt::Display for InputObjectDef {
 #[cfg(test)]
 mod tests {
     use super::*;
-    use crate::{InputField, Type_};
+    use crate::{Argument, InputField, Type_, Value};
     use indoc::indoc;
     use pretty_assertions::assert_eq;
 
@@ -115,15 +140,21 @@ mod tests {
         };
         let mut field_2 = InputField::new("playSpot".to_string(), ty_3);
         field_2.description(Some("Best playime spots, e.g. tree, bed.".to_string()));
+        let mut directive = Directive::new(String::from("testDirective"));
+        directive.arg(Argument::new(
+            String::from("first"),
+            Value::String("one".to_string()),
+        ));
 
         let mut input_def = InputObjectDef::new("PlayTime".to_string());
         input_def.field(field);
         input_def.field(field_2);
+        input_def.directive(directive);
 
         assert_eq!(
             input_def.to_string(),
             indoc! { r#"
-                input PlayTime {
+                input PlayTime @testDirective(first: "one") {
                   toys: [DanglerPoleToys] = "Cat Dangler Pole Bird"
                   "Best playime spots, e.g. tree, bed."
                   playSpot: FavouriteSpots
@@ -164,4 +195,37 @@ mod tests {
             "#}
         );
     }
+
+    #[test]
+    fn it_encodes_input_object_extension() {
+        let ty_1 = Type_::NamedType {
+            name: "DanglerPoleToys".to_string(),
+        };
+
+        let ty_2 = Type_::List { ty: Box::new(ty_1) };
+        let mut field = InputField::new("toys".to_string(), ty_2);
+        field.default(Some("\"Cat Dangler Pole Bird\"".to_string()));
+        let ty_3 = Type_::NamedType {
+            name: "FavouriteSpots".to_string(),
+        };
+        let mut field_2 = InputField::new("playSpot".to_string(), ty_3);
+        field_2.description(Some("Best playime spots, e.g. tree, bed.".to_string()));
+
+        let mut input_def = InputObjectDef::new("PlayTime".to_string());
+        input_def.field(field);
+        input_def.field(field_2);
+        input_def.description(Some("Cat playtime input".to_string()));
+        input_def.extend();
+
+        assert_eq!(
+            input_def.to_string(),
+            indoc! { r#"
+                extend input PlayTime {
+                  toys: [DanglerPoleToys] = "Cat Dangler Pole Bird"
+                  "Best playime spots, e.g. tree, bed."
+                  playSpot: FavouriteSpots
+                }
+            "#}
+        );
+    }
 }
diff --git a/crates/apollo-encoder/src/input_value.rs b/crates/apollo-encoder/src/input_value.rs
index cf99ec9..eff21cd 100644
--- a/crates/apollo-encoder/src/input_value.rs
+++ b/crates/apollo-encoder/src/input_value.rs
@@ -1,14 +1,9 @@
 use std::fmt;
 
-use crate::{StringValue, Type_};
+use crate::{Directive, StringValue, Type_};
 
-// NOTE(@lrlna): __InputValue is also meant to be used for InputFields on an
-// InputObject. We currently do not differentiate between InputFields and
-// Fields, so this is not applied directly to InputObjects. Once we are able to
-// walk an AST to encode a schema, we will want to make sure this struct is used
-// directly: InputObject --> InputField --> InputValue
 
-/// The __InputValue type represents field and directive arguments.
+/// The __InputValueDef type represents field and directive arguments.
 ///
 /// *InputValueDefinition*:
 ///     Description? Name **:** Type DefaultValue? Directives?
@@ -17,24 +12,23 @@ use crate::{StringValue, Type_};
 ///
 /// ### Example
 /// ```rust
-/// use apollo_encoder::{Type_, InputValue};
+/// use apollo_encoder::{Type_, InputValueDef};
 ///
 /// let ty_1 = Type_::NamedType {
 ///     name: "SpaceProgram".to_string(),
 /// };
 ///
 /// let ty_2 = Type_::List { ty: Box::new(ty_1) };
-/// let mut value = InputValue::new("cat".to_string(), ty_2);
+/// let mut value = InputValueDef::new("cat".to_string(), ty_2);
 /// value.description(Some("Very good cats".to_string()));
-/// value.deprecated(Some("Cats are no longer sent to space.".to_string()));
 ///
 /// assert_eq!(
 ///     value.to_string(),
-///     r#""Very good cats" cat: [SpaceProgram] @deprecated(reason: "Cats are no longer sent to space.")"#
+///     r#""Very good cats" cat: [SpaceProgram]"#
 /// );
 /// ```
 #[derive(Debug, PartialEq, Clone)]
-pub struct InputValue {
+pub struct InputValueDef {
     // Name must return a String.
     name: String,
     // Description may return a String.
@@ -46,45 +40,41 @@ pub struct InputValue {
     // not provided at runtime. If this input value has no default value,
     // returns null.
     default: Option<String>,
-    // Deprecated returns true if this field should no longer be used, otherwise false.
-    is_deprecated: bool,
-    // Deprecation reason optionally provides a reason why this field is deprecated.
-    deprecation_reason: Option<String>,
+    /// Contains all directives for this input value definition
+    directives: Vec<Directive>,
 }
 
-impl InputValue {
-    /// Create a new instance of InputValue.
+impl InputValueDef {
+    /// Create a new instance of InputValueDef.
     pub fn new(name: String, type_: Type_) -> Self {
         Self {
             description: StringValue::Input { source: None },
             name,
             type_,
-            is_deprecated: false,
-            deprecation_reason: None,
             default: None,
+            directives: Vec::new(),
         }
     }
 
-    /// Set the InputValue's description.
+    /// Set the InputValueDef's description.
     pub fn description(&mut self, description: Option<String>) {
         self.description = StringValue::Input {
             source: description,
         };
     }
 
-    /// Set the InputValue's default value.
+    /// Set the InputValueDef's default value.
     pub fn default(&mut self, default: Option<String>) {
         self.default = default;
     }
 
-    /// Set the InputValue's deprecation properties.
-    pub fn deprecated(&mut self, reason: Option<String>) {
-        self.is_deprecated = true;
-        self.deprecation_reason = reason;
+    /// Add a directive.
+    pub fn directive(&mut self, directive: Directive) {
+        self.directives.push(directive)
     }
 }
 
-impl fmt::Display for InputValue {
+impl fmt::Display for InputValueDef {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         write!(f, "{}", self.description)?;
 
@@ -94,13 +84,8 @@ impl fmt::Display for InputValue {
             write!(f, " = {}", default)?;
         }
 
-        if self.is_deprecated {
-            write!(f, " @deprecated")?;
-            // Just in case deprecated field is ever used without a reason,
-            // let's properly unwrap this Option.
-            if let Some(reason) = &self.deprecation_reason {
-                write!(f, "(reason: \"{}\")", reason)?;
-            }
+        for directive in &self.directives {
+            write!(f, " {}", directive)?;
         }
 
         Ok(())
@@ -109,6 +94,8 @@ impl fmt::Display for InputValue {
 
 #[cfg(test)]
 mod tests {
+    use crate::{Argument, Value};
+
     use super::*;
     use pretty_assertions::assert_eq;
 
@@ -120,7 +107,7 @@ mod tests {
 
         let ty_2 = Type_::List { ty: Box::new(ty_1) };
         let ty_3 = Type_::NonNull { ty: Box::new(ty_2) };
-        let value = InputValue::new("spaceCat".to_string(), ty_3);
+        let value = InputValueDef::new("spaceCat".to_string(), ty_3);
 
         assert_eq!(value.to_string(), r#"spaceCat: [SpaceProgram]!"#);
     }
@@ -132,7 +119,7 @@ mod tests {
         };
 
         let ty_2 = Type_::NonNull { ty: Box::new(ty_1) };
-        let mut value = InputValue::new("spaceCat".to_string(), ty_2);
+        let mut value = InputValueDef::new("spaceCat".to_string(), ty_2);
         value.default(Some("\"Norwegian Forest\"".to_string()));
 
         assert_eq!(
@@ -142,19 +129,21 @@ mod tests {
     }
 
     #[test]
-    fn it_encodes_value_with_deprecation() {
+    fn it_encodes_value_with_directive() {
         let ty_1 = Type_::NamedType {
             name: "SpaceProgram".to_string(),
         };
 
         let ty_2 = Type_::List { ty: Box::new(ty_1) };
-        let mut value = InputValue::new("cat".to_string(), ty_2);
+        let mut value = InputValueDef::new("cat".to_string(), ty_2);
+        let mut directive = Directive::new(String::from("testDirective"));
+        directive.arg(Argument::new(String::from("first"), Value::Int(1)));
         value.description(Some("Very good cats".to_string()));
-        value.deprecated(Some("Cats are no longer sent to space.".to_string()));
+        value.directive(directive);
 
         assert_eq!(
             value.to_string(),
-            r#""Very good cats" cat: [SpaceProgram] @deprecated(reason: "Cats are no longer sent to space.")"#
+            r#""Very good cats" cat: [SpaceProgram] @testDirective(first: 1)"#
         );
     }
 
@@ -167,7 +156,7 @@ mod tests {
         let ty_2 = Type_::NonNull { ty: Box::new(ty_1) };
         let ty_3 = Type_::List { ty: Box::new(ty_2) };
         let ty_4 = Type_::NonNull { ty: Box::new(ty_3) };
-        let mut value = InputValue::new("spaceCat".to_string(), ty_4);
+        let mut value = InputValueDef::new("spaceCat".to_string(), ty_4);
         value.description(Some("Very good space cats".to_string()));
 
         assert_eq!(
diff --git a/crates/apollo-encoder/src/interface_def.rs b/crates/apollo-encoder/src/interface_def.rs
index bea2054..c37f09a 100644
--- a/crates/apollo-encoder/src/interface_def.rs
+++ b/crates/apollo-encoder/src/interface_def.rs
@@ -1,6 +1,6 @@
 use std::fmt;
 
-use crate::{Field, StringValue};
+use crate::{Directive, FieldDef, StringValue};
 
 /// InterfaceDefs are an abstract type where there are common fields declared.
 ///
@@ -15,7 +15,7 @@ use crate::{Field, StringValue};
 ///
 /// ### Example
 /// ```rust
-/// use apollo_encoder::{Type_, Field, InterfaceDef};
+/// use apollo_encoder::{Type_, FieldDef, InterfaceDef};
 /// use indoc::indoc;
 ///
 /// let ty_1 = Type_::NamedType {
@@ -34,13 +34,13 @@ use crate::{Field, StringValue};
 ///     name: "Boolean".to_string(),
 /// };
 ///
-/// let mut field_1 = Field::new("main".to_string(), ty_1);
+/// let mut field_1 = FieldDef::new("main".to_string(), ty_1);
 /// field_1.description(Some("Cat's main dish of a meal.".to_string()));
 ///
-/// let mut field_2 = Field::new("snack".to_string(), ty_5);
+/// let mut field_2 = FieldDef::new("snack".to_string(), ty_5);
 /// field_2.description(Some("Cat's post meal snack.".to_string()));
 ///
-/// let mut field_3 = Field::new("pats".to_string(), ty_6);
+/// let mut field_3 = FieldDef::new("pats".to_string(), ty_6);
 /// field_3.description(Some("Does cat get a pat after meal?".to_string()));
 ///
 /// // a schema definition
@@ -79,7 +79,10 @@ pub struct InterfaceDef {
     // The vector of interfaces that this interface implements.
     interfaces: Vec<String>,
     // The vector of fields required by this interface.
-    fields: Vec<Field>,
+    fields: Vec<FieldDef>,
+    /// Contains all directives.
+    directives: Vec<Directive>,
+    extend: bool,
 }
 
 impl InterfaceDef {
@@ -90,6 +93,8 @@ impl InterfaceDef {
             description: StringValue::Top { source: None },
             fields: Vec::new(),
             interfaces: Vec::new(),
+            directives: Vec::new(),
+            extend: false,
         }
     }
 
@@ -105,15 +110,30 @@ impl InterfaceDef {
         self.interfaces.push(interface)
     }
 
+    /// Set the interface as an extension
+    pub fn extend(&mut self) {
+        self.extend = true;
+    }
+
     /// Push a Field to schema def's fields vector.
-    pub fn field(&mut self, field: Field) {
+    pub fn field(&mut self, field: FieldDef) {
         self.fields.push(field)
     }
+
+    /// Add a directive.
+    pub fn directive(&mut self, directive: Directive) {
+        self.directives.push(directive)
+    }
 }
 
 impl fmt::Display for InterfaceDef {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(f, "{}", self.description)?;
+        if self.extend {
+            write!(f, "extend ")?;
+        } else {
+            // No description when it's a extension
+            write!(f, "{}", self.description)?;
+        }
 
         write!(f, "interface {}", &self.name)?;
         for (i, interface) in self.interfaces.iter().enumerate() {
@@ -122,6 +142,10 @@ impl fmt::Display for InterfaceDef {
                 _ => write!(f, "& {}", interface)?,
             }
         }
+        for directive in &self.directives {
+            write!(f, " {}", directive)?;
+        }
+
         write!(f, " {{")?;
 
         for field in &self.fields {
@@ -134,7 +158,7 @@ impl fmt::Display for InterfaceDef {
 #[cfg(test)]
 mod tests {
     use super::*;
-    use crate::Type_;
+    use crate::{Argument, Type_, Value};
     use indoc::indoc;
     use pretty_assertions::assert_eq;
 
@@ -156,15 +180,21 @@ mod tests {
             name: "Boolean".to_string(),
         };
 
-        let mut field_1 = Field::new("main".to_string(), ty_1);
+        let mut field_1 = FieldDef::new("main".to_string(), ty_1);
         field_1.description(Some("Cat's main dish of a meal.".to_string()));
 
-        let mut field_2 = Field::new("snack".to_string(), ty_5);
+        let mut field_2 = FieldDef::new("snack".to_string(), ty_5);
         field_2.description(Some("Cat's post meal snack.".to_string()));
 
-        let mut field_3 = Field::new("pats".to_string(), ty_6);
+        let mut field_3 = FieldDef::new("pats".to_string(), ty_6);
         field_3.description(Some("Does cat get a pat\nafter meal?".to_string()));
 
+        let mut directive = Directive::new(String::from("testDirective"));
+        directive.arg(Argument::new(
+            String::from("first"),
+            Value::String("one".to_string()),
+        ));
+
         // a schema definition
         let mut interface = InterfaceDef::new("Meal".to_string());
         interface.description(Some(
@@ -173,6 +203,7 @@ mod tests {
         interface.field(field_1);
         interface.field(field_2);
         interface.field(field_3);
+        interface.directive(directive);
 
         assert_eq!(
             interface.to_string(),
@@ -181,7 +212,69 @@ mod tests {
             Meal interface for various
             meals during the day.
             """
-            interface Meal {
+            interface Meal @testDirective(first: "one") {
+              "Cat's main dish of a meal."
+              main: String
+              "Cat's post meal snack."
+              snack: [String!]!
+              """
+              Does cat get a pat
+              after meal?
+              """
+              pats: Boolean
+            }
+            "# }
+        );
+    }
+
+    #[test]
+    fn it_encodes_interface_extension() {
+        let ty_1 = Type_::NamedType {
+            name: "String".to_string(),
+        };
+
+        let ty_2 = Type_::NamedType {
+            name: "String".to_string(),
+        };
+
+        let ty_3 = Type_::NonNull { ty: Box::new(ty_2) };
+        let ty_4 = Type_::List { ty: Box::new(ty_3) };
+        let ty_5 = Type_::NonNull { ty: Box::new(ty_4) };
+
+        let ty_6 = Type_::NamedType {
+            name: "Boolean".to_string(),
+        };
+
+        let mut field_1 = FieldDef::new("main".to_string(), ty_1);
+        field_1.description(Some("Cat's main dish of a meal.".to_string()));
+
+        let mut field_2 = FieldDef::new("snack".to_string(), ty_5);
+        field_2.description(Some("Cat's post meal snack.".to_string()));
+
+        let mut field_3 = FieldDef::new("pats".to_string(), ty_6);
+        field_3.description(Some("Does cat get a pat\nafter meal?".to_string()));
+
+        let mut directive = Directive::new(String::from("testDirective"));
+        directive.arg(Argument::new(
+            String::from("first"),
+            Value::String("one".to_string()),
+        ));
+
+        // a schema definition
+        let mut interface = InterfaceDef::new("Meal".to_string());
+        interface.description(Some(
+            "Meal interface for various\nmeals during the day.".to_string(),
+        ));
+        interface.field(field_1);
+        interface.field(field_2);
+        interface.field(field_3);
+        interface.directive(directive);
+        interface.extend();
+
+        assert_eq!(
+            interface.to_string(),
+            indoc! { r#"
+            extend interface Meal @testDirective(first: "one") {
               "Cat's main dish of a meal."
               main: String
               "Cat's post meal snack."
diff --git a/crates/apollo-encoder/src/lib.rs b/crates/apollo-encoder/src/lib.rs
index 9b15287..3656a6e 100644
--- a/crates/apollo-encoder/src/lib.rs
+++ b/crates/apollo-encoder/src/lib.rs
@@ -1,140 +1,53 @@
-//! <div align="center">
-//!   <h1><code>apollo-encoder</code></h1>
-//!
-//!   <p>
-//!     <strong>A library to generate GraphQL Code, SDL.</strong>
-//!   </p>
-//!   <p>
-//!     <a href="https://crates.io/crates/apollo-encoder">
-//!         <img src="https://img.shields.io/crates/v/apollo-encoder.svg?style=flat-square" alt="Crates.io" />
-//!     </a>
-//!     <a href="https://crates.io/crates/apollo-encoder">
-//!         <img src="https://img.shields.io/crates/d/apollo-encoder.svg?style=flat-square" alt="Download" />
-//!     </a>
-//!     <a href="https://docs.rs/apollo-encoder/">
-//!         <img src="https://img.shields.io/static/v1?label=docs&message=apollo-encoder&color=blue&style=flat-square" alt="docs.rs docs" />
-//!     </a>
-//!   </p>
-//! </div>
-//!
-//! For more information on GraphQL Schema Types, please refer to [official
-//! documentation](https://graphql.org/learn/schema/).
-//!
-//! ## Getting started
-//! Add this to your `Cargo.toml` to start using `apollo-encoder`:
-//! ```toml
-//! # Just an example, change to the necessary package version.
-//! [dependencies]
-//! apollo-encoder = "0.1.0"
-//! ```
-//!
-//! Or using [cargo-edit]:
-//! ```bash
-//! cargo add apollo-encoder
-//! ```
-//!
-//! ## Example
-//! ```rust
-//! use apollo_encoder::{Schema, Field, UnionDef, EnumValue, Directive, EnumDef, Type_};
-//! use indoc::indoc;
-//!
-//! let mut schema = Schema::new();
-//!
-//! // Create a Directive Definition.
-//! let mut directive = Directive::new("provideTreat".to_string());
-//! directive.description(Some("Ensures cats get treats.".to_string()));
-//! directive.location("OBJECT".to_string());
-//! directive.location("FIELD_DEFINITION".to_string());
-//! directive.location("INPUT_FIELD_DEFINITION".to_string());
-//! schema.directive(directive);
-
-//! // Create an Enum Definition
-//! let mut enum_ty_1 = EnumValue::new("CatTree".to_string());
-//! enum_ty_1.description(Some("Top bunk of a cat tree.".to_string()));
-//! let enum_ty_2 = EnumValue::new("Bed".to_string());
-//! let mut enum_ty_3 = EnumValue::new("CardboardBox".to_string());
-//! enum_ty_3.deprecated(Some("Box was recycled.".to_string()));
-//!
-//! let mut enum_def = EnumDef::new("NapSpots".to_string());
-//! enum_def.description(Some("Favourite cat\nnap spots.".to_string()));
-//! enum_def.value(enum_ty_1);
-//! enum_def.value(enum_ty_2);
-//! enum_def.value(enum_ty_3);
-//! schema.enum_(enum_def);
-//! // Union Definition
-//! let mut union_def = UnionDef::new("Cat".to_string());
-//! union_def.description(Some(
-//!     "A union of all cats represented within a household.".to_string(),
-//! ));
-//! union_def.member("NORI".to_string());
-//! union_def.member("CHASHU".to_string());
-//! schema.union(union_def);
-//!
-//! assert_eq!(
-//!     schema.finish(),
-//!     indoc! { r#"
-//!         "Ensures cats get treats."
-//!         directive @provideTreat on OBJECT | FIELD_DEFINITION | INPUT_FIELD_DEFINITION
-//!         """
-//!         Favourite cat
-//!         nap spots.
-//!         """
-//!         enum NapSpots {
-//!           "Top bunk of a cat tree."
-//!           CatTree
-//!           Bed
-//!           CardboardBox @deprecated(reason: "Box was recycled.")
-//!         }
-//!         "A union of all cats represented within a household."
-//!         union Cat = NORI | CHASHU
-//!     "# }
-//! );
-//! ```
-//!
-//! ## License
-//! Licensed under either of
-//!
-//! - Apache License, Version 2.0 ([LICENSE-APACHE] or <https://www.apache.org/licenses/LICENSE-2.0>)
-//! - MIT license ([LICENSE-MIT] or <https://opensource.org/licenses/MIT>)
-//!
-//! at your option.
-//!
-//! [cargo-edit]: https://github.com/killercup/cargo-edit
-//! [LICENSE-APACHE]: https://github.com/apollographql/apollo-rs/blob/main/crates/apollo-parser/LICENSE-APACHE
-//! [LICENSE-MIT]: https://github.com/apollographql/apollo-rs/blob/main/crates/apollo-parser/LICENSE-MIT
-
+#![doc = include_str!("../README.md")]
 #![forbid(unsafe_code)]
 #![deny(missing_debug_implementations, nonstandard_style)]
 #![warn(missing_docs, future_incompatible, unreachable_pub, rust_2018_idioms)]
 
+mod argument;
+mod directive;
 mod directive_def;
+mod document;
 mod enum_def;
 mod enum_value;
 mod field;
 mod field_value;
+mod fragment;
 mod input_field;
 mod input_object_def;
 mod input_value;
 mod interface_def;
 mod object_def;
+mod operation;
 mod scalar_def;
 mod schema;
 mod schema_def;
+mod selection_set;
 mod string_value;
 mod union_def;
+mod value;
+mod variable;
 
-pub use directive_def::Directive;
+pub use argument::Argument;
+pub use argument::ArgumentsDef;
+pub use directive::Directive;
+pub use directive_def::DirectiveDef;
+pub use document::Document;
 pub use enum_def::EnumDef;
 pub use enum_value::EnumValue;
-pub use field::Field;
+pub use field::{Field, FieldDef};
 pub use field_value::Type_;
+pub use fragment::{FragmentDef, FragmentSpread, InlineFragment, TypeCondition};
 pub use input_field::InputField;
 pub use input_object_def::InputObjectDef;
-pub use input_value::InputValue;
+pub use input_value::InputValueDef;
 pub use interface_def::InterfaceDef;
 pub use object_def::ObjectDef;
+pub use operation::{OperationDef, OperationType};
 pub use scalar_def::ScalarDef;
 pub use schema::Schema;
 pub use schema_def::SchemaDef;
+pub use selection_set::{Selection, SelectionSet};
 pub use string_value::StringValue;
 pub use union_def::UnionDef;
+pub use value::Value;
+pub use variable::VariableDef;
diff --git a/crates/apollo-encoder/src/object_def.rs b/crates/apollo-encoder/src/object_def.rs
index 2e1c20f..06cb36c 100644
--- a/crates/apollo-encoder/src/object_def.rs
+++ b/crates/apollo-encoder/src/object_def.rs
@@ -1,6 +1,6 @@
 use std::fmt;
 
-use crate::{Field, StringValue};
+use crate::{Directive, FieldDef, StringValue};
 /// Object types represent concrete instantiations of sets of fields.
 ///
 /// The introspection types (e.g. `__Type`, `__Field`, etc) are examples of
@@ -13,7 +13,7 @@ use crate::{Field, StringValue};
 ///
 /// ### Example
 /// ```rust
-/// use apollo_encoder::{Type_, Field, ObjectDef};
+/// use apollo_encoder::{Type_, FieldDef, ObjectDef};
 /// use indoc::indoc;
 ///
 /// let ty_1 = Type_::NamedType {
@@ -21,18 +21,17 @@ use crate::{Field, StringValue};
 /// };
 ///
 /// let ty_2 = Type_::List { ty: Box::new(ty_1) };
-/// let mut field = Field::new("toys".to_string(), ty_2);
-/// field.deprecated(Some("Cats are too spoiled".to_string()));
+/// let mut field = FieldDef::new("toys".to_string(), ty_2);
 /// let ty_3 = Type_::NamedType {
 ///     name: "FoodType".to_string(),
 /// };
-/// let mut field_2 = Field::new("food".to_string(), ty_3);
+/// let mut field_2 = FieldDef::new("food".to_string(), ty_3);
 /// field_2.description(Some("Dry or wet food?".to_string()));
 ///
 /// let ty_4 = Type_::NamedType {
 ///     name: "Boolean".to_string(),
 /// };
-/// let field_3 = Field::new("catGrass".to_string(), ty_4);
+/// let field_3 = FieldDef::new("catGrass".to_string(), ty_4);
 ///
 /// let mut object_def = ObjectDef::new("PetStoreTrip".to_string());
 /// object_def.field(field);
@@ -44,7 +43,7 @@ use crate::{Field, StringValue};
 ///     object_def.to_string(),
 ///     indoc! { r#"
 ///         type PetStoreTrip implements ShoppingTrip {
-///           toys: [DanglerPoleToys] @deprecated(reason: "Cats are too spoiled")
+///           toys: [DanglerPoleToys]
 ///           "Dry or wet food?"
 ///           food: FoodType
 ///           catGrass: Boolean
@@ -61,7 +60,10 @@ pub struct ObjectDef {
     // The vector of interfaces that an object implements.
     interfaces: Vec<String>,
     // The vector of fields query‐able on this type.
-    fields: Vec<Field>,
+    fields: Vec<FieldDef>,
+    /// The vector of directives for this object
+    directives: Vec<Directive>,
+    extend: bool,
 }
 
 impl ObjectDef {
@@ -72,6 +74,8 @@ impl ObjectDef {
             description: StringValue::Top { source: None },
             interfaces: Vec::new(),
             fields: Vec::new(),
+            directives: Vec::new(),
+            extend: false,
         }
     }
 
@@ -82,28 +86,47 @@ impl ObjectDef {
         };
     }
 
-    /// Set the interfaces ObjectDef implements.
-    pub fn interface(&mut self, interface: String) {
-        self.interfaces.push(interface)
+    /// Add a directive on ObjectDef.
+    pub fn directive(&mut self, directive: Directive) {
+        self.directives.push(directive)
+    }
+
+    /// Set the object type as an extension
+    pub fn extend(&mut self) {
+        self.extend = true;
     }
 
     /// Push a Field to ObjectDef's fields vector.
-    pub fn field(&mut self, field: Field) {
+    pub fn field(&mut self, field: FieldDef) {
         self.fields.push(field)
     }
+
+    /// Add an interface ObjectDef implements.
+    pub fn interface(&mut self, interface: String) {
+        self.interfaces.push(interface)
+    }
 }
 
 impl fmt::Display for ObjectDef {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(f, "{}", self.description)?;
+        if self.extend {
+            write!(f, "extend ")?;
+        } else {
+            // No description when it's a extension
+            write!(f, "{}", self.description)?;
+        }
 
         write!(f, "type {}", &self.name)?;
         for (i, interface) in self.interfaces.iter().enumerate() {
             match i {
                 0 => write!(f, " implements {}", interface)?,
-                _ => write!(f, "& {}", interface)?,
+                _ => write!(f, " & {}", interface)?,
             }
         }
+        for directive in &self.directives {
+            write!(f, " {}", directive)?;
+        }
+
         write!(f, " {{")?;
 
         for field in &self.fields {
@@ -116,7 +139,7 @@ impl fmt::Display for ObjectDef {
 #[cfg(test)]
 mod tests {
     use super::*;
-    use crate::{Field, Type_};
+    use crate::{Argument, FieldDef, Type_, Value};
     use indoc::indoc;
     use pretty_assertions::assert_eq;
 
@@ -127,7 +150,7 @@ mod tests {
         };
 
         let ty_2 = Type_::List { ty: Box::new(ty_1) };
-        let field = Field::new("toys".to_string(), ty_2);
+        let field = FieldDef::new("toys".to_string(), ty_2);
 
         let mut object_def = ObjectDef::new("PetStoreTrip".to_string());
         object_def.field(field);
@@ -150,10 +173,15 @@ mod tests {
             name: "DanglerPoleToys".to_string(),
         };
 
-        let mut field = Field::new("toys".to_string(), ty_1);
-        field.deprecated(Some(
-            "\"DanglerPoleToys\" are no longer interesting".to_string(),
+        let mut field = FieldDef::new("toys".to_string(), ty_1);
+        let mut deprecated_directive = Directive::new(String::from("deprecated"));
+        deprecated_directive.arg(Argument::new(
+            String::from("reason"),
+            Value::String(String::from(
+                "\"DanglerPoleToys\" are no longer interesting",
+            )),
         ));
+        field.directive(deprecated_directive);
 
         let mut object_def = ObjectDef::new("PetStoreTrip".to_string());
         object_def.field(field);
@@ -164,11 +192,38 @@ mod tests {
             indoc! { r#"
                 "What to get at Fressnapf?"
                 type PetStoreTrip {
-                  toys: DanglerPoleToys @deprecated(reason:
-                  """
-                  "DanglerPoleToys" are no longer interesting
-                  """
-                  )
+                  toys: DanglerPoleToys @deprecated(reason: """"DanglerPoleToys" are no longer interesting""")
+                }
+            "#}
+        );
+    }
+
+    #[test]
+    fn it_encodes_object_with_extend() {
+        let ty_1 = Type_::NamedType {
+            name: "DanglerPoleToys".to_string(),
+        };
+
+        let mut field = FieldDef::new("toys".to_string(), ty_1);
+        let mut deprecated_directive = Directive::new(String::from("deprecated"));
+        deprecated_directive.arg(Argument::new(
+            String::from("reason"),
+            Value::String(String::from(
+                "\"DanglerPoleToys\" are no longer interesting",
+            )),
+        ));
+        field.directive(deprecated_directive);
+
+        let mut object_def = ObjectDef::new("PetStoreTrip".to_string());
+        object_def.field(field);
+        object_def.description(Some("What to get at Fressnapf?".to_string()));
+        object_def.extend();
+
+        assert_eq!(
+            object_def.to_string(),
+            indoc! { r#"
+                extend type PetStoreTrip {
+                  toys: DanglerPoleToys @deprecated(reason: """"DanglerPoleToys" are no longer interesting""")
                 }
             "#}
         );
@@ -181,18 +236,23 @@ mod tests {
         };
 
         let ty_2 = Type_::List { ty: Box::new(ty_1) };
-        let mut field = Field::new("toys".to_string(), ty_2);
-        field.deprecated(Some("Cats are too spoiled".to_string()));
+        let mut field = FieldDef::new("toys".to_string(), ty_2);
+        let mut deprecated_directive = Directive::new(String::from("deprecated"));
+        deprecated_directive.arg(Argument::new(
+            String::from("reason"),
+            Value::String(String::from("Cats are too spoiled")),
+        ));
+        field.directive(deprecated_directive);
         let ty_3 = Type_::NamedType {
             name: "FoodType".to_string(),
         };
-        let mut field_2 = Field::new("food".to_string(), ty_3);
+        let mut field_2 = FieldDef::new("food".to_string(), ty_3);
         field_2.description(Some("Dry or wet food?".to_string()));
 
         let ty_4 = Type_::NamedType {
             name: "Boolean".to_string(),
         };
-        let field_3 = Field::new("catGrass".to_string(), ty_4);
+        let field_3 = FieldDef::new("catGrass".to_string(), ty_4);
 
         let mut object_def = ObjectDef::new("PetStoreTrip".to_string());
         object_def.field(field);
@@ -215,13 +275,65 @@ mod tests {
         );
     }
 
+    #[test]
+    fn it_encodes_object_with_interface_and_directives() {
+        let ty_1 = Type_::NamedType {
+            name: "DanglerPoleToys".to_string(),
+        };
+
+        let mut directive = Directive::new(String::from("testDirective"));
+        directive.arg(Argument::new(
+            String::from("first"),
+            Value::String("one".to_string()),
+        ));
+        let ty_2 = Type_::List { ty: Box::new(ty_1) };
+        let mut field = FieldDef::new("toys".to_string(), ty_2);
+        let mut deprecated_directive = Directive::new(String::from("deprecated"));
+        deprecated_directive.arg(Argument::new(
+            String::from("reason"),
+            Value::String(String::from("Cats are too spoiled")),
+        ));
+        field.directive(deprecated_directive);
+        let ty_3 = Type_::NamedType {
+            name: "FoodType".to_string(),
+        };
+        let mut field_2 = FieldDef::new("food".to_string(), ty_3);
+        field_2.description(Some("Dry or wet food?".to_string()));
+
+        let ty_4 = Type_::NamedType {
+            name: "Boolean".to_string(),
+        };
+        let field_3 = FieldDef::new("catGrass".to_string(), ty_4);
+
+        let mut object_def = ObjectDef::new("PetStoreTrip".to_string());
+        object_def.field(field);
+        object_def.field(field_2);
+        object_def.field(field_3);
+        object_def.description(Some("Shopping list for cats at the pet store.".to_string()));
+        object_def.interface("ShoppingTrip".to_string());
+        object_def.directive(directive);
+
+        assert_eq!(
+            object_def.to_string(),
+            indoc! { r#"
+                "Shopping list for cats at the pet store."
+                type PetStoreTrip implements ShoppingTrip @testDirective(first: "one") {
+                  toys: [DanglerPoleToys] @deprecated(reason: "Cats are too spoiled")
+                  "Dry or wet food?"
+                  food: FoodType
+                  catGrass: Boolean
+                }
+            "#}
+        );
+    }
+
     #[test]
     fn it_encodes_object_with_block_string_description() {
         let ty_1 = Type_::NamedType {
             name: "String".to_string(),
         };
 
-        let mut field = Field::new("name".to_string(), ty_1);
+        let mut field = FieldDef::new("name".to_string(), ty_1);
         field.description(Some("multiline\ndescription".to_string()));
 
         let mut object_def = ObjectDef::new("Book".to_string());
diff --git a/crates/apollo-encoder/src/operation.rs b/crates/apollo-encoder/src/operation.rs
new file mode 100644
index 0000000..4a04a10
--- /dev/null
+++ b/crates/apollo-encoder/src/operation.rs
@@ -0,0 +1,295 @@
+use std::fmt;
+
+use crate::{Directive, SelectionSet, VariableDef};
+
+/// The __operationDef type represents an operation definition
+///
+/// *OperationDefinition*:
+///     OperationType Name? VariableDefinitions? Directives? SelectionSet
+///
+/// Detailed documentation can be found in [GraphQL spec](https://spec.graphql.org/October2021/#sec-Language.Operations).
+///
+/// ### Example
+/// ```rust
+/// use apollo_encoder::{Argument, Field, InlineFragment, Directive, OperationDef, OperationType, Selection, SelectionSet, TypeCondition, Type_, Value, VariableDef};
+/// use indoc::indoc;
+///
+/// let selection_set = {
+///     let sels = vec![
+///         Selection::Field(Field::new(String::from("first"))),
+///         Selection::Field(Field::new(String::from("second"))),
+///     ];
+///     let mut sel_set = SelectionSet::new();
+///     sels.into_iter().for_each(|sel| sel_set.selection(sel));
+///
+///     sel_set
+/// };
+/// let var_def = VariableDef::new(
+///     String::from("variable_def"),
+///     Type_::List {
+///         ty: Box::new(Type_::NamedType {
+///             name: String::from("Int"),
+///         }),
+///     },
+/// );
+/// let mut new_op = OperationDef::new(OperationType::Query, selection_set);
+/// let mut directive = Directive::new(String::from("testDirective"));
+/// directive.arg(Argument::new(
+///     String::from("first"),
+///     Value::String("one".to_string()),
+/// ));
+/// new_op.variable_definition(var_def);
+/// new_op.directive(directive);
+///
+/// assert_eq!(
+///     new_op.to_string(),
+///     indoc! { r#"
+///         query($variable_def: [Int]) @testDirective(first: "one") {
+///           first
+///           second
+///         }
+///     "#}
+/// );
+/// ```
+#[derive(Debug)]
+pub struct OperationDef {
+    operation_type: OperationType,
+    name: Option<String>,
+    variable_definitions: Vec<VariableDef>,
+    directives: Vec<Directive>,
+    selection_set: SelectionSet,
+    /// If a document contains only one operation and that operation is a query which defines no variables and
+    /// contains no directives then that operation may be represented in a short-hand form which omits the query keyword and operation name.
+    shorthand: bool,
+}
+
+impl OperationDef {
+    /// Create a new instance of OperationDef
+    pub fn new(operation_type: OperationType, selection_set: SelectionSet) -> Self {
+        Self {
+            operation_type,
+            selection_set,
+            name: None,
+            variable_definitions: Vec::new(),
+            directives: Vec::new(),
+            shorthand: false,
+        }
+    }
+
+    /// Set the operation def's name.
+    pub fn name(&mut self, name: Option<String>) {
+        self.name = name;
+    }
+
+    /// Add a variable definitions.
+    pub fn variable_definition(&mut self, variable_definition: VariableDef) {
+        self.variable_definitions.push(variable_definition);
+    }
+
+    /// Add a directive.
+    pub fn directive(&mut self, directive: Directive) {
+        self.directives.push(directive);
+    }
+
+    /// Set this operation as a query shorthand
+    /// If a document contains only one operation and that operation is a query which defines no variables and
+    /// contains no directives then that operation may be represented in a short-hand form which omits the query keyword and operation name.
+    /// Be careful, it will automatically drop variable definitions and directives
+    pub fn shorthand(&mut self) {
+        self.shorthand = true;
+    }
+}
+
+impl fmt::Display for OperationDef {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        let indent_level = 0;
+
+        if !self.shorthand {
+            write!(f, "{}", self.operation_type)?;
+            if let Some(name) = &self.name {
+                write!(f, " {}", name)?;
+            }
+
+            if !self.variable_definitions.is_empty() {
+                write!(f, "(")?;
+                for (i, var_def) in self.variable_definitions.iter().enumerate() {
+                    if i == self.variable_definitions.len() - 1 {
+                        write!(f, "{}", var_def)?;
+                    } else {
+                        write!(f, "{}, ", var_def)?;
+                    }
+                }
+                write!(f, ")")?;
+            }
+            for directive in &self.directives {
+                write!(f, " {}", directive)?;
+            }
+            write!(f, " ")?;
+        }
+
+        write!(f, "{}", self.selection_set.format_with_indent(indent_level))?;
+
+        Ok(())
+    }
+}
+
+/// The __operationType type represents the kind of operation
+///
+/// *OperationType*:
+///     query | mutation | subscription
+///
+/// Detailed documentation can be found in [GraphQL spec](https://spec.graphql.org/October2021/#OperationType).
+#[derive(Debug)]
+pub enum OperationType {
+    /// Represents a query operation
+    Query,
+    /// Represents a mutation operation
+    Mutation,
+    /// Represents a subscription operation
+    Subscription,
+}
+
+impl fmt::Display for OperationType {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match self {
+            OperationType::Query => write!(f, "query"),
+            OperationType::Mutation => write!(f, "mutation"),
+            OperationType::Subscription => write!(f, "subscription"),
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::{field::Field, Argument, FragmentSpread, Selection, Type_, Value};
+    use indoc::indoc;
+
+    #[test]
+    fn it_encodes_a_query_operation() {
+        let selection_set = {
+            let sels = vec![
+                Selection::Field(Field::new(String::from("first"))),
+                Selection::Field(Field::new(String::from("second"))),
+            ];
+            let mut sel_set = SelectionSet::new();
+            sels.into_iter().for_each(|sel| sel_set.selection(sel));
+
+            sel_set
+        };
+        let var_def = VariableDef::new(
+            String::from("variable_def"),
+            Type_::List {
+                ty: Box::new(Type_::NamedType {
+                    name: String::from("Int"),
+                }),
+            },
+        );
+        let mut new_op = OperationDef::new(OperationType::Query, selection_set);
+        let mut directive = Directive::new(String::from("testDirective"));
+        directive.arg(Argument::new(
+            String::from("first"),
+            Value::String("one".to_string()),
+        ));
+        new_op.variable_definition(var_def);
+        new_op.directive(directive);
+
+        assert_eq!(
+            new_op.to_string(),
+            indoc! { r#"
+                query($variable_def: [Int]) @testDirective(first: "one") {
+                  first
+                  second
+                }
+            "#}
+        );
+    }
+
+    #[test]
+    fn it_encodes_a_shorthand_query_operation() {
+        let selection_set = {
+            let sels = vec![
+                Selection::Field(Field::new(String::from("first"))),
+                Selection::Field(Field::new(String::from("second"))),
+            ];
+            let mut sel_set = SelectionSet::new();
+            sels.into_iter().for_each(|sel| sel_set.selection(sel));
+
+            sel_set
+        };
+        let mut new_op = OperationDef::new(OperationType::Query, selection_set);
+        new_op.shorthand();
+
+        assert_eq!(
+            new_op.to_string(),
+            indoc! { r#"
+                {
+                  first
+                  second
+                }
+            "#}
+        );
+    }
+
+    #[test]
+    fn it_encodes_a_deeper_query_operation() {
+        // ----- Selection set creation
+        let fourth_field = Field::new("fourth".to_string());
+        let mut third_field = Field::new("third".to_string());
+        third_field.selection_set(Some(SelectionSet::with_selections(vec![Selection::Field(
+            fourth_field,
+        )])));
+        let mut second_field = Field::new("second".to_string());
+        second_field.selection_set(Some(SelectionSet::with_selections(vec![Selection::Field(
+            third_field,
+        )])));
+
+        let mut first_field = Field::new("first".to_string());
+        first_field.selection_set(Some(SelectionSet::with_selections(vec![Selection::Field(
+            second_field,
+        )])));
+
+        let selections = vec![
+            Selection::Field(first_field),
+            Selection::FragmentSpread(FragmentSpread::new(String::from("myFragment"))),
+        ];
+        let mut selection_set = SelectionSet::new();
+        selections
+            .into_iter()
+            .for_each(|s| selection_set.selection(s));
+        // -------------------------
+
+        let var_def = VariableDef::new(
+            String::from("variable_def"),
+            Type_::List {
+                ty: Box::new(Type_::NamedType {
+                    name: String::from("Int"),
+                }),
+            },
+        );
+        let mut new_op = OperationDef::new(OperationType::Query, selection_set);
+        let mut directive = Directive::new(String::from("testDirective"));
+        directive.arg(Argument::new(
+            String::from("first"),
+            Value::String("one".to_string()),
+        ));
+        new_op.variable_definition(var_def);
+        new_op.directive(directive);
+
+        assert_eq!(
+            new_op.to_string(),
+            indoc! { r#"
+                query($variable_def: [Int]) @testDirective(first: "one") {
+                  first {
+                    second {
+                      third {
+                        fourth
+                      }
+                    }
+                  }
+                  ...myFragment
+                }
+            "#}
+        );
+    }
+}
diff --git a/crates/apollo-encoder/src/scalar_def.rs b/crates/apollo-encoder/src/scalar_def.rs
index 42c427b..f940e9b 100644
--- a/crates/apollo-encoder/src/scalar_def.rs
+++ b/crates/apollo-encoder/src/scalar_def.rs
@@ -1,6 +1,6 @@
 use std::fmt;
 
-use crate::StringValue;
+use crate::{Directive, StringValue};
 /// Represents scalar types such as Int, String, and Boolean.
 /// Scalars cannot have fields.
 ///
@@ -30,6 +30,8 @@ pub struct ScalarDef {
     name: String,
     // Description may return a String or null.
     description: StringValue,
+    directives: Vec<Directive>,
+    extend: bool,
 }
 
 impl ScalarDef {
@@ -38,6 +40,8 @@ impl ScalarDef {
         Self {
             name,
             description: StringValue::Top { source: None },
+            directives: Vec::new(),
+            extend: false,
         }
     }
 
@@ -47,17 +51,38 @@ impl ScalarDef {
             source: description,
         };
     }
+
+    /// Add a directive.
+    pub fn directive(&mut self, directive: Directive) {
+        self.directives.push(directive);
+    }
+
+    /// Set the scalar as an extension
+    pub fn extend(&mut self) {
+        self.extend = true;
+    }
 }
 
 impl fmt::Display for ScalarDef {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(f, "{}", self.description)?;
-        writeln!(f, "scalar {}", self.name)
+        if self.extend {
+            write!(f, "extend ")?;
+        } else {
+            write!(f, "{}", self.description)?;
+        }
+        write!(f, "scalar {}", self.name)?;
+        for directive in &self.directives {
+            write!(f, " {}", directive)?;
+        }
+        writeln!(f)
     }
 }
 
 #[cfg(test)]
 mod tests {
+
+    use crate::{Argument, Value};
+
     use super::*;
     use pretty_assertions::assert_eq;
 
@@ -82,6 +107,27 @@ mod tests {
             scalar.to_string(),
             r#""Int representing number of treats received."
 scalar NumberOfTreatsPerDay
+"#
+        );
+    }
+
+    #[test]
+    fn it_encodes_scalar_with_extend_directive() {
+        let mut scalar = ScalarDef::new("NumberOfTreatsPerDay".to_string());
+        scalar.description(Some(
+            "Int representing number of treats received.".to_string(),
+        ));
+        scalar.extend();
+        let mut directive = Directive::new(String::from("testDirective"));
+        directive.arg(Argument::new(
+            String::from("first"),
+            Value::String("one".to_string()),
+        ));
+        scalar.directive(directive);
+
+        assert_eq!(
+            scalar.to_string(),
+            r#"extend scalar NumberOfTreatsPerDay @testDirective(first: "one")
 "#
         );
     }
diff --git a/crates/apollo-encoder/src/schema.rs b/crates/apollo-encoder/src/schema.rs
index 12cada7..d26fdfb 100644
--- a/crates/apollo-encoder/src/schema.rs
+++ b/crates/apollo-encoder/src/schema.rs
@@ -1,5 +1,5 @@
 use crate::{
-    Directive, EnumDef, InputObjectDef, InterfaceDef, ObjectDef, ScalarDef, SchemaDef, UnionDef,
+    DirectiveDef, EnumDef, InputObjectDef, InterfaceDef, ObjectDef, ScalarDef, SchemaDef, UnionDef,
 };
 
 /// GraphQLSchema represented in Schema Definition Language.
@@ -33,6 +33,7 @@ use crate::{
 /// );
 /// ```
 #[derive(Debug)]
+// TODO delete in favour of Document
 pub struct Schema {
     buf: String,
 }
@@ -44,7 +45,7 @@ impl Schema {
     }
 
     /// Adds a new Directive Definition.
-    pub fn directive(&mut self, directive: Directive) {
+    pub fn directive(&mut self, directive: DirectiveDef) {
         self.buf.push_str(&directive.to_string());
     }
 
@@ -101,8 +102,8 @@ impl Default for Schema {
 #[cfg(test)]
 mod tests {
     use crate::{
-        Directive, EnumDef, EnumValue, Field, InputField, InputObjectDef, ObjectDef, ScalarDef,
-        Schema, SchemaDef, Type_, UnionDef,
+        Argument, Directive, DirectiveDef, EnumDef, EnumValue, FieldDef, InputField,
+        InputObjectDef, ObjectDef, ScalarDef, Schema, SchemaDef, Type_, UnionDef, Value,
     };
     use indoc::indoc;
     use pretty_assertions::assert_eq;
@@ -112,7 +113,7 @@ mod tests {
         let mut schema = Schema::new();
 
         // create a directive
-        let mut directive = Directive::new("provideTreat".to_string());
+        let mut directive = DirectiveDef::new("provideTreat".to_string());
         directive.description(Some("Ensures cats get treats.".to_string()));
         directive.location("OBJECT".to_string());
         directive.location("FIELD_DEFINITION".to_string());
@@ -133,7 +134,7 @@ mod tests {
             ty: Box::new(field_value),
         };
 
-        let mut field = Field::new("cat".to_string(), null_field);
+        let mut field = FieldDef::new("cat".to_string(), null_field);
         field.description(Some("Very good cats".to_string()));
 
         // Union Definition
@@ -152,20 +153,25 @@ mod tests {
             ty: Box::new(object_value),
         };
 
-        let mut object_field = Field::new("toys".to_string(), object_value_2);
-        object_field.deprecated(Some("Cats are too spoiled".to_string()));
+        let mut object_field = FieldDef::new("toys".to_string(), object_value_2);
+        let mut deprecated_directive = Directive::new(String::from("deprecated"));
+        deprecated_directive.arg(Argument::new(
+            String::from("reason"),
+            Value::String(String::from("Cats are too spoiled")),
+        ));
+        object_field.directive(deprecated_directive);
 
         let object_value_2 = Type_::NamedType {
             name: "FoodType".to_string(),
         };
 
-        let mut object_field_2 = Field::new("food".to_string(), object_value_2);
+        let mut object_field_2 = FieldDef::new("food".to_string(), object_value_2);
         object_field_2.description(Some("Dry or wet food?".to_string()));
 
         let object_field_3 = Type_::NamedType {
             name: "Boolean".to_string(),
         };
-        let object_field_3 = Field::new("catGrass".to_string(), object_field_3);
+        let object_field_3 = FieldDef::new("catGrass".to_string(), object_field_3);
 
         let mut object_def = ObjectDef::new("PetStoreTrip".to_string());
         object_def.field(object_field);
@@ -179,7 +185,12 @@ mod tests {
         enum_ty_1.description(Some("Top bunk of a cat tree.".to_string()));
         let enum_ty_2 = EnumValue::new("BED".to_string());
         let mut enum_ty_3 = EnumValue::new("CARDBOARD_BOX".to_string());
-        enum_ty_3.deprecated(Some("Box was recycled.".to_string()));
+        let mut deprecated_directive = Directive::new(String::from("deprecated"));
+        deprecated_directive.arg(Argument::new(
+            String::from("reason"),
+            Value::String(String::from("Box was recycled.")),
+        ));
+        enum_ty_3.directive(deprecated_directive);
 
         let mut enum_def = EnumDef::new("NapSpots".to_string());
         enum_def.description(Some("Favourite cat nap spots.".to_string()));
diff --git a/crates/apollo-encoder/src/schema_def.rs b/crates/apollo-encoder/src/schema_def.rs
index a9fb538..579d890 100644
--- a/crates/apollo-encoder/src/schema_def.rs
+++ b/crates/apollo-encoder/src/schema_def.rs
@@ -1,6 +1,6 @@
 use std::fmt;
 
-use crate::StringValue;
+use crate::{Directive, StringValue};
 
 /// A GraphQL service’s collective type system capabilities are referred to as that service’s “schema”.
 ///
@@ -40,6 +40,9 @@ pub struct SchemaDef {
     query: Option<String>,
     mutation: Option<String>,
     subscription: Option<String>,
+    directives: Vec<Directive>,
+    // Extend a schema
+    extend: bool,
 }
 
 impl SchemaDef {
@@ -50,6 +53,8 @@ impl SchemaDef {
             query: None,
             mutation: None,
             subscription: None,
+            directives: Vec::new(),
+            extend: false,
         }
     }
 
@@ -60,6 +65,16 @@ impl SchemaDef {
         };
     }
 
+    /// Add a directive.
+    pub fn directive(&mut self, directive: Directive) {
+        self.directives.push(directive);
+    }
+
+    /// Set as an extension
+    pub fn extend(&mut self) {
+        self.extend = true;
+    }
+
     /// Set the schema def's query type.
     pub fn query(&mut self, query: String) {
         self.query = Some(query);
@@ -84,9 +99,21 @@ impl Default for SchemaDef {
 
 impl fmt::Display for SchemaDef {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(f, "{}", self.description)?;
+        if self.extend {
+            write!(f, "extend ")?;
+        } else {
+            // No description when it's an extension schema
+            write!(f, "{}", self.description)?;
+        }
+
+        write!(f, "schema")?;
+
+        for directive in &self.directives {
+            write!(f, " {}", directive)?;
+        }
+
+        writeln!(f, " {{")?;
 
-        writeln!(f, "schema {{")?;
         if let Some(query) = &self.query {
             writeln!(f, "  query: {}", query)?;
         }
@@ -127,4 +154,24 @@ mod tests {
         "#}
         );
     }
+
+    #[test]
+    fn it_encodes_extend_schema_with_mutation_and_subscription() {
+        let mut schema_def = SchemaDef::new();
+        schema_def.query("TryingToFindCatQuery".to_string());
+        schema_def.mutation("MyMutation".to_string());
+        schema_def.subscription("MySubscription".to_string());
+        schema_def.extend();
+
+        assert_eq!(
+            schema_def.to_string(),
+            indoc! { r#"
+            extend schema {
+              query: TryingToFindCatQuery
+              mutation: MyMutation
+              subscription: MySubscription
+            }
+        "#}
+        );
+    }
 }
diff --git a/crates/apollo-encoder/src/selection_set.rs b/crates/apollo-encoder/src/selection_set.rs
new file mode 100644
index 0000000..756b3ec
--- /dev/null
+++ b/crates/apollo-encoder/src/selection_set.rs
@@ -0,0 +1,204 @@
+use crate::{Field, FragmentSpread, InlineFragment};
+use std::fmt;
+
+/// The __selectionSet type represents a selection_set type in a fragment spread, an operation or a field
+///
+/// *SelectionSet*:
+///     Selection*
+///
+/// Detailed documentation can be found in [GraphQL spec](https://spec.graphql.org/October2021/#sec-Selection-Sets).
+///
+/// ### Example
+/// ```rust
+/// use apollo_encoder::{Field, FragmentSpread, Selection, SelectionSet, TypeCondition};
+/// use indoc::indoc;
+///
+/// let selections = vec![
+///     Selection::Field(Field::new(String::from("myField"))),
+///     Selection::FragmentSpread(FragmentSpread::new(String::from("myFragment"))),
+/// ];
+/// let mut selection_set = SelectionSet::new();
+/// selections
+///     .into_iter()
+///     .for_each(|s| selection_set.selection(s));
+///
+/// assert_eq!(
+///     selection_set.to_string(),
+///     indoc! {r#"
+///         {
+///           myField
+///           ...myFragment
+///         }
+///     "#}
+/// )
+/// ```
+#[derive(Debug, PartialEq, Clone, Default)]
+pub struct SelectionSet {
+    selections: Vec<Selection>,
+}
+
+impl SelectionSet {
+    /// Create an instance of SelectionSet
+    pub fn new() -> Self {
+        Self::default()
+    }
+
+    /// Create an instance of SelectionSet given its selections
+    pub fn with_selections(selections: Vec<Selection>) -> Self {
+        Self { selections }
+    }
+
+    /// Add a selection in the SelectionSet
+    pub fn selection(&mut self, selection: Selection) {
+        self.selections.push(selection);
+    }
+
+    /// Should be used everywhere in this crate isntead of the Display implementation
+    /// Display implementation is only useful as a public api
+    pub(crate) fn format_with_indent(&self, mut indent_level: usize) -> String {
+        let mut text = String::from("{\n");
+        indent_level += 1;
+        let indent = "  ".repeat(indent_level);
+        for sel in &self.selections {
+            text.push_str(&format!(
+                "{}{}\n",
+                indent,
+                sel.format_with_indent(indent_level)
+            ));
+        }
+        if indent_level <= 1 {
+            text.push_str("}\n");
+        } else {
+            text.push_str(&format!("{}}}", "  ".repeat(indent_level - 1)));
+        }
+
+        text
+    }
+}
+
+// This impl is only useful when we generate only a SelectionSet
+// If it's used from a parent element, we call `format_with_indent`
+impl fmt::Display for SelectionSet {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        let indent_level = 1;
+        writeln!(f, "{{")?;
+        let indent = "  ".repeat(indent_level);
+        for sel in &self.selections {
+            writeln!(f, "{}{}", indent, sel.format_with_indent(indent_level))?;
+        }
+        writeln!(f, "}}")?;
+
+        Ok(())
+    }
+}
+
+/// The __selection type represents a selection in a selection set
+/// *Selection*:
+///     Field | FragmentSpread | InlineFragment
+///
+/// Detailed documentation can be found in [GraphQL spec](https://spec.graphql.org/October2021/#Selection).
+#[derive(Debug, Clone, PartialEq)]
+pub enum Selection {
+    /// Represents a field
+    Field(Field),
+    /// Represents a fragment spread
+    FragmentSpread(FragmentSpread),
+    /// Represents an inline fragment
+    InlineFragment(InlineFragment),
+}
+
+impl Selection {
+    pub(crate) fn format_with_indent(&self, indent_level: usize) -> String {
+        match self {
+            Selection::Field(field) => field.format_with_indent(indent_level),
+            Selection::FragmentSpread(frag_spread) => frag_spread.to_string(),
+            Selection::InlineFragment(inline_frag) => inline_frag.format_with_indent(indent_level),
+        }
+    }
+}
+
+impl fmt::Display for Selection {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        let indent_level = 0;
+        match self {
+            Selection::Field(field) => write!(f, "{}", field.format_with_indent(indent_level)),
+            Selection::FragmentSpread(fragment_spread) => write!(f, "{fragment_spread}"),
+            Selection::InlineFragment(inline_fragment) => {
+                write!(f, "{}", inline_fragment.format_with_indent(indent_level))
+            }
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use indoc::indoc;
+
+    use super::*;
+
+    #[test]
+    fn it_encodes_selection_set() {
+        let selections = vec![
+            Selection::Field(Field::new(String::from("myField"))),
+            Selection::FragmentSpread(FragmentSpread::new(String::from("myFragment"))),
+        ];
+        let mut selection_set = SelectionSet::new();
+        selections
+            .into_iter()
+            .for_each(|s| selection_set.selection(s));
+
+        assert_eq!(
+            selection_set.to_string(),
+            indoc! {r#"
+                {
+                  myField
+                  ...myFragment
+                }
+            "#}
+        )
+    }
+
+    #[test]
+    fn it_encodes_deeper_selection_set() {
+        let fourth_field = Field::new("fourth".to_string());
+        let mut third_field = Field::new("third".to_string());
+        third_field.selection_set(Some(SelectionSet::with_selections(vec![Selection::Field(
+            fourth_field,
+        )])));
+        let mut second_field = Field::new("second".to_string());
+        second_field.selection_set(Some(SelectionSet::with_selections(vec![Selection::Field(
+            third_field,
+        )])));
+
+        let mut first_field = Field::new("first".to_string());
+        first_field.selection_set(Some(SelectionSet::with_selections(vec![Selection::Field(
+            second_field,
+        )])));
+
+        let selections = vec![
+            Selection::Field(first_field),
+            Selection::FragmentSpread(FragmentSpread::new(String::from("myFragment"))),
+        ];
+        let mut selection_set = SelectionSet::new();
+
+        selections
+            .into_iter()
+            .for_each(|s| selection_set.selection(s));
+
+        assert_eq!(
+            selection_set.to_string(),
+            indoc! {r#"
+                {
+                  first {
+                    second {
+                      third {
+                        fourth
+                      }
+                    }
+                  }
+                  ...myFragment
+                }
+            "#}
+        )
+    }
+}
diff --git a/crates/apollo-encoder/src/union_def.rs b/crates/apollo-encoder/src/union_def.rs
index d142a0b..eeb43eb 100644
--- a/crates/apollo-encoder/src/union_def.rs
+++ b/crates/apollo-encoder/src/union_def.rs
@@ -1,6 +1,6 @@
 use std::fmt;
 
-use crate::StringValue;
+use crate::{Directive, StringValue};
 
 /// UnionDefs are an abstract type where no common fields are declared.
 ///
@@ -31,6 +31,9 @@ pub struct UnionDef {
     description: StringValue,
     // The vector of members that can be represented within this union.
     members: Vec<String>,
+    /// Contains all directives.
+    directives: Vec<Directive>,
+    extend: bool,
 }
 
 impl UnionDef {
@@ -40,9 +43,16 @@ impl UnionDef {
             name,
             description: StringValue::Top { source: None },
             members: Vec::new(),
+            extend: false,
+            directives: Vec::new(),
         }
     }
 
+    /// Set the union type as an extension
+    pub fn extend(&mut self) {
+        self.extend = true;
+    }
+
     /// Set the UnionDefs description.
     pub fn description(&mut self, description: Option<String>) {
         self.description = StringValue::Top {
@@ -50,6 +60,11 @@ impl UnionDef {
         };
     }
 
+    /// Add a directive
+    pub fn directive(&mut self, directive: Directive) {
+        self.directives.push(directive);
+    }
+
     /// Set a UnionDef member.
     pub fn member(&mut self, member: String) {
         self.members.push(member);
@@ -58,13 +73,24 @@ impl UnionDef {
 
 impl fmt::Display for UnionDef {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(f, "{}", self.description)?;
+        if self.extend {
+            write!(f, "extend ")?;
+        } else {
+            // No description when it's a extension
+            write!(f, "{}", self.description)?;
+        }
 
-        write!(f, "union {} = ", self.name)?;
+        write!(f, "union {}", self.name)?;
+
+        for directive in &self.directives {
+            write!(f, " {}", directive)?;
+        }
+
+        write!(f, " =")?;
 
         for (i, member) in self.members.iter().enumerate() {
             match i {
-                0 => write!(f, "{}", member)?,
+                0 => write!(f, " {}", member)?,
                 _ => write!(f, " | {}", member)?,
             }
         }
@@ -76,7 +102,6 @@ impl fmt::Display for UnionDef {
 #[cfg(test)]
 mod tests {
     use super::*;
-    // use indoc::indoc;
     use pretty_assertions::assert_eq;
 
     #[test]
@@ -90,6 +115,21 @@ mod tests {
             union_.to_string(),
             r#""A union of all animals in a household."
 union Pet = Cat | Dog
+"#
+        );
+    }
+
+    #[test]
+    fn it_encodes_union_extension() {
+        let mut union_ = UnionDef::new("Pet".to_string());
+        union_.description(Some("A union of all animals in a household.".to_string()));
+        union_.member("Cat".to_string());
+        union_.member("Dog".to_string());
+        union_.extend();
+
+        assert_eq!(
+            union_.to_string(),
+            r#"extend union Pet = Cat | Dog
 "#
         );
     }
diff --git a/crates/apollo-encoder/src/value.rs b/crates/apollo-encoder/src/value.rs
new file mode 100644
index 0000000..ee2a96c
--- /dev/null
+++ b/crates/apollo-encoder/src/value.rs
@@ -0,0 +1,114 @@
+use std::fmt;
+
+/// The __value type represents available values you could give as an input.
+///
+/// *Value*:
+///     Variable | IntValue | FloatValue | StringValue | BooleanValue | NullValue | EnumValue | ListValue | ObjectValue
+///
+/// Detailed documentation can be found in [GraphQL spec](https://spec.graphql.org/October2021/#Value).
+#[derive(Debug, PartialEq, Clone)]
+pub enum Value {
+    /// Name of a variable example: `varName`
+    Variable(String),
+    /// Int value example: `7`
+    Int(i64),
+    /// Float value example: `25.4`
+    Float(f64),
+    /// String value example: `"My string"`
+    String(String),
+    /// Boolean value example: `false`
+    Boolean(bool),
+    /// Null value example: `null`
+    Null,
+    /// Enum value example: `"VARIANT_EXAMPLE"`
+    Enum(String),
+    /// List value example: `[1, 2, 3]`
+    List(Vec<Value>),
+    /// Object value example: `{ first: 1, second: 2 }`
+    Object(Vec<(String, Value)>),
+}
+
+impl fmt::Display for Value {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match self {
+            Self::Variable(v) => write!(f, "${v}"),
+            Self::Int(i) => write!(f, "{i}"),
+            Self::Float(fl) => write!(f, "{fl}"),
+            Self::String(s) => {
+                if s.contains('"') | s.contains('\n') | s.contains('\r') {
+                    write!(f, r#""""{s}""""#)
+                } else {
+                    write!(f, r#""{s}""#)
+                }
+            }
+            Self::Boolean(b) => write!(f, "{b}"),
+            Self::Null => write!(f, "null"),
+            Self::Enum(val) => write!(f, "{val}"),
+            Self::List(list) => write!(
+                f,
+                "[{}]",
+                list.iter()
+                    .map(|elt| format!("{elt}"))
+                    .collect::<Vec<String>>()
+                    .join(", ")
+            ),
+            Self::Object(obj) => write!(
+                f,
+                "{{ {} }}",
+                obj.iter()
+                    .map(|(k, v)| format!("{}: {v}", String::from(k)))
+                    .collect::<Vec<String>>()
+                    .join(", ")
+            ),
+        }
+    }
+}
+
+macro_rules! to_number_value {
+    ($ty: path, $inner_type: path, $value_variant: ident) => {
+        impl From<$ty> for Value {
+            fn from(val: $ty) -> Self {
+                Self::$value_variant(val as $inner_type)
+            }
+        }
+    };
+    ($({$ty: path, $inner_type: path, $value_variant: ident}),+) => {
+        $(
+            to_number_value!($ty, $inner_type, $value_variant);
+        )+
+    };
+}
+
+// Numbers
+to_number_value!(
+    {i64, i64, Int},
+    {i32, i64, Int},
+    {i16, i64, Int},
+    {i8, i64, Int},
+    {isize, i64, Int},
+    {u64, i64, Int},
+    {u32, i64, Int},
+    {u16, i64, Int},
+    {u8, i64, Int},
+    {usize, i64, Int},
+    {f64, f64, Float},
+    {f32, f64, Float}
+);
+
+impl From<String> for Value {
+    fn from(val: String) -> Self {
+        Self::String(val)
+    }
+}
+
+impl From<&str> for Value {
+    fn from(val: &str) -> Self {
+        Self::String(val.to_string())
+    }
+}
+
+impl From<bool> for Value {
+    fn from(val: bool) -> Self {
+        Self::Boolean(val)
+    }
+}
diff --git a/crates/apollo-encoder/src/variable.rs b/crates/apollo-encoder/src/variable.rs
new file mode 100644
index 0000000..478a1d6
--- /dev/null
+++ b/crates/apollo-encoder/src/variable.rs
@@ -0,0 +1,100 @@
+use std::fmt;
+
+use crate::{Directive, Type_, Value};
+
+/// The __variableDef type represents a variable definition
+///
+/// *VariableDefinition*:
+///     VariableName : Type DefaultValue? Directives?
+///
+/// Detailed documentation can be found in [GraphQL spec](https://spec.graphql.org/October2021/#sec-Language.Variables).
+///
+/// ### Example
+/// ```rust
+/// use apollo_encoder::{Type_, Value, VariableDef};
+///
+/// let mut variable = VariableDef::new(
+///     String::from("my_var"),
+///     Type_::NamedType {
+///         name: String::from("MyType"),
+///     },
+/// );
+/// variable.default_value(Some(Value::Object(vec![
+///     (String::from("first"), Value::Int(25)),
+///     (String::from("second"), Value::String(String::from("test"))),
+/// ])));
+///
+/// assert_eq!(
+///     variable.to_string(),
+///     String::from(r#"$my_var: MyType = { first: 25, second: "test" }"#)
+/// );
+/// ```
+#[derive(Debug)]
+pub struct VariableDef {
+    variable: String,
+    ty: Type_,
+    default_value: Option<Value>,
+    directives: Vec<Directive>,
+}
+
+impl VariableDef {
+    /// Create an instance of VariableDef
+    pub fn new(variable: String, ty: Type_) -> Self {
+        Self {
+            variable,
+            ty,
+            default_value: Option::default(),
+            directives: Vec::new(),
+        }
+    }
+
+    /// Set a default value to the variable
+    pub fn default_value(&mut self, default_value: Option<Value>) {
+        self.default_value = default_value;
+    }
+
+    /// Add a directive
+    pub fn directive(&mut self, directive: Directive) {
+        self.directives.push(directive);
+    }
+}
+
+impl fmt::Display for VariableDef {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f, "${}: {}", self.variable, self.ty)?;
+
+        if let Some(default_value) = &self.default_value {
+            write!(f, " = {}", default_value)?;
+        }
+
+        for directive in &self.directives {
+            write!(f, " {}", directive)?;
+        }
+
+        Ok(())
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn it_encodes_variable_definition() {
+        let mut variable = VariableDef::new(
+            String::from("my_var"),
+            Type_::NamedType {
+                name: String::from("MyType"),
+            },
+        );
+        variable.default_value(Some(Value::Object(vec![
+            (String::from("first"), Value::Int(25)),
+            (String::from("second"), Value::String(String::from("test"))),
+        ])));
+
+        assert_eq!(
+            variable.to_string(),
+            String::from(r#"$my_var: MyType = { first: 25, second: "test" }"#)
+        );
+    }
+}
diff --git a/crates/apollo-parser/src/parser/grammar/selection.rs b/crates/apollo-parser/src/parser/grammar/selection.rs
index 8980788..e9054e5 100644
--- a/crates/apollo-parser/src/parser/grammar/selection.rs
+++ b/crates/apollo-parser/src/parser/grammar/selection.rs
@@ -28,14 +28,22 @@ pub(crate) fn selection(p: &mut Parser) {
     while let Some(node) = p.peek() {
         match node {
             T![...] => {
-                if let Some(node) = p.peek_data_n(2) {
-                    match node.as_str() {
-                        "on" | "{" => fragment::inline_fragment(p),
-                        _ => fragment::fragment_spread(p),
+                let next_token = p.peek_token_n(2);
+                match next_token {
+                    Some(next_token) => {
+                        if next_token.kind() == TokenKind::Name && next_token.data() != "on" {
+                            fragment::fragment_spread(p);
+                        } else if matches!(
+                            next_token.kind(),
+                            TokenKind::At | TokenKind::Name | TokenKind::LCurly
+                        ) {
+                            fragment::inline_fragment(p);
+                        } else {
+                            p.err("expected an Inline Fragment or a Fragment Spread");
+                        }
+                        has_selection = true;
                     }
-                    has_selection = true;
-                } else {
-                    p.err("expected an Inline Fragment or a Fragment Spread");
+                    None => p.err("expected an Inline Fragment or a Fragment Spread"),
                 }
             }
             T!['{'] => {
@@ -47,7 +55,7 @@ pub(crate) fn selection(p: &mut Parser) {
             }
             _ => {
                 if !has_selection {
-                    p.err("exepcted at least one Selection in Selection Set");
+                    p.err("expected at least one Selection in Selection Set");
                 }
                 break;
             }
diff --git a/crates/apollo-parser/src/parser/mod.rs b/crates/apollo-parser/src/parser/mod.rs
index 5caffaf..dcb2337 100644
--- a/crates/apollo-parser/src/parser/mod.rs
+++ b/crates/apollo-parser/src/parser/mod.rs
@@ -244,6 +244,15 @@ impl Parser {
         self.tokens.last()
     }
 
+    /// Peek Token `n` and return it.
+    pub(crate) fn peek_token_n(&self, n: usize) -> Option<&Token> {
+        self.tokens
+            .iter()
+            .rev()
+            .filter(|token| !matches!(token.kind(), TokenKind::Whitespace | TokenKind::Comment))
+            .nth(n - 1)
+    }
+
     /// Peek Token `n` and return its TokenKind.
     pub(crate) fn peek_n(&self, n: usize) -> Option<TokenKind> {
         self.tokens


Test patch:
diff --git a/crates/apollo-parser/src/tests.rs b/crates/apollo-parser/src/tests.rs
index fc0a649..047af0a 100644
--- a/crates/apollo-parser/src/tests.rs
+++ b/crates/apollo-parser/src/tests.rs
@@ -62,11 +62,16 @@ fn assert_errors_are_present(errors: Iter<'_, Error>, path: &Path) {
 }
 
 fn assert_errors_are_absent(errors: Iter<'_, Error>, path: &Path) {
-    assert!(
-        errors.len() == 0,
-        "There should be no errors in the file {:?}",
-        path.display(),
-    );
+    if errors.len() > 0 {
+        println!(
+            "errors: {}",
+            errors
+                .map(|e| e.message())
+                .collect::<Vec<&str>>()
+                .join("\n")
+        );
+        panic!("There should be no errors in the file {:?}", path.display(),);
+    }
 }
 
 /// Concatenate tokens and errors.
diff --git a/crates/apollo-parser/test_data/parser/err/0029_operation_definition_with_empty_selection_set.txt b/crates/apollo-parser/test_data/parser/err/0029_operation_definition_with_empty_selection_set.txt
index d26a698..bd39311 100644
--- a/crates/apollo-parser/test_data/parser/err/0029_operation_definition_with_empty_selection_set.txt
+++ b/crates/apollo-parser/test_data/parser/err/0029_operation_definition_with_empty_selection_set.txt
@@ -6,4 +6,4 @@
         - SELECTION_SET@6..8
             - L_CURLY@6..7 "{"
             - R_CURLY@7..8 "}"
-- ERROR@7:8 "exepcted at least one Selection in Selection Set" }
+- ERROR@7:8 "expected at least one Selection in Selection Set" }
diff --git a/crates/apollo-parser/test_data/parser/err/0030_operation_definition_with_description.txt b/crates/apollo-parser/test_data/parser/err/0030_operation_definition_with_description.txt
index 049588b..a370379 100644
--- a/crates/apollo-parser/test_data/parser/err/0030_operation_definition_with_description.txt
+++ b/crates/apollo-parser/test_data/parser/err/0030_operation_definition_with_description.txt
@@ -11,4 +11,4 @@
             - L_CURLY@13..14 "{"
             - R_CURLY@14..15 "}"
 - ERROR@0:93 "expected an Operation Type or a Selection Set" "after this PR this should not be an issue: https://github.com/graphql/graphql-spec/pull/892"
-- ERROR@107:108 "exepcted at least one Selection in Selection Set" }
+- ERROR@107:108 "expected at least one Selection in Selection Set" }
diff --git a/crates/apollo-parser/test_data/parser/ok/0006_selection_with_fragment_spread.graphql b/crates/apollo-parser/test_data/parser/ok/0006_selection_with_fragment_spread.graphql
index 86a36b3..cd0f34b 100644
--- a/crates/apollo-parser/test_data/parser/ok/0006_selection_with_fragment_spread.graphql
+++ b/crates/apollo-parser/test_data/parser/ok/0006_selection_with_fragment_spread.graphql
@@ -1,4 +1,15 @@
 {
-    pet
-    ...snackSelection
-}
\ No newline at end of file
+  pet
+  ...snackSelection
+  ... on Nap {
+    cozyLocation
+    durationOfNap
+  }
+  ...snackSelection @deprecated
+  ... on Nap @provides(duration: "2 hours") {
+    cozyLocation
+  }
+  ... @J(N: 0) {
+    a
+  }
+}
diff --git a/crates/apollo-parser/test_data/parser/ok/0006_selection_with_fragment_spread.txt b/crates/apollo-parser/test_data/parser/ok/0006_selection_with_fragment_spread.txt
index 18a22a1..c552918 100644
--- a/crates/apollo-parser/test_data/parser/ok/0006_selection_with_fragment_spread.txt
+++ b/crates/apollo-parser/test_data/parser/ok/0006_selection_with_fragment_spread.txt
@@ -1,16 +1,115 @@
-- DOCUMENT@0..33
-    - OPERATION_DEFINITION@0..33
-        - SELECTION_SET@0..33
+- DOCUMENT@0..210
+    - OPERATION_DEFINITION@0..210
+        - SELECTION_SET@0..210
             - L_CURLY@0..1 "{"
-            - WHITESPACE@1..6 "\n    "
-            - FIELD@6..14
-                - NAME@6..14
-                    - IDENT@6..9 "pet"
-                    - WHITESPACE@9..14 "\n    "
-            - FRAGMENT_SPREAD@14..32
-                - SPREAD@14..17 "..."
-                - FRAGMENT_NAME@17..32
-                    - NAME@17..32
-                        - IDENT@17..31 "snackSelection"
-                        - WHITESPACE@31..32 "\n"
-            - R_CURLY@32..33 "}"
+            - WHITESPACE@1..4 "\n  "
+            - FIELD@4..10
+                - NAME@4..10
+                    - IDENT@4..7 "pet"
+                    - WHITESPACE@7..10 "\n  "
+            - FRAGMENT_SPREAD@10..30
+                - SPREAD@10..13 "..."
+                - FRAGMENT_NAME@13..30
+                    - NAME@13..30
+                        - IDENT@13..27 "snackSelection"
+                        - WHITESPACE@27..30 "\n  "
+            - INLINE_FRAGMENT@30..84
+                - SPREAD@30..33 "..."
+                - WHITESPACE@33..34 " "
+                - TYPE_CONDITION@34..41
+                    - on_KW@34..36 "on"
+                    - WHITESPACE@36..37 " "
+                    - NAMED_TYPE@37..41
+                        - NAME@37..41
+                            - IDENT@37..40 "Nap"
+                            - WHITESPACE@40..41 " "
+                - SELECTION_SET@41..84
+                    - L_CURLY@41..42 "{"
+                    - WHITESPACE@42..47 "\n    "
+                    - FIELD@47..64
+                        - NAME@47..64
+                            - IDENT@47..59 "cozyLocation"
+                            - WHITESPACE@59..64 "\n    "
+                    - FIELD@64..80
+                        - NAME@64..80
+                            - IDENT@64..77 "durationOfNap"
+                            - WHITESPACE@77..80 "\n  "
+                    - R_CURLY@80..81 "}"
+                    - WHITESPACE@81..84 "\n  "
+            - FRAGMENT_SPREAD@84..116
+                - SPREAD@84..87 "..."
+                - FRAGMENT_NAME@87..102
+                    - NAME@87..102
+                        - IDENT@87..101 "snackSelection"
+                        - WHITESPACE@101..102 " "
+                - DIRECTIVES@102..116
+                    - DIRECTIVE@102..116
+                        - AT@102..103 "@"
+                        - NAME@103..116
+                            - IDENT@103..113 "deprecated"
+                            - WHITESPACE@113..116 "\n  "
+            - INLINE_FRAGMENT@116..183
+                - SPREAD@116..119 "..."
+                - WHITESPACE@119..120 " "
+                - TYPE_CONDITION@120..127
+                    - on_KW@120..122 "on"
+                    - WHITESPACE@122..123 " "
+                    - NAMED_TYPE@123..127
+                        - NAME@123..127
+                            - IDENT@123..126 "Nap"
+                            - WHITESPACE@126..127 " "
+                - DIRECTIVES@127..158
+                    - DIRECTIVE@127..158
+                        - AT@127..128 "@"
+                        - NAME@128..136
+                            - IDENT@128..136 "provides"
+                        - ARGUMENTS@136..158
+                            - L_PAREN@136..137 "("
+                            - ARGUMENT@137..156
+                                - NAME@137..145
+                                    - IDENT@137..145 "duration"
+                                - COLON@145..146 ":"
+                                - WHITESPACE@146..147 " "
+                                - STRING_VALUE@147..156
+                                    - STRING@147..156 "\"2 hours\""
+                            - R_PAREN@156..157 ")"
+                            - WHITESPACE@157..158 " "
+                - SELECTION_SET@158..183
+                    - L_CURLY@158..159 "{"
+                    - WHITESPACE@159..164 "\n    "
+                    - FIELD@164..179
+                        - NAME@164..179
+                            - IDENT@164..176 "cozyLocation"
+                            - WHITESPACE@176..179 "\n  "
+                    - R_CURLY@179..180 "}"
+                    - WHITESPACE@180..183 "\n  "
+            - INLINE_FRAGMENT@183..208
+                - SPREAD@183..186 "..."
+                - WHITESPACE@186..187 " "
+                - DIRECTIVES@187..196
+                    - DIRECTIVE@187..196
+                        - AT@187..188 "@"
+                        - NAME@188..189
+                            - IDENT@188..189 "J"
+                        - ARGUMENTS@189..196
+                            - L_PAREN@189..190 "("
+                            - ARGUMENT@190..194
+                                - NAME@190..191
+                                    - IDENT@190..191 "N"
+                                - COLON@191..192 ":"
+                                - WHITESPACE@192..193 " "
+                                - INT_VALUE@193..194
+                                    - INT@193..194 "0"
+                            - R_PAREN@194..195 ")"
+                            - WHITESPACE@195..196 " "
+                - SELECTION_SET@196..208
+                    - L_CURLY@196..197 "{"
+                    - WHITESPACE@197..202 "\n    "
+                    - FIELD@202..206
+                        - NAME@202..206
+                            - IDENT@202..203 "a"
+                            - WHITESPACE@203..206 "\n  "
+                    - R_CURLY@206..207 "}"
+                    - WHITESPACE@207..208 "\n"
+            - R_CURLY@208..209 "}"
+            - WHITESPACE@209..210 "\n"