From 715907eb8e3eeb650cd49c6116c0e96d2c8ceddb Mon Sep 17 00:00:00 2001
From: Sandipsinh Dilipsinh Rathod
 <62684960+ssddOnTop@users.noreply.github.com>
Date: Thu, 11 Jul 2024 21:45:14 +0530
Subject: [PATCH] fix(jit): implement `skip` and `include` (#2357)

Co-authored-by: Tushar Mathur <tusharmath@gmail.com>
---
 Cargo.lock                                 | 151 +++++++++++----------
 src/core/jit/builder.rs                    |  93 ++++++++++++-
 src/core/jit/common/json_placeholder.rs    |   4 +-
 src/core/jit/exec.rs                       |   7 +-
 src/core/jit/model.rs                      |  63 +++++++++
 src/core/jit/request.rs                    |  11 +-
 src/core/jit/synth/mod.rs                  |  10 +-
 src/core/jit/synth/synth_borrow.rs         |   7 +-
 src/core/jit/synth/synth_const.rs          |  92 ++++++++-----
 tests/jit_spec.rs                          |  26 ++++
 tests/snapshots/jit_spec__tests__skip.snap |  48 +++++++
 11 files changed, 394 insertions(+), 118 deletions(-)
 create mode 100644 tests/snapshots/jit_spec__tests__skip.snap

diff --git a/Cargo.lock b/Cargo.lock
index cf1b8b278b..2ea53b2c97 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -280,7 +280,7 @@ dependencies = [
  "proc-macro2",
  "quote",
  "strum",
- "syn 2.0.70",
+ "syn 2.0.69",
  "thiserror",
 ]
 
@@ -434,7 +434,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.70",
+ "syn 2.0.69",
 ]
 
 [[package]]
@@ -502,7 +502,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.70",
+ "syn 2.0.69",
 ]
 
 [[package]]
@@ -519,7 +519,7 @@ checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.70",
+ "syn 2.0.69",
 ]
 
 [[package]]
@@ -563,7 +563,7 @@ dependencies = [
  "futures-util",
  "http 0.2.12",
  "http-body 0.4.6",
- "hyper 0.14.30",
+ "hyper 0.14.29",
  "itoa",
  "matchit",
  "memchr",
@@ -907,9 +907,9 @@ dependencies = [
 
 [[package]]
 name = "clap"
-version = "4.5.9"
+version = "4.5.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "64acc1846d54c1fe936a78dc189c34e28d3f5afc348403f28ecf53660b9b8462"
+checksum = "84b3edb18336f4df585bc9aa31dd99c036dfa5dc5e9a2939a722a188f3a8970d"
 dependencies = [
  "clap_builder",
  "clap_derive",
@@ -917,9 +917,9 @@ dependencies = [
 
 [[package]]
 name = "clap_builder"
-version = "4.5.9"
+version = "4.5.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6fb8393d67ba2e7bfaf28a23458e4e2b543cc73a99595511eb207fdb8aede942"
+checksum = "c1c09dd5ada6c6c78075d6fd0da3f90d8080651e2d6cc8eb2f1aaa4034ced708"
 dependencies = [
  "anstream",
  "anstyle",
@@ -936,7 +936,7 @@ dependencies = [
  "heck",
  "proc-macro2",
  "quote",
- "syn 2.0.70",
+ "syn 2.0.69",
 ]
 
 [[package]]
@@ -1216,7 +1216,7 @@ dependencies = [
  "proc-macro2",
  "quote",
  "strsim 0.11.1",
- "syn 2.0.70",
+ "syn 2.0.69",
 ]
 
 [[package]]
@@ -1238,7 +1238,7 @@ checksum = "733cabb43482b1a1b53eee8583c2b9e8684d592215ea83efd305dd31bc2f0178"
 dependencies = [
  "darling_core 0.20.9",
  "quote",
- "syn 2.0.70",
+ "syn 2.0.69",
 ]
 
 [[package]]
@@ -1276,7 +1276,7 @@ checksum = "0a6433aac097572ea8ccc60b3f2e756c661c9aeed9225cdd4d0cb119cb7ff6ba"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.70",
+ "syn 2.0.69",
 ]
 
 [[package]]
@@ -1320,7 +1320,7 @@ dependencies = [
  "proc-macro2",
  "quote",
  "rustc_version",
- "syn 2.0.70",
+ "syn 2.0.69",
 ]
 
 [[package]]
@@ -1332,7 +1332,7 @@ dependencies = [
  "darling 0.20.9",
  "proc-macro2",
  "quote",
- "syn 2.0.70",
+ "syn 2.0.69",
 ]
 
 [[package]]
@@ -1703,7 +1703,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.70",
+ "syn 2.0.69",
 ]
 
 [[package]]
@@ -2265,7 +2265,7 @@ dependencies = [
  "crossbeam-utils",
  "form_urlencoded",
  "futures-util",
- "hyper 0.14.30",
+ "hyper 0.14.29",
  "lazy_static",
  "levenshtein",
  "log",
@@ -2280,9 +2280,9 @@ dependencies = [
 
 [[package]]
 name = "hyper"
-version = "0.14.30"
+version = "0.14.29"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a152ddd61dfaec7273fe8419ab357f33aee0d914c5f4efbf0d96fa749eea5ec9"
+checksum = "f361cde2f109281a220d4307746cdfd5ee3f410da58a70377762396775634b33"
 dependencies = [
  "bytes",
  "futures-channel",
@@ -2295,7 +2295,7 @@ dependencies = [
  "httpdate",
  "itoa",
  "pin-project-lite",
- "socket2 0.5.7",
+ "socket2 0.4.10",
  "tokio",
  "tower-service",
  "tracing",
@@ -2329,7 +2329,7 @@ checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590"
 dependencies = [
  "futures-util",
  "http 0.2.12",
- "hyper 0.14.30",
+ "hyper 0.14.29",
  "rustls 0.21.12",
  "tokio",
  "tokio-rustls 0.24.1",
@@ -2343,7 +2343,7 @@ checksum = "399c78f9338483cb7e630c8474b07268983c6bd5acee012e4211f9f7bb21b070"
 dependencies = [
  "futures-util",
  "http 0.2.12",
- "hyper 0.14.30",
+ "hyper 0.14.29",
  "log",
  "rustls 0.22.4",
  "rustls-native-certs",
@@ -2358,7 +2358,7 @@ version = "0.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1"
 dependencies = [
- "hyper 0.14.30",
+ "hyper 0.14.29",
  "pin-project-lite",
  "tokio",
  "tokio-io-timeout",
@@ -2550,6 +2550,15 @@ dependencies = [
  "either",
 ]
 
+[[package]]
+name = "itertools"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
+dependencies = [
+ "either",
+]
+
 [[package]]
 name = "itoa"
 version = "1.0.11"
@@ -2838,7 +2847,7 @@ dependencies = [
  "proc-macro2",
  "quote",
  "regex-syntax 0.8.4",
- "syn 2.0.70",
+ "syn 2.0.69",
 ]
 
 [[package]]
@@ -2986,7 +2995,7 @@ checksum = "49e7bc1560b95a3c4a25d03de42fe76ca718ab92d1a22a55b9b4cf67b3ae635c"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.70",
+ "syn 2.0.69",
 ]
 
 [[package]]
@@ -2997,7 +3006,7 @@ checksum = "dcf09caffaac8068c346b6df2a7fc27a177fd20b39421a39ce0a211bde679a6c"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.70",
+ "syn 2.0.69",
 ]
 
 [[package]]
@@ -3570,7 +3579,7 @@ dependencies = [
  "pest_meta",
  "proc-macro2",
  "quote",
- "syn 2.0.70",
+ "syn 2.0.69",
 ]
 
 [[package]]
@@ -3605,14 +3614,14 @@ dependencies = [
 
 [[package]]
 name = "phonenumber"
-version = "0.3.6+8.13.36"
+version = "0.3.5+8.13.36"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "11756237b57b8cc5e97dc8b1e70ea436324d30e7075de63b14fd15073a8f692a"
+checksum = "f174c8db59b620032bd52b655fc97000458850fec0db35fcd4e802b668517ec0"
 dependencies = [
  "bincode",
  "either",
  "fnv",
- "itertools 0.10.5",
+ "itertools 0.12.1",
  "lazy_static",
  "nom",
  "quick-xml",
@@ -3647,7 +3656,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.70",
+ "syn 2.0.69",
 ]
 
 [[package]]
@@ -3767,7 +3776,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e"
 dependencies = [
  "proc-macro2",
- "syn 2.0.70",
+ "syn 2.0.69",
 ]
 
 [[package]]
@@ -3855,7 +3864,7 @@ checksum = "22505a5c94da8e3b7c2996394d1c933236c4d743e81a410bcca4e6989fc066a4"
 dependencies = [
  "bytes",
  "heck",
- "itertools 0.10.5",
+ "itertools 0.12.1",
  "log",
  "multimap",
  "once_cell",
@@ -3864,7 +3873,7 @@ dependencies = [
  "prost",
  "prost-types",
  "regex",
- "syn 2.0.70",
+ "syn 2.0.69",
  "tempfile",
 ]
 
@@ -3875,10 +3884,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1"
 dependencies = [
  "anyhow",
- "itertools 0.10.5",
+ "itertools 0.12.1",
  "proc-macro2",
  "quote",
- "syn 2.0.70",
+ "syn 2.0.69",
 ]
 
 [[package]]
@@ -4274,7 +4283,7 @@ dependencies = [
  "h2",
  "http 0.2.12",
  "http-body 0.4.6",
- "hyper 0.14.30",
+ "hyper 0.14.29",
  "hyper-rustls 0.24.2",
  "ipnet",
  "js-sys",
@@ -4379,7 +4388,7 @@ dependencies = [
  "proc-macro2",
  "quote",
  "rquickjs-core",
- "syn 2.0.70",
+ "syn 2.0.69",
 ]
 
 [[package]]
@@ -4454,20 +4463,20 @@ dependencies = [
  "log",
  "ring",
  "rustls-pki-types",
- "rustls-webpki 0.102.5",
+ "rustls-webpki 0.102.4",
  "subtle",
  "zeroize",
 ]
 
 [[package]]
 name = "rustls"
-version = "0.23.11"
+version = "0.23.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4828ea528154ae444e5a642dbb7d5623354030dc9822b83fd9bb79683c7399d0"
+checksum = "05cff451f60db80f490f3c182b77c35260baace73209e9cdbbe526bfe3a4d402"
 dependencies = [
  "once_cell",
  "rustls-pki-types",
- "rustls-webpki 0.102.5",
+ "rustls-webpki 0.102.4",
  "subtle",
  "zeroize",
 ]
@@ -4522,9 +4531,9 @@ dependencies = [
 
 [[package]]
 name = "rustls-webpki"
-version = "0.102.5"
+version = "0.102.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f9a6fccd794a42c2c105b513a2f62bc3fd8f3ba57a4593677ceb0bd035164d78"
+checksum = "ff448f7e92e913c4b7d4c6d8e4540a1724b319b4152b8aef6d4cf8339712b33e"
 dependencies = [
  "ring",
  "rustls-pki-types",
@@ -4582,7 +4591,7 @@ dependencies = [
  "proc-macro2",
  "quote",
  "serde_derive_internals",
- "syn 2.0.70",
+ "syn 2.0.69",
 ]
 
 [[package]]
@@ -4694,7 +4703,7 @@ checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.70",
+ "syn 2.0.69",
 ]
 
 [[package]]
@@ -4705,7 +4714,7 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.70",
+ "syn 2.0.69",
 ]
 
 [[package]]
@@ -5028,7 +5037,7 @@ dependencies = [
  "proc-macro2",
  "quote",
  "rustversion",
- "syn 2.0.70",
+ "syn 2.0.69",
 ]
 
 [[package]]
@@ -5050,9 +5059,9 @@ dependencies = [
 
 [[package]]
 name = "syn"
-version = "2.0.70"
+version = "2.0.69"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2f0209b68b3613b093e0ec905354eccaedcfe83b8cb37cbdeae64026c3064c16"
+checksum = "201fcda3845c23e8212cd466bfebf0bd20694490fc0356ae8e428e0824a915a6"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -5160,7 +5169,7 @@ dependencies = [
  "http-cache-reqwest",
  "http-cache-semantics",
  "httpmock",
- "hyper 0.14.30",
+ "hyper 0.14.29",
  "hyper-rustls 0.25.0",
  "indenter",
  "indexmap 2.2.6",
@@ -5202,7 +5211,7 @@ dependencies = [
  "reqwest-middleware",
  "resource",
  "rquickjs",
- "rustls 0.23.11",
+ "rustls 0.23.10",
  "rustls-pemfile 1.0.4",
  "rustls-pki-types",
  "schemars",
@@ -5248,7 +5257,7 @@ dependencies = [
  "async-graphql-value",
  "async-trait",
  "dotenvy",
- "hyper 0.14.30",
+ "hyper 0.14.29",
  "lambda_http",
  "lambda_runtime",
  "reqwest",
@@ -5267,7 +5276,7 @@ dependencies = [
  "async-std",
  "async-trait",
  "console_error_panic_hook",
- "hyper 0.14.30",
+ "hyper 0.14.29",
  "lazy_static",
  "protox",
  "reqwest",
@@ -5319,7 +5328,7 @@ version = "0.1.0"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.70",
+ "syn 2.0.69",
 ]
 
 [[package]]
@@ -5378,7 +5387,7 @@ version = "0.1.0"
 dependencies = [
  "anyhow",
  "http-body-util",
- "hyper 0.14.30",
+ "hyper 0.14.29",
  "hyper-util",
  "once_cell",
  "opentelemetry 0.23.0",
@@ -5474,7 +5483,7 @@ checksum = "5999e24eaa32083191ba4e425deb75cdf25efefabe5aaccb7446dd0d4122a3f5"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.70",
+ "syn 2.0.69",
 ]
 
 [[package]]
@@ -5494,7 +5503,7 @@ checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.70",
+ "syn 2.0.69",
 ]
 
 [[package]]
@@ -5618,7 +5627,7 @@ checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.70",
+ "syn 2.0.69",
 ]
 
 [[package]]
@@ -5721,7 +5730,7 @@ dependencies = [
  "h2",
  "http 0.2.12",
  "http-body 0.4.6",
- "hyper 0.14.30",
+ "hyper 0.14.29",
  "hyper-timeout",
  "percent-encoding",
  "pin-project",
@@ -5748,7 +5757,7 @@ dependencies = [
  "proc-macro2",
  "prost-build",
  "quote",
- "syn 2.0.70",
+ "syn 2.0.69",
 ]
 
 [[package]]
@@ -5761,7 +5770,7 @@ dependencies = [
  "proc-macro2",
  "prost-build",
  "quote",
- "syn 2.0.70",
+ "syn 2.0.69",
 ]
 
 [[package]]
@@ -5787,7 +5796,7 @@ dependencies = [
  "futures-util",
  "http 0.2.12",
  "http-body 0.4.6",
- "hyper 0.14.30",
+ "hyper 0.14.29",
  "opentelemetry 0.23.0",
  "pin-project-lite",
  "tonic",
@@ -5859,7 +5868,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.70",
+ "syn 2.0.69",
 ]
 
 [[package]]
@@ -6174,7 +6183,7 @@ dependencies = [
  "once_cell",
  "proc-macro2",
  "quote",
- "syn 2.0.70",
+ "syn 2.0.69",
  "wasm-bindgen-shared",
 ]
 
@@ -6208,7 +6217,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.70",
+ "syn 2.0.69",
  "wasm-bindgen-backend",
  "wasm-bindgen-shared",
 ]
@@ -6395,7 +6404,7 @@ checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.70",
+ "syn 2.0.69",
 ]
 
 [[package]]
@@ -6417,7 +6426,7 @@ checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.70",
+ "syn 2.0.69",
 ]
 
 [[package]]
@@ -6672,7 +6681,7 @@ dependencies = [
  "async-trait",
  "proc-macro2",
  "quote",
- "syn 2.0.70",
+ "syn 2.0.69",
  "wasm-bindgen",
  "wasm-bindgen-futures",
  "wasm-bindgen-macro-support",
@@ -6700,7 +6709,7 @@ dependencies = [
  "darling 0.20.9",
  "proc-macro2",
  "quote",
- "syn 2.0.70",
+ "syn 2.0.69",
 ]
 
 [[package]]
@@ -6732,7 +6741,7 @@ checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.70",
+ "syn 2.0.69",
 ]
 
 [[package]]
diff --git a/src/core/jit/builder.rs b/src/core/jit/builder.rs
index 016e49e642..ce1b413ddc 100644
--- a/src/core/jit/builder.rs
+++ b/src/core/jit/builder.rs
@@ -1,16 +1,49 @@
 use std::collections::HashMap;
+use std::ops::Deref;
 
 use async_graphql::parser::types::{
-    DocumentOperations, ExecutableDocument, FragmentDefinition, OperationType, Selection,
-    SelectionSet,
+    Directive, DocumentOperations, ExecutableDocument, FragmentDefinition, OperationType,
+    Selection, SelectionSet,
 };
 use async_graphql::Positioned;
+use async_graphql_value::Value;
 
 use super::model::*;
 use crate::core::blueprint::{Blueprint, Index, QueryField};
 use crate::core::counter::{Count, Counter};
 use crate::core::merge_right::MergeRight;
 
+#[derive(PartialEq)]
+enum Condition {
+    True,
+    False,
+    Variable(Variable),
+}
+
+struct Conditions {
+    skip: Option<Condition>,
+    include: Option<Condition>,
+}
+
+impl Conditions {
+    /// Checks if the field should be skipped always
+    fn is_const_skip(&self) -> bool {
+        matches!(self.skip, Some(Condition::True)) ^ matches!(self.include, Some(Condition::True))
+    }
+
+    fn into_variable_tuple(self) -> (Option<Variable>, Option<Variable>) {
+        let comp = |condition| match condition? {
+            Condition::Variable(var) => Some(var),
+            _ => None,
+        };
+
+        let include = comp(self.include);
+        let skip = comp(self.skip);
+
+        (include, skip)
+    }
+}
+
 pub struct Builder {
     pub index: Index,
     pub arg_id: Counter<usize>,
@@ -29,7 +62,48 @@ impl Builder {
         }
     }
 
+    #[inline(always)]
+    fn include(
+        &self,
+        directives: &[Positioned<async_graphql::parser::types::Directive>],
+    ) -> Conditions {
+        fn get_condition(dir: &Directive) -> Option<Condition> {
+            let arg = dir.get_argument("if").map(|pos| &pos.node);
+            let is_include = dir.name.node.as_str() == "include";
+            match arg {
+                None => None,
+                Some(value) => match value {
+                    Value::Boolean(bool) => {
+                        let condition = if is_include ^ bool {
+                            Condition::True
+                        } else {
+                            Condition::False
+                        };
+                        Some(condition)
+                    }
+                    Value::Variable(var) => {
+                        Some(Condition::Variable(Variable::new(var.deref().to_owned())))
+                    }
+                    _ => None,
+                },
+            }
+        }
+        Conditions {
+            skip: directives
+                .iter()
+                .find(|d| d.node.name.node.as_str() == "skip")
+                .map(|d| &d.node)
+                .and_then(get_condition),
+            include: directives
+                .iter()
+                .find(|d| d.node.name.node.as_str() == "include")
+                .map(|d| &d.node)
+                .and_then(get_condition),
+        }
+    }
+
     #[allow(clippy::too_many_arguments)]
+    #[inline(always)]
     fn iter(
         &self,
         selection: &SelectionSet,
@@ -41,6 +115,16 @@ impl Builder {
         for selection in &selection.items {
             match &selection.node {
                 Selection::Field(Positioned { node: gql_field, .. }) => {
+                    let conditions = self.include(&gql_field.directives);
+
+                    // if include is always false xor skip is always true,
+                    // then we can skip the field from the plan
+                    if conditions.is_const_skip() {
+                        continue;
+                    }
+
+                    let (include, skip) = conditions.into_variable_tuple();
+
                     let field_name = gql_field.name.node.as_str();
                     let field_args = gql_field
                         .arguments
@@ -91,6 +175,8 @@ impl Builder {
                             name,
                             ir,
                             type_of,
+                            skip,
+                            include,
                             args,
                             extensions: exts.clone(),
                         });
@@ -116,6 +202,7 @@ impl Builder {
         fields
     }
 
+    #[inline(always)]
     fn get_type(&self, ty: OperationType) -> Option<&str> {
         match ty {
             OperationType::Query => Some(self.index.get_query()),
@@ -124,6 +211,7 @@ impl Builder {
         }
     }
 
+    #[inline(always)]
     pub fn build(&self) -> Result<ExecutionPlan, String> {
         let mut fields = Vec::new();
         let mut fragments: HashMap<&str, &FragmentDefinition> = HashMap::new();
@@ -176,7 +264,6 @@ mod tests {
         let config = Config::from_sdl(CONFIG).to_result().unwrap();
         let blueprint = Blueprint::try_from(&config.into()).unwrap();
         let document = async_graphql::parser::parse_query(query).unwrap();
-
         Builder::new(&blueprint, document).build().unwrap()
     }
 
diff --git a/src/core/jit/common/json_placeholder.rs b/src/core/jit/common/json_placeholder.rs
index c832bda7bb..e2f0a1de9b 100644
--- a/src/core/jit/common/json_placeholder.rs
+++ b/src/core/jit/common/json_placeholder.rs
@@ -7,6 +7,7 @@ use crate::core::config::{Config, ConfigModule};
 use crate::core::jit::builder::Builder;
 use crate::core::jit::store::{Data, Store};
 use crate::core::jit::synth::Synth;
+use crate::core::jit::Variables;
 use crate::core::json::JsonLike;
 use crate::core::valid::Validator;
 
@@ -82,6 +83,7 @@ impl JsonPlaceholder {
             store
         });
 
-        Synth::new(plan, store)
+        let vars = Variables::new();
+        Synth::new(plan, store, vars)
     }
 }
diff --git a/src/core/jit/exec.rs b/src/core/jit/exec.rs
index ae12be86ee..1d1f8911cb 100644
--- a/src/core/jit/exec.rs
+++ b/src/core/jit/exec.rs
@@ -20,10 +20,10 @@ pub struct Executor<Synth, IRExec> {
     exec: IRExec,
 }
 
-impl<Input, Output, Error, Synth, Exec> Executor<Synth, Exec>
+impl<Input: Clone, Output, Error, Synth, Exec> Executor<Synth, Exec>
 where
     Output: JsonLike<Json = Output> + Debug,
-    Synth: Synthesizer<Value = Result<Output, Error>>,
+    Synth: Synthesizer<Value = Result<Output, Error>, Variable = Input>,
     Exec: IRExecutor<Input = Input, Output = Output, Error = Error>,
 {
     pub fn new(plan: ExecutionPlan, synth: Synth, exec: Exec) -> Self {
@@ -40,8 +40,9 @@ where
     }
 
     pub async fn execute(self, request: Request<Input>) -> Response<Output, Error> {
+        let vars = request.variables.clone();
         let store = self.execute_inner(request).await;
-        Response::new(self.synth.synthesize(store))
+        Response::new(self.synth.synthesize(store, vars))
     }
 }
 
diff --git a/src/core/jit/model.rs b/src/core/jit/model.rs
index 1329b0f2f7..3cd0234b68 100644
--- a/src/core/jit/model.rs
+++ b/src/core/jit/model.rs
@@ -1,7 +1,37 @@
+use std::collections::HashMap;
 use std::fmt::{Debug, Formatter};
 
+use serde::Deserialize;
+
 use crate::core::ir::model::IR;
 
+#[derive(Debug, Deserialize, Clone)]
+pub struct Variables<Value>(HashMap<String, Value>);
+
+impl<Value> Default for Variables<Value> {
+    fn default() -> Self {
+        Self::new()
+    }
+}
+
+impl<Value> Variables<Value> {
+    pub fn new() -> Self {
+        Self(HashMap::new())
+    }
+    pub fn get(&self, key: &str) -> Option<&Value> {
+        self.0.get(key)
+    }
+    pub fn insert(&mut self, key: String, value: Value) {
+        self.0.insert(key, value);
+    }
+}
+
+impl<V> FromIterator<(String, V)> for Variables<V> {
+    fn from_iter<T: IntoIterator<Item = (String, V)>>(iter: T) -> Self {
+        Self(iter.into_iter().collect())
+    }
+}
+
 #[derive(Debug, Clone)]
 pub struct Arg {
     pub id: ArgId,
@@ -50,9 +80,40 @@ pub struct Field<Extensions> {
     pub name: String,
     pub ir: Option<IR>,
     pub type_of: crate::core::blueprint::Type,
+    pub skip: Option<Variable>,
+    pub include: Option<Variable>,
     pub args: Vec<Arg>,
     pub extensions: Option<Extensions>,
 }
+#[derive(Clone, Debug, PartialEq)]
+pub struct Variable(String);
+
+impl Variable {
+    pub fn new(name: String) -> Self {
+        Variable(name)
+    }
+}
+
+impl<A> Field<A> {
+    #[inline(always)]
+    pub fn skip(&self, variables: &Variables<async_graphql_value::ConstValue>) -> bool {
+        let eval = |variable_option: Option<&Variable>,
+                    variables: &Variables<async_graphql_value::ConstValue>,
+                    default: bool| {
+            match variable_option {
+                Some(Variable(name)) => variables.get(name).map_or(default, |value| match value {
+                    async_graphql_value::ConstValue::Boolean(b) => *b,
+                    _ => default,
+                }),
+                None => default,
+            }
+        };
+        let skip = eval(self.skip.as_ref(), variables, false);
+        let include = eval(self.include.as_ref(), variables, true);
+
+        skip == include
+    }
+}
 
 const EMPTY_VEC: &Vec<Field<Nested>> = &Vec::new();
 impl Field<Nested> {
@@ -90,6 +151,8 @@ impl Field<Flat> {
             name: self.name,
             ir: self.ir,
             type_of: self.type_of,
+            skip: self.skip,
+            include: self.include,
             args: self.args,
             extensions,
         }
diff --git a/src/core/jit/request.rs b/src/core/jit/request.rs
index d79b4adef1..ce7cb9a091 100644
--- a/src/core/jit/request.rs
+++ b/src/core/jit/request.rs
@@ -5,15 +5,16 @@ use serde::Deserialize;
 
 use super::{Builder, Error, ExecutionPlan, Result};
 use crate::core::blueprint::Blueprint;
+use crate::core::jit::model::Variables;
 
-#[derive(Debug, Deserialize, Setters)]
+#[derive(Debug, Deserialize, Setters, Clone)]
 pub struct Request<Value> {
     #[serde(default)]
     pub query: String,
     #[serde(default, rename = "operationName")]
     pub operation_name: Option<String>,
     #[serde(default)]
-    pub variables: HashMap<String, Value>,
+    pub variables: Variables<Value>,
     #[serde(default)]
     pub extensions: HashMap<String, Value>,
 }
@@ -25,9 +26,9 @@ impl From<async_graphql::Request> for Request<async_graphql_value::ConstValue> {
             operation_name: value.operation_name,
             variables: match value.variables.into_value() {
                 async_graphql_value::ConstValue::Object(val) => {
-                    HashMap::from_iter(val.into_iter().map(|(k, v)| (k.to_string(), v)))
+                    Variables::from_iter(val.into_iter().map(|(name, val)| (name.to_string(), val)))
                 }
-                _ => HashMap::new(),
+                _ => Variables::default(),
             },
             extensions: value.extensions,
         }
@@ -47,7 +48,7 @@ impl<A> Request<A> {
         Self {
             query: query.to_string(),
             operation_name: None,
-            variables: HashMap::new(),
+            variables: Variables::default(),
             extensions: HashMap::new(),
         }
     }
diff --git a/src/core/jit/synth/mod.rs b/src/core/jit/synth/mod.rs
index 8edb154176..534e3f5aa5 100644
--- a/src/core/jit/synth/mod.rs
+++ b/src/core/jit/synth/mod.rs
@@ -4,8 +4,14 @@ mod synth_const;
 // pub use synth_borrow::SynthBorrow;
 pub use synth_const::{Synth, SynthConst};
 
-use super::Store;
+use super::{Store, Variables};
+use crate::core::json::JsonLike;
 pub trait Synthesizer {
     type Value;
-    fn synthesize(self, store: Store<Self::Value>) -> Self::Value;
+    type Variable: JsonLike;
+    fn synthesize(
+        self,
+        store: Store<Self::Value>,
+        variables: Variables<Self::Variable>,
+    ) -> Self::Value;
 }
diff --git a/src/core/jit/synth/synth_borrow.rs b/src/core/jit/synth/synth_borrow.rs
index 9c995fae4d..243593e54f 100644
--- a/src/core/jit/synth/synth_borrow.rs
+++ b/src/core/jit/synth/synth_borrow.rs
@@ -131,6 +131,7 @@ mod tests {
     use crate::core::jit::model::FieldId;
     use crate::core::jit::store::{Data, Store};
     use crate::core::jit::synth::SynthBorrow;
+    use crate::core::jit::Variables;
     use crate::core::valid::Validator;
 
     const POSTS: &str = r#"
@@ -207,7 +208,11 @@ mod tests {
         let config = Config::from_sdl(CONFIG).to_result().unwrap();
         let config = ConfigModule::from(config);
 
-        let builder = Builder::new(&Blueprint::try_from(&config).unwrap(), doc);
+        let builder = Builder::new(
+            &Blueprint::try_from(&config).unwrap(),
+            doc,
+            Variables::new(),
+        );
         let plan = builder.build().unwrap();
 
         let store = store
diff --git a/src/core/jit/synth/synth_const.rs b/src/core/jit/synth/synth_const.rs
index 387b7cecc5..ffd86afa99 100644
--- a/src/core/jit/synth/synth_const.rs
+++ b/src/core/jit/synth/synth_const.rs
@@ -5,23 +5,36 @@ use super::super::Result;
 use super::Synthesizer;
 use crate::core::jit::model::{Field, Nested};
 use crate::core::jit::store::{Data, Store};
-use crate::core::jit::{DataPath, ExecutionPlan};
+use crate::core::jit::{DataPath, ExecutionPlan, Variables};
 use crate::core::json::JsonLike;
 
 pub struct Synth {
     selection: Vec<Field<Nested>>,
     store: Store<Result<Value>>,
+    variables: Variables<async_graphql_value::ConstValue>,
 }
 
 impl Synth {
-    pub fn new(plan: ExecutionPlan, store: Store<Result<Value>>) -> Self {
-        Self { selection: plan.into_nested(), store }
+    pub fn new(
+        plan: ExecutionPlan,
+        store: Store<Result<Value>>,
+        variables: Variables<async_graphql_value::ConstValue>,
+    ) -> Self {
+        Self { selection: plan.into_nested(), store, variables }
+    }
+
+    #[inline(always)]
+    fn include<T>(&self, field: &Field<T>) -> bool {
+        !field.skip(&self.variables)
     }
 
     pub fn synthesize(&self) -> Result<Value> {
         let mut data = IndexMap::default();
 
         for child in self.selection.iter() {
+            if !self.include(child) {
+                continue;
+            }
             let val = self.iter(child, None, &DataPath::new())?;
             data.insert(Name::new(child.name.as_str()), val);
         }
@@ -90,32 +103,40 @@ impl Synth {
         parent: &'b Value,
         data_path: &'b DataPath,
     ) -> Result<Value> {
+        let include = self.include(node);
+
         match parent {
             Value::Object(obj) => {
                 let mut ans = IndexMap::default();
                 let children = node.nested();
-
-                if children.is_empty() {
-                    let val = obj.get(node.name.as_str());
-                    // if it's a leaf node, then push the value
-                    if let Some(val) = val {
-                        ans.insert(Name::new(node.name.as_str()), val.to_owned());
-                    } else {
-                        return Ok(Value::Null);
-                    }
-                } else {
-                    for child in children {
-                        let val = obj.get(child.name.as_str());
+                if include {
+                    if children.is_empty() {
+                        let val = obj.get(node.name.as_str());
+                        // if it's a leaf node, then push the value
                         if let Some(val) = val {
-                            ans.insert(
-                                Name::new(child.name.as_str()),
-                                self.iter_inner(child, val, data_path)?,
-                            );
+                            ans.insert(Name::new(node.name.as_str()), val.to_owned());
                         } else {
-                            ans.insert(
-                                Name::new(child.name.as_str()),
-                                self.iter(child, None, data_path)?,
-                            );
+                            return Ok(Value::Null);
+                        }
+                    } else {
+                        for child in children {
+                            // all checks for skip must occur in `iter_inner`
+                            // and include be checked before calling `iter` or recursing.
+                            let include = self.include(child);
+                            if include {
+                                let val = obj.get(child.name.as_str());
+                                if let Some(val) = val {
+                                    ans.insert(
+                                        Name::new(child.name.as_str()),
+                                        self.iter_inner(child, val, data_path)?,
+                                    );
+                                } else {
+                                    ans.insert(
+                                        Name::new(child.name.as_str()),
+                                        self.iter(child, None, data_path)?,
+                                    );
+                                }
+                            }
                         }
                     }
                 }
@@ -123,9 +144,11 @@ impl Synth {
             }
             Value::List(arr) => {
                 let mut ans = vec![];
-                for (i, val) in arr.iter().enumerate() {
-                    let val = self.iter_inner(node, val, &data_path.clone().with_index(i))?;
-                    ans.push(val)
+                if include {
+                    for (i, val) in arr.iter().enumerate() {
+                        let val = self.iter_inner(node, val, &data_path.clone().with_index(i))?;
+                        ans.push(val)
+                    }
                 }
                 Ok(Value::List(ans))
             }
@@ -146,15 +169,19 @@ impl SynthConst {
 
 impl Synthesizer for SynthConst {
     type Value = Result<Value>;
-
-    fn synthesize(self, store: Store<Self::Value>) -> Self::Value {
-        Synth::new(self.plan, store).synthesize()
+    type Variable = Value;
+
+    fn synthesize(
+        self,
+        store: Store<Self::Value>,
+        variables: Variables<Self::Variable>,
+    ) -> Self::Value {
+        Synth::new(self.plan, store, variables).synthesize()
     }
 }
 
 #[cfg(test)]
 mod tests {
-
     use async_graphql::Value;
 
     use super::Synth;
@@ -164,6 +191,7 @@ mod tests {
     use crate::core::jit::common::JsonPlaceholder;
     use crate::core::jit::model::FieldId;
     use crate::core::jit::store::{Data, Store};
+    use crate::core::jit::Variables;
     use crate::core::valid::Validator;
 
     const POSTS: &str = r#"
@@ -249,8 +277,8 @@ mod tests {
                 store.set_data(id, data.map(Ok));
                 store
             });
-
-        let synth = Synth::new(plan, store);
+        let vars = Variables::new();
+        let synth = Synth::new(plan, store, vars);
         let val = synth.synthesize().unwrap();
 
         serde_json::to_string_pretty(&val).unwrap()
diff --git a/tests/jit_spec.rs b/tests/jit_spec.rs
index d8b24f405f..8969bf3380 100644
--- a/tests/jit_spec.rs
+++ b/tests/jit_spec.rs
@@ -109,6 +109,32 @@ mod tests {
         let response = executor.execute(request).await;
         let data = response.data;
 
+        insta::assert_json_snapshot!(data);
+    }
+    #[tokio::test]
+    async fn test_skip() {
+        //  NOTE: This test makes a real HTTP call
+        let mut request = Request::new(
+            r#"
+                query ($TRUE: Boolean!){
+                  users {
+                    id @skip(if: true)
+                    name @skip(if: $TRUE)
+                    email @include(if: $TRUE)
+                    username @include(if: false)
+                    phone @skip(if: false) @include(if: true)
+                  }
+                }
+        "#,
+        );
+        request
+            .variables
+            .insert("TRUE".to_string(), Value::Boolean(true));
+
+        let executor = new_executor(&request).await.unwrap();
+        let response = executor.execute(request).await;
+        let data = response.data;
+
         insta::assert_json_snapshot!(data);
     }
 }
diff --git a/tests/snapshots/jit_spec__tests__skip.snap b/tests/snapshots/jit_spec__tests__skip.snap
new file mode 100644
index 0000000000..b60906f839
--- /dev/null
+++ b/tests/snapshots/jit_spec__tests__skip.snap
@@ -0,0 +1,48 @@
+---
+source: tests/jit_spec.rs
+expression: data
+---
+{
+  "users": [
+    {
+      "email": "Sincere@april.biz",
+      "phone": "1-770-736-8031 x56442"
+    },
+    {
+      "email": "Shanna@melissa.tv",
+      "phone": "010-692-6593 x09125"
+    },
+    {
+      "email": "Nathan@yesenia.net",
+      "phone": "1-463-123-4447"
+    },
+    {
+      "email": "Julianne.OConner@kory.org",
+      "phone": "493-170-9623 x156"
+    },
+    {
+      "email": "Lucio_Hettinger@annie.ca",
+      "phone": "(254)954-1289"
+    },
+    {
+      "email": "Karley_Dach@jasper.info",
+      "phone": "1-477-935-8478 x6430"
+    },
+    {
+      "email": "Telly.Hoeger@billy.biz",
+      "phone": "210.067.6132"
+    },
+    {
+      "email": "Sherwood@rosamond.me",
+      "phone": "586.493.6943 x140"
+    },
+    {
+      "email": "Chaim_McDermott@dana.io",
+      "phone": "(775)976-6794 x41206"
+    },
+    {
+      "email": "Rey.Padberg@karina.biz",
+      "phone": "024-648-3804"
+    }
+  ]
+}