Repository: Brendonovich/prisma-client-rust (language: rust)

Issue:
Parse GraphQL Directly
instead of converting to string first


Discussion / hints:

Fix patch:
diff --git a/Cargo.toml b/Cargo.toml
index 25151b26..ce17e571 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -19,8 +19,10 @@ members = [
     "prisma-cli"
 ]
 
-[profile.test]
-debug = 0
+[features]
+default = ["sql", "mongodb"]
+sql = ["query-core/sql"]
+mongodb = ["query-core/mongodb"]
 
 [dependencies]
 serde = { version = "1.0", features = ["derive"] }
@@ -28,7 +30,7 @@ serde_json = "1.0"
 chrono = { version = "0.4.19", features = ["serde"] }
 thiserror = "1.0.30"
 
-query-core = { git = "https://github.com/Brendonovich/prisma-engines", rev = "8b94a80a2d8f12a2916fe28e3218162aa71e8539" }
+query-core = { git = "https://github.com/Brendonovich/prisma-engines", default-features = false, rev = "8b94a80a2d8f12a2916fe28e3218162aa71e8539" }
 query-connector = { git = "https://github.com/Brendonovich/prisma-engines", rev = "8b94a80a2d8f12a2916fe28e3218162aa71e8539" }
 request-handlers = { git = "https://github.com/Brendonovich/prisma-engines", rev = "8b94a80a2d8f12a2916fe28e3218162aa71e8539" }
 prisma-models = { git = "https://github.com/Brendonovich/prisma-engines", rev = "8b94a80a2d8f12a2916fe28e3218162aa71e8539" }
@@ -37,3 +39,6 @@ graphql-parser = { git = "https://github.com/prisma/graphql-parser", rev = "6a3f
 
 [dev-dependencies]
 rand = "0.8.5"
+
+[profile.test]
+debug = 0
diff --git a/cli/src/generator/ast/dmmf.rs b/cli/src/generator/ast/dmmf.rs
index bad90826..8d139026 100644
--- a/cli/src/generator/ast/dmmf.rs
+++ b/cli/src/generator/ast/dmmf.rs
@@ -1,4 +1,6 @@
+use quote::{__private::TokenStream, format_ident, quote};
 use serde::{Deserialize, Serialize};
+use syn::Ident;
 
 use crate::generator::GraphQLType;
 
@@ -387,6 +389,17 @@ impl Field {
             ]
         }
     }
+
+    pub fn type_as_query_value(&self, var: &Ident) -> TokenStream {
+        if self.is_list {
+            let converter = self.field_type.to_prisma_value(&format_ident!("v"));
+            quote!(QueryValue::List(#var.into_iter().map(|v| #converter).collect()))
+        } else {
+            let t = self.field_type.to_prisma_value(var);
+
+            quote!(#t.into())
+        }
+    }
 }
 
 impl Model {
diff --git a/cli/src/generator/codegen/client.rs b/cli/src/generator/codegen/client.rs
index 89e02cb2..e0c966af 100644
--- a/cli/src/generator/codegen/client.rs
+++ b/cli/src/generator/codegen/client.rs
@@ -27,19 +27,25 @@ pub fn generate(root: &Root) -> TokenStream {
     quote! {
         #![allow(warnings, unused)]
 
-        use prisma_client_rust::query::{Query, Input, Output, Field, QueryContext, transform_equals, Result as QueryResult};
-        use prisma_client_rust::datamodel::parse_configuration;
-        use prisma_client_rust::prisma_models::InternalDataModelBuilder;
-        use prisma_client_rust::query_core::{schema_builder, executor, BuildMode, QuerySchema, QueryExecutor, CoreError, InterpreterError, QueryGraphBuilderError};
-        use prisma_client_rust::{chrono, operator::Operator, serde_json, CountResult, Direction};
-
-        pub use prisma_client_rust::{query::{Error as QueryError}, NewClientError};
-
-        use serde::{Serialize, Deserialize};
-
+        use prisma_client_rust::{
+            chrono,
+            datamodel::parse_configuration,
+            operator::Operator,
+            prisma_models::{InternalDataModelBuilder, PrismaValue},
+            query::{QueryContext, Result as QueryResult},
+            query_core::{
+                executor, schema_builder, BuildMode, CoreError, InterpreterError, QueryExecutor,
+                QueryGraphBuilderError, QuerySchema, QueryValue, Selection,
+            },
+            serde_json, transform_equals, Args, BatchResult, Direction, FindManyArgs,
+            FindManySelectionArgs, SerializedWhere, SerializedWhereValue,
+        };
+        pub use prisma_client_rust::{query::Error as QueryError, NewClientError};
+        use serde::{Deserialize, Serialize};
+        use std::ops::Deref;
         use std::path::Path;
         use std::sync::Arc;
-        
+
         static DATAMODEL_STR: &'static str = #datamodel;
 
         pub struct PrismaClient {
@@ -47,7 +53,7 @@ pub fn generate(root: &Root) -> TokenStream {
             query_schema: Arc<QuerySchema>,
         }
 
-        pub async fn new_client() -> Result<PrismaClient, NewClientError> { 
+        pub async fn new_client() -> Result<PrismaClient, NewClientError> {
             let config = parse_configuration(DATAMODEL_STR)?.subject;
             let source = config
                 .datasources
@@ -58,7 +64,7 @@ pub fn generate(root: &Root) -> TokenStream {
             } else {
                 source.load_url(|key| std::env::var(key).ok())?
             };
-            
+
             // sqlite fix
             let url = if url.starts_with("file:") {
                 let path = url.split(":").nth(1).unwrap();
@@ -101,56 +107,56 @@ pub fn generate(root: &Root) -> TokenStream {
         }
 
         impl PrismaClient {
-            pub async fn _query_raw<T: serde::de::DeserializeOwned>(&self, query: &str) -> QueryResult<Vec<T>> {
-                let query = Query {
-                    ctx: QueryContext::new(&self.executor, self.query_schema.clone()),
-                    operation: "mutation".into(),
-                    method: "queryRaw".into(),
-                    inputs: vec![
-                        Input {
-                            name: "query".into(),
-                            value: Some(query.into()),
-                            ..Default::default()
-                        },
-                        Input {
-                            name: "parameters".into(),
-                            value: Some("[]".into()),
-                            ..Default::default()
-                        }
-                    ],
-                    name: "".into(),
-                    model: "".into(),
-                    outputs: vec![]
-                };
-
-                query.perform().await
-            }
-
-            pub async fn _execute_raw(&self, query: &str) -> QueryResult<i64> {
-                let query = Query {
-                    ctx: QueryContext::new(&self.executor, self.query_schema.clone()),
-                    operation: "mutation".into(),
-                    method: "executeRaw".into(),
-                    inputs: vec![
-                        Input {
-                            name: "query".into(),
-                            value: Some(query.into()),
-                            ..Default::default()
-                        },
-                        Input {
-                            // TODO: use correct value
-                            name: "parameters".into(),
-                            value: Some("[]".into()),
-                            ..Default::default()
-                        },
-                    ],
-                    name: "".into(),
-                    model: "".into(),
-                    outputs: vec![]
-                };
-
-                query.perform().await.map(|result: i64| result)
-            }
+            // pub async fn _query_raw<T: serde::de::DeserializeOwned>(&self, query: &str) -> QueryResult<Vec<T>> {
+            //     let query = Query {
+            //         ctx: QueryContext::new(&self.executor, self.query_schema.clone()),
+            //         operation: "mutation".into(),
+            //         method: "queryRaw".into(),
+            //         inputs: vec![
+            //             Input {
+            //                 name: "query".into(),
+            //                 value: Some(query.into()),
+            //                 ..Default::default()
+            //             },
+            //             Input {
+            //                 name: "parameters".into(),
+            //                 value: Some("[]".into()),
+            //                 ..Default::default()
+            //             }
+            //         ],
+            //         name: "".into(),
+            //         model: "".into(),
+            //         outputs: vec![]
+            //     };
+
+            //     query.perform().await
+            // }
+
+            // pub async fn _execute_raw(&self, query: &str) -> QueryResult<i64> {
+            //     let query = Query {
+            //         ctx: QueryContext::new(&self.executor, self.query_schema.clone()),
+            //         operation: "mutation".into(),
+            //         method: "executeRaw".into(),
+            //         inputs: vec![
+            //             Input {
+            //                 name: "query".into(),
+            //                 value: Some(query.into()),
+            //                 ..Default::default()
+            //             },
+            //             Input {
+            //                 // TODO: use correct value
+            //                 name: "parameters".into(),
+            //                 value: Some("[]".into()),
+            //                 ..Default::default()
+            //             },
+            //         ],
+            //         name: "".into(),
+            //         model: "".into(),
+            //         outputs: vec![]
+            //     };
+
+            //     query.perform().await.map(|result: i64| result)
+            // }
 
             #(#model_actions)*
         }
diff --git a/cli/src/generator/codegen/enums.rs b/cli/src/generator/codegen/enums.rs
index 83bd3ac0..f73e572d 100644
--- a/cli/src/generator/codegen/enums.rs
+++ b/cli/src/generator/codegen/enums.rs
@@ -21,11 +21,30 @@ pub fn generate(root: &Root) -> TokenStream {
             })
             .collect::<Vec<_>>();
 
+        let match_arms = e
+            .values
+            .iter()
+            .map(|v| {
+                let name = &v.name;
+                let variant_name = format_ident!("{}", v.name.to_case(Case::Pascal));
+
+                quote!(Self::#variant_name => #name.to_string())
+            })
+            .collect::<Vec<_>>();
+
         quote! {
             #[derive(Debug, Clone, Copy, Serialize, Deserialize)]
             pub enum #name {
                 #(#variants),*
             }
+
+            impl ToString for #name {
+                fn to_string(&self) -> String {
+                    match self {
+                        #(#match_arms),*
+                    }
+                }
+            }
         }
     });
 
@@ -45,11 +64,29 @@ pub fn generate(root: &Root) -> TokenStream {
             })
             .collect::<Vec<_>>();
 
+        let match_arms = e
+            .values
+            .iter()
+            .map(|name| {
+                let variant_name = format_ident!("{}", name.to_case(Case::Pascal));
+
+                quote!(Self::#variant_name => #name.to_string())
+            })
+            .collect::<Vec<_>>();
+
         quote! {
             #[derive(Debug, Clone, Copy, Serialize, Deserialize)]
             pub enum #name {
                 #(#variants),*
             }
+
+            impl ToString for #name {
+                fn to_string(&self) -> String {
+                    match self {
+                        #(#match_arms),*
+                    }
+                }
+            }
         }
     });
 
diff --git a/cli/src/generator/codegen/models.rs b/cli/src/generator/codegen/models.rs
index 368a9339..eb813597 100644
--- a/cli/src/generator/codegen/models.rs
+++ b/cli/src/generator/codegen/models.rs
@@ -4,11 +4,11 @@ use syn::Ident;
 
 use crate::generator::{
     ast::{dmmf::Document, Model, Field},
-    Root,
+    Root, GraphQLType,
 };
 
 struct Outputs {
-    outputs: Vec<TokenStream>,
+    outputs: Vec<String>
 }
 
 impl Outputs {
@@ -18,11 +18,8 @@ impl Outputs {
                 .fields
                 .iter()
                 .filter(|f| f.kind.include_in_struct())
-                .map(|field| {
-                    let field_name_string = &field.name;
-                    quote!(Output::new(#field_name_string))
-                })
-                .collect(),
+                .map(|f| f.name.to_string())
+                .collect()
         }
     }
 
@@ -30,10 +27,14 @@ impl Outputs {
         let Self { outputs } = self;
 
         quote! {
-            pub fn _outputs() -> Vec<Output> {
-                vec![
-                    #(#outputs),*
-                ]
+            pub fn _outputs() -> Vec<Selection> {
+                [#(#outputs),*]
+                    .into_iter()
+                    .map(|o| {
+                        let builder = Selection::builder(o);
+                        builder.build()
+                    })
+                    .collect()
             }
         }
     }
@@ -43,32 +44,75 @@ struct WithParams {
     pub with_fn: TokenStream,
     variants: Vec<TokenStream>,
     match_arms: Vec<TokenStream>,
+    from_args: Vec<TokenStream>
 }
 
 impl WithParams {
     pub fn new() -> Self {
-
         Self {
             with_fn: quote! {
-                pub fn with(mut self, param: WithParam) -> Self {
-                    self.with_params.push(param);
+                pub fn with(mut self, params: impl Into<WithParam>) -> Self {
+                    self.args = self.args.with(params.into());
                     self
                 }
             },
             variants: vec![],
             match_arms: vec![],
+            from_args: vec![]
         }
     }
-
-    fn add_variant(&mut self, variant: TokenStream, match_arm: TokenStream) {
-        self.variants.push(variant);
-        self.match_arms.push(match_arm);
+    
+    fn add_single_variant(&mut self, field_name: &str, model_module: &Ident, variant_name: &Ident) {
+        self.variants.push(quote!(#variant_name(super::#model_module::Args)));
+        self.match_arms.push(quote! {
+            Self::#variant_name(args) => {
+                let mut selections = super::#model_module::_outputs();
+                selections.extend(args.with_params.into_iter().map(Into::<Selection>::into));
+                
+                let mut builder = Selection::builder(#field_name);
+                builder.nested_selections(selections);
+                builder.build()
+            }
+        });
+        self.from_args.push(quote! {
+            impl From<super::#model_module::Args> for WithParam {
+                fn from(args: super::#model_module::Args) -> Self {
+                    Self::#variant_name(args)
+                }
+            }
+        });
+    }
+    
+    fn add_many_variant(&mut self, field_name: &str, model_module: &Ident, variant_name: &Ident) {
+        self.variants.push(quote!(#variant_name(super::#model_module::FindManyArgs)));
+        self.match_arms.push(quote! {
+            Self::#variant_name(args) => {
+                let FindManySelectionArgs {
+                    mut nested_selections,
+                    arguments
+                } = args.into();
+                nested_selections.extend(super::#model_module::_outputs());
+                
+                let mut builder = Selection::builder(#field_name);
+                builder.nested_selections(nested_selections)
+                    .set_arguments(arguments);
+                builder.build()
+            }
+        });
+        self.from_args.push(quote! {
+            impl From<super::#model_module::FindManyArgs> for WithParam {
+                fn from(args: super::#model_module::FindManyArgs) -> Self {
+                    Self::#variant_name(args)
+                }
+            }
+        });
     }
 
     pub fn quote(&self) -> TokenStream {
         let Self {
             variants,
             match_arms,
+            from_args,
             ..
         } = self;
 
@@ -76,14 +120,16 @@ impl WithParams {
             pub enum WithParam {
                 #(#variants),*
             }
-
-            impl WithParam {
-                pub fn to_output(self) -> Output {
+            
+            impl Into<Selection> for WithParam {
+                fn into(self) -> Selection {
                     match self {
                         #(#match_arms),*
                     }
                 }
             }
+            
+            #(#from_args)*
         }
     }
 }
@@ -117,9 +163,9 @@ impl SetParams {
             pub enum SetParam {
                 #(#variants),*
             }
-
-            impl SetParam {
-                pub fn to_field(self) -> Field {
+            
+            impl Into<(String, QueryValue)> for SetParam {
+                fn into(self) -> (String, QueryValue) {
                     match self {
                         #(#match_arms),*
                     }
@@ -152,7 +198,7 @@ impl OrderByParams {
         Self {
             order_by_fn: quote! {
                 pub fn order_by(mut self, param: OrderByParam) -> Self {
-                    self.order_by_params.push(param);
+                    self.args = self.args.order_by(param);
                     self
                 }
             },
@@ -161,9 +207,14 @@ impl OrderByParams {
         }
     }
 
-    fn add_variant(&mut self, variant: TokenStream, match_arm: TokenStream) {
-        self.variants.push(variant);
-        self.match_arms.push(match_arm);
+    fn add_variant(&mut self, field_name: &str, variant_name: &Ident) {
+        self.variants.push(quote!(#variant_name(Direction)));
+        self.match_arms.push(quote! {
+            Self::#variant_name(direction) => (
+                #field_name.to_string(), 
+                QueryValue::String(direction.to_string())
+            ) 
+        });
     }
 
     pub fn quote(&self) -> TokenStream {
@@ -177,9 +228,9 @@ impl OrderByParams {
             pub enum OrderByParam {
                 #(#variants),*
             }
-
-            impl OrderByParam {
-                pub fn to_field(self) -> Field {
+            
+            impl Into<(String, QueryValue)> for OrderByParam {
+                fn into(self) -> (String, QueryValue) {
                     match self {
                         #(#match_arms),*
                     }
@@ -198,30 +249,18 @@ struct PaginationParams {
 impl PaginationParams {
     pub fn new() -> Self {
         let pagination_fns = quote! {
-            pub fn skip(mut self, skip: usize) -> Self {
-                self.query.inputs.push(Input {
-                    name: "skip".into(),
-                    value: Some(serde_json::to_value(skip).unwrap()),
-                    ..Default::default()
-                });
+            pub fn skip(mut self, value: i64) -> Self {
+                self.args = self.args.skip(value);
                 self
             }
 
-            pub fn take(mut self, take: usize) -> Self {
-                self.query.inputs.push(Input {
-                    name: "take".into(),
-                    value: Some(serde_json::to_value(take).unwrap()),
-                    ..Default::default()
-                });
+            pub fn take(mut self, value: i64) -> Self {
+                self.args = self.args.take(value);
                 self
             }
 
-            pub fn cursor(mut self, cursor: Cursor) -> Self {
-                self.query.inputs.push(Input {
-                    name: "cursor".into(),
-                    fields: vec![cursor.to_field()],
-                    ..Default::default()
-                });
+            pub fn cursor(mut self, value: impl Into<Cursor>) -> Self {
+                self.args = self.args.cursor(value.into());
                 self
             }
         };
@@ -233,9 +272,17 @@ impl PaginationParams {
         }
     }
 
-    pub fn add_variant(&mut self, variant: TokenStream, match_arm: TokenStream) {
-        self.cursor_variants.push(variant);
-        self.cursor_match_arms.push(match_arm);
+    pub fn add_variant(&mut self, field_name: &str, variant_name: &Ident, cursor_type: &GraphQLType) {
+        let rust_type = cursor_type.tokens();
+        self.cursor_variants.push(quote!(#variant_name(#rust_type)));
+        
+        let prisma_value = cursor_type.to_prisma_value(&format_ident!("cursor"));
+        self.cursor_match_arms.push(quote! {
+            Self::#variant_name(cursor) => (
+                #field_name.to_string(),
+                #prisma_value.into()
+            )
+        });
     }
 
     pub fn quote(&self) -> TokenStream {
@@ -249,9 +296,9 @@ impl PaginationParams {
             pub enum Cursor {
                 #(#cursor_variants),*
             }
-
-            impl Cursor {
-                fn to_field(self) -> Field {
+            
+            impl Into<(String, QueryValue)> for Cursor {
+                fn into(self) -> (String, QueryValue) {
                     match self {
                         #(#cursor_match_arms),*
                     }
@@ -344,7 +391,7 @@ impl ModelQueryModules {
 
 struct WhereParams {
     pub variants: Vec<TokenStream>,
-    pub to_field: Vec<TokenStream>,
+    pub to_query_value: Vec<TokenStream>,
     pub unique_variants: Vec<TokenStream>,
     pub from_unique_match_arms: Vec<TokenStream>,
     pub from_optional_uniques: Vec<TokenStream>
@@ -354,7 +401,7 @@ impl WhereParams {
     pub fn new() -> Self {
         Self {
             variants: vec![],
-            to_field: vec![],
+            to_query_value: vec![],
             unique_variants: vec![],
             from_unique_match_arms: vec![],
             from_optional_uniques: vec![]
@@ -363,7 +410,7 @@ impl WhereParams {
 
     pub fn add_variant(&mut self, variant: TokenStream, match_arm: TokenStream) {
         self.variants.push(variant);
-        self.to_field.push(match_arm);
+        self.to_query_value.push(match_arm);
     }
 
     pub fn add_unique_variant(
@@ -412,7 +459,7 @@ impl WhereParams {
     pub fn quote(&self) -> TokenStream {
         let Self {
             variants,
-            to_field,
+            to_query_value,
             unique_variants,
             from_unique_match_arms,
             from_optional_uniques
@@ -422,11 +469,11 @@ impl WhereParams {
             pub enum WhereParam {
                 #(#variants),*
             }
-
-            impl WhereParam {
-                pub fn to_field(self) -> Field {
+            
+            impl Into<SerializedWhere> for WhereParam {
+                fn into(self) -> SerializedWhere {
                     match self {
-                        #(#to_field),*
+                        #(#to_query_value),*
                     }
                 }
             }
@@ -529,8 +576,8 @@ pub fn generate(root: &Root) -> Vec<TokenStream> {
         let mut model_set_params = SetParams::new();
         let mut model_where_params = WhereParams::new();
         let mut model_actions = Actions::new();
-
-        let model_name_pascal_string = model.name.to_case(Case::Pascal);
+        
+        let model_name_string = &model.name;
         let model_name_snake = format_ident!("{}", model.name.to_case(Case::Snake));
  
         for op in Document::operators() {
@@ -540,13 +587,23 @@ pub fn generate(root: &Root) -> Vec<TokenStream> {
             model_where_params.add_variant(
                 quote!(#variant_name(Vec<WhereParam>)),
                 quote! {
-                    Self::#variant_name(value) => Field {
-                        name: #op_action.into(),
-                        list: true,
-                        wrap_list: true,
-                        fields: Some(value.into_iter().map(|f| f.to_field()).collect()),
-                        ..Default::default()
-                    }
+                    Self::#variant_name(value) => (
+                        #op_action.to_string(),
+                        SerializedWhereValue::List(
+                            value
+                                .into_iter()
+                                .map(|v| {
+                                    QueryValue::Object(
+                                        transform_equals(
+                                            vec![Into::<SerializedWhere>::into(v)].into_iter(),
+                                        )
+                                        .into_iter()
+                                        .collect(),
+                                    )
+                                })
+                                .collect(),
+                        ),
+                    )
                 },
             );
         }
@@ -559,6 +616,8 @@ pub fn generate(root: &Root) -> Vec<TokenStream> {
             let mut variant_data_as_types = vec![];
             let mut variant_data_as_args = vec![];
             let mut variant_data_as_destructured = vec![];
+            let mut variant_data_as_query_values = vec![];
+            let variant_data_names = unique.fields.iter().map(ToString::to_string).collect::<Vec<_>>();
             
             for field in &unique.fields {
                 let model_field = model.fields.iter().find(|mf| &mf.name == field).unwrap();
@@ -574,24 +633,11 @@ pub fn generate(root: &Root) -> Vec<TokenStream> {
                 variant_data_as_args.push(quote!(#field_name_snake: #field_type));
                 variant_data_as_types.push(field_type);
                 variant_data_as_destructured.push(quote!(#field_name_snake));
+                variant_data_as_query_values.push(model_field.type_as_query_value(&field_name_snake));
             }
 
             let field_name_string = unique.fields.join("_");
 
-            let variant_data_where_params = unique.fields.iter().map(|f| {
-                let model_field = model.fields.iter().find(|mf| &mf.name == f).unwrap();
-                
-                let field_name = format_ident!("{}", f.to_case(Case::Snake));
-                let equals_variant = format_ident!("{}Equals", f.to_case(Case::Pascal));
-                
-                let field_name = match model_field.is_required {
-                    true => quote!(#field_name),
-                    false => quote!(Some(#field_name))
-                };
-
-                quote!(WhereParam::#equals_variant(#field_name))
-            }).collect::<Vec<_>>();
-
             model_query_module.add_compound_field(
                 quote! {
                     pub fn #accessor_name<T: From<UniqueWhereParam>>(#(#variant_data_as_args),*) -> T {
@@ -603,19 +649,10 @@ pub fn generate(root: &Root) -> Vec<TokenStream> {
             model_where_params.add_unique_variant(
                 quote!(#variant_name(#(#variant_data_as_types),*)),
                 quote! {
-                    Self::#variant_name(#(#variant_data_as_destructured),*) => {
-                        Field {
-                            name: #field_name_string.into(),
-                            fields: Some(transform_equals(vec![
-                                    #(#variant_data_where_params),*
-                                ]
-                                .into_iter()
-                                .map(|f| f.to_field())
-                                .collect()
-                            )),
-                            ..Default::default()
-                        }
-                    }
+                    Self::#variant_name(#(#variant_data_as_destructured),*) => (
+                        #field_name_string.to_string(),
+                        SerializedWhereValue::Object(vec![#((#variant_data_names.to_string(), #variant_data_as_query_values)),*])
+                    )
                 },
                 quote! {
                     UniqueWhereParam::#variant_name(#(#variant_data_as_destructured),*) => Self::#variant_name(#(#variant_data_as_destructured),*)
@@ -655,15 +692,21 @@ pub fn generate(root: &Root) -> Vec<TokenStream> {
                     model_where_params.add_variant(
                         quote!(#variant_name(Vec<super::#relation_type_snake::WhereParam>)),
                         quote! {
-                            Self::#variant_name(value) => Field {
-                                name: #field_string.into(),
-                                fields: Some(vec![Field {
-                                    name: #method_action_string.into(),
-                                    fields: Some(value.into_iter().map(|f| f.to_field()).collect()),
-                                    ..Default::default()
-                                }]),
-                                ..Default::default()
-                            }
+                            Self::#variant_name(value) => (
+                                #field_string.to_string(),
+                                SerializedWhereValue::Object(vec![(
+                                    #method_action_string.to_string(),
+                                    QueryValue::Object(
+                                        transform_equals(
+                                            value
+                                                .into_iter()
+                                                .map(Into::<SerializedWhere>::into)    
+                                        )
+                                        .into_iter()
+                                        .collect()
+                                    ),
+                                )])
+                            )
                         },
                     );
 
@@ -677,8 +720,8 @@ pub fn generate(root: &Root) -> Vec<TokenStream> {
                 // Relation actions eg. Fetch, Link, Unlink
                 if field.is_list {
                     field_query_module.add_method(quote! {
-                        pub fn fetch(params: Vec<#relation_type_snake::WhereParam>) -> WithParam {
-                            WithParam::#field_pascal(params)
+                        pub fn fetch(params: Vec<#relation_type_snake::WhereParam>) -> #relation_type_snake::FindManyArgs {
+                            #relation_type_snake::FindManyArgs::new(params)
                         }
 
                         pub fn link<T: From<Link>>(params: Vec<#relation_type_snake::UniqueWhereParam>) -> T {
@@ -703,74 +746,60 @@ pub fn generate(root: &Root) -> Vec<TokenStream> {
                     model_set_params.add_variant(
                         quote!(#link_variant(Vec<super::#relation_type_snake::UniqueWhereParam>)),
                         quote! {
-                            Self::#link_variant(where_params) => Field {
-                                name: #field_string.into(),
-                                fields: Some(vec![
-                                    Field {
-                                        name: "connect".into(),
-                                        fields: Some(transform_equals(
-                                            where_params
-                                                .into_iter()
-                                                .map(|param| Into::<super::#relation_type_snake::WhereParam>::into(param)
-                                                    .to_field())
-                                                .collect()
-                                        )),
-                                        list: true,
-                                        wrap_list: true,
-                                        ..Default::default()
-                                    }
-                                ]),
-                                ..Default::default()
-                            }
+                            SetParam::#link_variant(where_params) => (
+                                #field_string.to_string(),
+                                QueryValue::Object(
+                                    vec![(
+                                        "connect".to_string(),
+                                        QueryValue::Object(
+                                            transform_equals(
+                                                where_params
+                                                    .into_iter()
+                                                    .map(Into::<super::#relation_type_snake::WhereParam>::into)
+                                                    .map(Into::into)
+                                            )
+                                            .into_iter()
+                                            .collect()
+                                        )
+                                    )]
+                                    .into_iter()
+                                    .collect()
+                                )
+                            )
                         }
                     );
 
                     model_set_params.add_variant(
                         quote!(#unlink_variant(Vec<super::#relation_type_snake::UniqueWhereParam>)),
                         quote! {
-                            Self::#unlink_variant(where_params) => Field {
-                                name: #field_string.into(),
-                                fields: Some(vec![
-                                    Field {
-                                        name: "disconnect".into(),
-                                        list: true,
-                                        wrap_list: true,
-                                        fields: Some(transform_equals(
-                                            where_params
-                                                .into_iter()
-                                                .map(|param| Into::<super::#relation_type_snake::WhereParam>::into(param)
-                                                    .to_field())
-                                                .collect()
-                                        )),
-                                        ..Default::default()
-                                    }
-                                ]),
-                                ..Default::default()
-                            }
-                        },
-                    );
-
-                    model_with_params.add_variant(
-                        quote!(#field_pascal(Vec<super::#relation_type_snake::WhereParam>)),
-                        quote! {
-                            Self::#field_pascal(where_params) => Output {
-                                name: #field_string.into(),
-                                outputs: super::#relation_type_snake::_outputs(),
-                                inputs: if where_params.len() > 0 {
-                                    vec![Input {
-                                        name: "where".into(),
-                                        fields: where_params
+                            SetParam::#unlink_variant(where_params) => (
+                                #field_string.to_string(),
+                                QueryValue::Object(
+                                    vec![(
+                                        "disconnect".to_string(),
+                                        QueryValue::Object(
+                                            transform_equals( 
+                                                where_params
+                                                    .into_iter()
+                                                    .map(Into::<super::#relation_type_snake::WhereParam>::into)
+                                                    .map(Into::into)
+                                            )
                                             .into_iter()
-                                            .map(|param| Into::<super::#relation_type_snake::WhereParam>::into(param)
-                                                .to_field())
-                                            .collect(),
-                                        ..Default::default()
-                                    }]
-                                } else { vec![] },
-                                ..Default::default()
-                            }
+                                            .collect()
+                                        )
+                                    )]
+                                    .into_iter()
+                                    .collect()
+                                )
+                            )
                         },
                     );
+                    
+                    model_with_params.add_many_variant(
+                        field_string,
+                        &relation_type_snake,
+                        &field_pascal
+                    );
 
                     model_data_struct.add_relation(
                         quote! {
@@ -788,8 +817,8 @@ pub fn generate(root: &Root) -> Vec<TokenStream> {
                     );
                 } else {
                     field_query_module.add_method(quote! {
-                        pub fn fetch() -> WithParam {
-                            WithParam::#field_pascal
+                        pub fn fetch() -> #relation_type_snake::Args {
+                            #relation_type_snake::Args::new()
                         }
 
                         pub fn link<T: From<Link>>(value: #relation_type_snake::UniqueWhereParam) -> T {
@@ -810,19 +839,26 @@ pub fn generate(root: &Root) -> Vec<TokenStream> {
                     model_set_params.add_variant(
                         quote!(#link_variant(super::#relation_type_snake::UniqueWhereParam)),
                         quote! {
-                            Self::#link_variant(where_param) => Field {
-                                name: #field_string.into(),
-                                fields: Some(vec![
-                                    Field {
-                                        name: "connect".into(),
-                                        fields: Some(transform_equals(vec![
-                                            Into::<super::#relation_type_snake::WhereParam>::into(where_param).to_field()
-                                        ])),
-                                        ..Default::default()
-                                    }
-                                ]),
-                                ..Default::default()
-                            }
+                            SetParam::#link_variant(where_param) => (
+                                #field_string.to_string(),
+                                QueryValue::Object(
+                                    vec![(
+                                        "connect".to_string(),
+                                        QueryValue::Object(
+                                            transform_equals(
+                                                vec![where_param]
+                                                    .into_iter()
+                                                    .map(Into::<super::#relation_type_snake::WhereParam>::into)
+                                                    .map(Into::into)
+                                            )
+                                            .into_iter()
+                                            .collect()
+                                        )
+                                    )]
+                                    .into_iter()
+                                    .collect()
+                                )
+                            )
                         }
                     );
 
@@ -836,28 +872,25 @@ pub fn generate(root: &Root) -> Vec<TokenStream> {
                         model_set_params.add_variant(
                             quote!(#unlink_variant),
                             quote! {
-                                Self::#unlink_variant => Field {
-                                    name: #field_string.into(),
-                                    fields: Some(vec![Field {
-                                        name: "disconnect".into(),
-                                        value: Some(true.into()),
-                                        ..Default::default()
-                                    }]),
-                                    ..Default::default()
-                                }
+                                SetParam::#unlink_variant => (
+                                    #field_string.to_string(),
+                                    QueryValue::Object(
+                                        vec![(
+                                            "disconnect".to_string(),
+                                            QueryValue::Boolean(true)
+                                        )]
+                                        .into_iter()
+                                        .collect()
+                                    )
+                                )
                             },
                         );
                     }
 
-                    model_with_params.add_variant(
-                        quote!(#field_pascal),
-                        quote! {
-                            Self::#field_pascal => Output {
-                                name: #field_string.into(),
-                                outputs: super::#relation_type_snake::_outputs(),
-                                ..Default::default()
-                            }
-                        },
+                    model_with_params.add_single_variant(
+                        field_string,
+                        &relation_type_snake,
+                        &field_pascal
                     );
                     
                     let accessor_type = if field.is_required {
@@ -921,7 +954,7 @@ pub fn generate(root: &Root) -> Vec<TokenStream> {
                 if field.required_on_create() {
                     model_actions.push_required_arg(
                         quote!(#field_snake: #field_snake::Link,),
-                        quote!(input_fields.push(SetParam::from(#field_snake).to_field());),
+                        quote!(params.push(#field_snake.into());),
                    );
                 }
             }
@@ -930,22 +963,18 @@ pub fn generate(root: &Root) -> Vec<TokenStream> {
                 let field_set_variant = SetParams::field_set_variant(&field.name);
 
                 if !field.prisma {
+                    let converter = field.type_as_query_value(&format_ident!("value"));
                     let (field_set_variant_type, field_content) = if field.is_list {
                         (
                             quote!(Vec<#field_type>),
-                            quote!(fields: Some(value.iter().map(|f| f.to_field()).collect())),
+                            quote!(converter),
                         )
                     } else {
-                        let typ = if field.is_required {
-                            quote!(#field_type)
+                        if field.is_required {
+                            (quote!(#field_type), converter)
                         } else {
-                            quote!(Option<#field_type>)
-                        };
-                        
-                        (
-                            typ,
-                            quote!(value: Some(serde_json::to_value(value).unwrap())),
-                        )
+                            (quote!(Option<#field_type>), quote!(value.map(|value| #converter).unwrap_or(QueryValue::Null)))
+                        }
                     };
 
                     field_query_module.add_method(quote! {
@@ -962,31 +991,33 @@ pub fn generate(root: &Root) -> Vec<TokenStream> {
                             }
                         }
                     });
-
+                    
+                    
                     model_set_params.add_variant(
                         quote!(#field_set_variant(#field_set_variant_type)),
                         quote! {
-                            Self::#field_set_variant(value) => Field {
-                                name: #field_string.into(),
-                                value: Some(serde_json::to_value(value).unwrap()),
-                                ..Default::default()
-                            }
+                            SetParam::#field_set_variant(value) => (
+                                #field_string.to_string(),
+                                #field_content
+                            )
                         },
                     );
 
                     let equals_variant_name = format_ident!("{}Equals", &field_pascal);
                     let equals_variant = quote!(#equals_variant_name(#field_set_variant_type));
+                    let type_as_query_value = field.type_as_query_value(&format_ident!("value"));
+                    
+                    let type_as_query_value = if field.is_required {
+                        type_as_query_value
+                    } else {
+                        quote!(value.map(|value| #type_as_query_value).unwrap_or(QueryValue::Null))
+                    };
 
                     let match_arm = quote! {
-                        Self::#equals_variant_name(value) => Field {
-                            name: #field_string.into(),
-                            fields: Some(vec![Field {
-                                name: "equals".into(),
-                                #field_content,
-                                ..Default::default()
-                            }]),
-                            ..Default::default()
-                        }
+                        Self::#equals_variant_name(value) => (
+                            #field_string.to_string(),
+                            SerializedWhereValue::Object(vec![("equals".to_string(), #type_as_query_value)])
+                        )
                     };
 
                     match (field.is_id, field.is_unique, field.is_required)  {
@@ -1083,6 +1114,8 @@ pub fn generate(root: &Root) -> Vec<TokenStream> {
                         let typ = if method.is_list {
                             quote!(Vec<#typ>)
                         } else { typ };
+                        
+                        let prisma_value_type = method.typ.to_prisma_value(&format_ident!("value"));
 
                         let variant_name = format_ident!("{}{}", method.name.to_case(Case::Pascal), field_pascal);
 
@@ -1096,46 +1129,34 @@ pub fn generate(root: &Root) -> Vec<TokenStream> {
                         model_set_params.add_variant(
                             quote!(#variant_name(#typ)),
                             quote! {
-                                Self::#variant_name(value) => Field {
-                                    name: #field_string.into(),
-                                    fields: Some(vec![Field{
-                                        name: #method_action.into(),
-                                        value: Some(serde_json::to_value(value).unwrap()),
-                                        ..Default::default()
-                                    }]),
-                                    ..Default::default()
-                                }
+                                SetParam::#variant_name(value) => (
+                                    #field_string.to_string(),
+                                    QueryValue::Object(
+                                        vec![(
+                                            #method_action.to_string(),
+                                            #prisma_value_type.into()
+                                        )]
+                                            .into_iter()
+                                            .collect()
+                                    )
+                                )
                             }
                         );
                     }
                 }
 
-                model_order_by_params.add_variant(
-                    quote!(#field_pascal(Direction)),
-                    quote! {
-                        Self::#field_pascal(direction) => Field {
-                            name: #field_string.into(),
-                            value: Some(serde_json::to_value(direction).unwrap()),
-                            ..Default::default()
-                        }
-                    },
-                );
+                model_order_by_params.add_variant(field_string, &field_pascal);
 
                 model_pagination_params.add_variant(
-                    quote!(#field_pascal(#field_type)),
-                    quote! {
-                        Self::#field_pascal(value) => Field {
-                            name: #field_string.into(),
-                            value: Some(serde_json::to_value(value).unwrap()),
-                            ..Default::default()
-                        }
-                    },
+                    field_string,
+                    &field_pascal,
+                    &field.field_type
                 );
 
                 if field.required_on_create() {
                     model_actions.push_required_arg(
                         quote!(#field_snake: #field_snake::Set,),
-                        quote!(input_fields.push(SetParam::from(#field_snake).to_field());),
+                        quote!(params.push(#field_snake.into());),
                     );
                 }
             }
@@ -1163,38 +1184,35 @@ pub fn generate(root: &Root) -> Vec<TokenStream> {
                         field.name.to_string()
                     };
 
-                    let (typ, field_contents) = if method.is_list {
+                    let (typ, value_as_query_value) = if method.is_list {
+                        let prisma_value_converter = method.typ.to_prisma_value(&format_ident!("v"));
+                        
                         (
                             quote!(Vec<#typ>),
                             quote! {
-                                list: true,
-                                fields: Some(value.iter().map(|v| Field {
-                                    value: Some(serde_json::to_value(v).unwrap()),
-                                    ..Default::default()
-                                }).collect()),
+                                QueryValue::List(
+                                    value
+                                        .into_iter()
+                                        .map(|v| #prisma_value_converter.into())
+                                        .collect()
+                                )
                             },
                         )
                     } else {
+                        let as_prisma_value = method.typ.to_prisma_value(&format_ident!("value"));
                         (
                             typ,
-                            quote! {
-                                value: Some(serde_json::to_value(value).unwrap()),
-                            },
+                            quote!(#as_prisma_value.into()),
                         )
                     };
 
                     model_where_params.add_variant(
                         quote!(#variant_name(#typ)),
                         quote! {
-                            Self::#variant_name(value) => Field {
-                                name: #field_name.into(),
-                                fields: Some(vec![Field {
-                                    name: #method_action_string.into(),
-                                    #field_contents
-                                    ..Default::default()
-                                }]),
-                                ..Default::default()
-                            }
+                            Self::#variant_name(value) => (
+                                #field_name.to_string(),
+                                SerializedWhereValue::Object(vec![(#method_action_string.to_string(), #value_as_query_value)])
+                            )
                         },
                     );
 
@@ -1247,126 +1265,63 @@ pub fn generate(root: &Root) -> Vec<TokenStream> {
 
                 #where_params
 
+                pub type FindManyArgs = prisma_client_rust::FindManyArgs<WhereParam, WithParam, OrderByParam, Cursor>;
+                    
                 pub struct FindMany<'a> {
-                    query: Query<'a>,
-                    order_by_params: Vec<OrderByParam>,
-                    with_params: Vec<WithParam>
+                    ctx: QueryContext<'a>,
+                    args: FindManyArgs,
                 }
 
                 impl<'a> FindMany<'a> {
                     pub async fn exec(self) -> QueryResult<Vec<Data>> {
-                        let Self {
-                            mut query,
-                            order_by_params,
-                            with_params
-                        } = self;
-
-                        if order_by_params.len() > 0 {
-                            query.inputs.push(Input {
-                                name: "orderBy".into(),
-                                fields: order_by_params
-                                    .into_iter()
-                                    .map(|f| f.to_field())
-                                    .collect(),
-                                ..Default::default()
-                            });
-                        }
-
-                        query.outputs.extend(with_params
-                            .into_iter()
-                            .map(|f| f.to_output())
-                            .collect::<Vec<_>>());
-
-                        query.perform().await
+                        let Self { ctx, args } = self;
+                        ctx.execute(args.to_operation(#model_name_string, _outputs()))
+                            .await
                     }
 
                     pub fn delete(self) -> DeleteMany<'a> {
+                        let Self {
+                            ctx,
+                            args
+                        } = self;
+                        
                         DeleteMany {
-                            query: Query {
-                                operation: "mutation".into(),
-                                method: "deleteMany".into(),
-                                model: #model_name_pascal_string.into(),
-                                outputs: vec! [
-                                    Output::new("count"),
-                                ],
-                                ..self.query
-                            }
+                            ctx,
+                            args: DeleteManyArgs::new(args.where_params),
                         }
                     }
 
                     pub fn update(mut self, params: Vec<SetParam>) -> UpdateMany<'a> {
-                        self.query.inputs.push(Input {
-                            name: "data".into(),
-                            fields: params
-                                .into_iter()
-                                .map(|param| {
-                                    let mut field = param.to_field();
-
-                                    if let Some(value) = field.value {
-                                        field.fields = Some(vec![Field {
-                                            name: "set".into(),
-                                            value: Some(value),
-                                            ..Default::default()
-                                        }]);
-                                        field.value = None;
-                                    }
-
-                                    field
-                                })
-                                .collect(),
-                            ..Default::default()
-                        });
-
+                        let Self {
+                            ctx,
+                            args
+                        } = self;
+                        
                         UpdateMany {
-                            query: Query {
-                                operation: "mutation".into(),
-                                method: "updateMany".into(),
-                                outputs: vec! [
-                                    Output::new("count"),
-                                ],
-                                ..self.query
-                            }
+                            ctx,
+                            args: UpdateManyArgs::new(args.where_params, params),
                         }
                     }
 
-                    #order_by_fn
-
                     #with_fn
 
+                    #order_by_fn
+
                     #pagination_fns
                 }
+                
+                pub type FindFirstArgs = prisma_client_rust::FindFirstArgs<WhereParam, WithParam, OrderByParam, Cursor>;
 
                 pub struct FindFirst<'a> {
-                    query: Query<'a>,
-                    order_by_params: Vec<OrderByParam>,
-                    with_params: Vec<WithParam>
+                    ctx: QueryContext<'a>,
+                    args: FindFirstArgs,
                 }
 
                 impl<'a> FindFirst<'a> {
                     pub async fn exec(self) -> QueryResult<Option<Data>> {
-                        let Self {
-                            mut query,
-                            order_by_params,
-                            with_params
-                        } = self;
-
-                        if order_by_params.len() > 0 {
-                            query.inputs.push(Input {
-                                name: "orderBy".into(),
-                                fields: order_by_params
-                                    .into_iter()
-                                    .map(|f| f.to_field())
-                                    .collect(),
-                                ..Default::default()
-                            });
-                        }
-
-                        query.outputs.extend(with_params
-                            .into_iter()
-                            .map(|f| f.to_output())
-                            .collect::<Vec<_>>());
-
-                        query.perform().await
+                        let Self { ctx, args } = self;
+                        ctx.execute(args.to_operation(#model_name_string, _outputs()))
+                            .await
                     }
 
                     #with_fn
@@ -1375,118 +1330,91 @@ pub fn generate(root: &Root) -> Vec<TokenStream> {
 
                     #pagination_fns
                 }
-
+                
+                pub type Args = prisma_client_rust::Args<WithParam>;
+                pub type FindUniqueArgs = prisma_client_rust::FindUniqueArgs<WhereParam, WithParam>;
+                
                 pub struct FindUnique<'a> {
-                    query: Query<'a>,
-                    with_params: Vec<WithParam>
+                    ctx: QueryContext<'a>,
+                    args: FindUniqueArgs,
                 }
 
                 impl<'a> FindUnique<'a> {
                     pub async fn exec(self) -> QueryResult<Option<Data>> {
-                        let Self {
-                            mut query,
-                            with_params
-                        } = self;
-
-                        query.outputs.extend(with_params
-                            .into_iter()
-                            .map(|f| f.to_output())
-                            .collect::<Vec<_>>());
-
-                        query.perform().await
+                        let Self { ctx, args } = self;
+                        ctx.execute(args.to_operation(#model_name_string, _outputs()))
+                            .await
                     }
 
                     pub fn delete(self) -> Delete<'a> {
-                        Delete {
-                            query: Query {
-                                operation: "mutation".into(),
-                                method: "deleteOne".into(),
-                                model: #model_name_pascal_string.into(),
-                                ..self.query
-                            },
-                            with_params: vec![]
-                        }
+                        let Self {
+                            ctx,
+                            args
+                        } = self;
+                        
+                        let FindUniqueArgs {
+                            where_param,
+                            with_params
+                        } = args;
+                        
+                        Delete { ctx, args: DeleteArgs::new(where_param, with_params) }
                     }
 
-                    pub fn update(mut self, params: Vec<SetParam>) -> UpdateUnique<'a> {
-                        self.query.inputs.push(Input {
-                            name: "data".into(),
-                            fields: params
-                                .into_iter()
-                                .map(|param| {
-                                    let mut field = param.to_field();
-
-                                    if let Some(value) = field.value {
-                                        field.fields = Some(vec![Field {
-                                            name: "set".into(),
-                                            value: Some(value),
-                                            ..Default::default()
-                                        }]);
-                                        field.value = None;
-                                    }
-
-                                    field
-                                })
-                                .collect(),
-                            ..Default::default()
-                        });
+                    #with_fn
 
-                        UpdateUnique {
-                            query: Query {
-                                operation: "mutation".into(),
-                                method: "updateOne".into(),
-                                ..self.query
-                            },
-                            with_params: vec![]
+                    pub fn update(mut self, params: Vec<SetParam>) -> Update<'a> {
+                        let Self {
+                            ctx,
+                            args
+                        } = self;
+                        
+                        let FindUniqueArgs {
+                            where_param,
+                            with_params
+                        } = args;
+                        
+                        Update {
+                            ctx,
+                            args: UpdateArgs::new(where_param, params, with_params)
                         }
                     }
-
-                    #with_fn
                 }
+                
+                pub type CreateArgs = prisma_client_rust::CreateArgs<SetParam, WithParam>;
 
                 pub struct Create<'a> {
-                    query: Query<'a>,
-                    with_params: Vec<WithParam>
+                    ctx: QueryContext<'a>,
+                    args: CreateArgs,
                 }
 
                 impl<'a> Create<'a> {
                     pub async fn exec(self) -> QueryResult<Data> {
-                        let Self {
-                            mut query,
-                            with_params
-                        } = self;
-
-                        query.outputs.extend(with_params
-                            .into_iter()
-                            .map(|f| f.to_output())
-                            .collect::<Vec<_>>());
-
-                        query.perform().await
+                        let Self { ctx, args } = self;
+                        ctx.execute(args.to_operation(#model_name_string, _outputs()))
+                            .await
                     }
 
                     #with_fn
                 }
 
-                pub struct UpdateUnique<'a> {
-                    query: Query<'a>,
-                    with_params: Vec<WithParam>
+                pub type UpdateArgs = prisma_client_rust::UpdateArgs<WhereParam, SetParam, WithParam>;
+
+                pub struct Update<'a> {
+                    ctx: QueryContext<'a>,
+                    args: UpdateArgs
                 }
 
-                impl<'a> UpdateUnique<'a> {
+                impl<'a> Update<'a> {
                     pub async fn exec(self) -> QueryResult<Option<Data>> {
                         let Self {
-                            mut query,
-                            with_params,
+                            ctx,
+                            args,
                         } = self;
                         
-                        query.outputs.extend(
-                            with_params
-                                .into_iter()
-                                .map(|f| f.to_output())
-                                .collect::<Vec<_>>(),
-                        );
+                        let result = ctx.execute(args.to_operation(#model_name_string, _outputs()))
+                            .await;
                         
-                        match query.perform().await {
+                        match result {
                             Err(QueryError::Execute(CoreError::InterpreterError(InterpreterError::InterpretationError(
                                 msg,
                                 Some(interpreter_error),
@@ -1504,87 +1432,70 @@ pub fn generate(root: &Root) -> Vec<TokenStream> {
                     }
 
                     #with_fn
+                    
                 }
+                
+                pub type UpdateManyArgs = prisma_client_rust::UpdateManyArgs<WhereParam, SetParam>;
 
                 pub struct UpdateMany<'a> {
-                    query: Query<'a>
+                    ctx: QueryContext<'a>,
+                    args: UpdateManyArgs
                 }
 
                 impl<'a> UpdateMany<'a> {
-                    pub async fn exec(self) -> QueryResult<usize> {
-                        self.query.perform().await.map(|res: CountResult| res.count)
+                    pub async fn exec(self) -> QueryResult<i64> {
+                        let Self { ctx, args } = self;
+                        ctx.execute(args.to_operation(#model_name_string)).await.map(|res: BatchResult| res.count)
                     }
                 }
+                
+                pub type UpsertArgs = prisma_client_rust::UpsertArgs<WhereParam, SetParam, WithParam>;
 
                 pub struct Upsert<'a> {
-                    query: Query<'a>,
+                    ctx: QueryContext<'a>,
+                    args: UpsertArgs
                 }
 
                 impl<'a> Upsert<'a> {
                     pub async fn exec(self) -> QueryResult<Data> {
-                        self.query.perform().await
+                        let Self { ctx, args } = self;
+                        ctx.execute(args.to_operation(#model_name_string, _outputs()))
+                            .await
                     }
 
                     pub fn create(
                         mut self,
                         #(#required_args)*
-                        params: Vec<SetParam>
+                        mut params: Vec<SetParam>
                     ) -> Self {
-                        let mut input_fields = params.into_iter().map(|p| p.to_field()).collect::<Vec<_>>();
-
                         #(#required_arg_pushes)*
 
-                        self.query.inputs.push(Input {
-                            name: "create".into(),
-                            fields: input_fields,
-                            ..Default::default()
-                        });
+                        self.args = self.args.create(params);
 
                         self
                     }
 
                     pub fn update(mut self, params: Vec<SetParam>) -> Self {
-                        self.query.inputs.push(Input {
-                            name: "update".into(),
-                            fields: params
-                                .into_iter()
-                                .map(|param| {
-                                    let mut field = param.to_field();
-                                    if let Some(value) = field.value {
-                                        field.fields = Some(vec![Field {
-                                            name: "set".into(),
-                                            value: Some(value),
-                                            ..Default::default()
-                                        }]);
-                                        field.value = None;
-                                    }
-                                    field
-                                })
-                                .collect(),
-                            ..Default::default()
-                        });
+                        self.args = self.args.update(params);
+                        
                         self
                     }
                 }
-
+                
+                pub type DeleteArgs = prisma_client_rust::DeleteArgs<WhereParam, WithParam>;
+                
                 pub struct Delete<'a> {
-                    query: Query<'a>,
-                    with_params: Vec<WithParam>
+                    ctx: QueryContext<'a>,
+                    args: DeleteArgs
                 }
 
                 impl<'a> Delete<'a> {
                     pub async fn exec(self) -> QueryResult<Option<Data>> {
-                        let Self {
-                            mut query,
-                            with_params
-                        } = self;
-
-                        query.outputs.extend(with_params
-                            .into_iter()
-                            .map(|f| f.to_output())
-                            .collect::<Vec<_>>());
-
-                        match query.perform().await {
+                        let Self { ctx, args } = self;
+                        
+                        let result = ctx.execute(args.to_operation(#model_name_string, _outputs())).await;
+                        
+                        match result {
                             Err(QueryError::Execute(CoreError::InterpreterError(InterpreterError::InterpretationError(
                                 msg,
                                 Some(interpreter_error),
@@ -1604,13 +1515,17 @@ pub fn generate(root: &Root) -> Vec<TokenStream> {
                     #with_fn
                 }
                 
+                pub type DeleteManyArgs = prisma_client_rust::DeleteManyArgs<WhereParam>;
+                
                 pub struct DeleteMany<'a> {
-                    query: Query<'a>
+                    ctx: QueryContext<'a>,
+                    args: DeleteManyArgs
                 }
 
                 impl<'a> DeleteMany<'a> {
-                    pub async fn exec(self) -> QueryResult<usize> {
-                        self.query.perform().await.map(|res: CountResult| res.count)
+                    pub async fn exec(self) -> QueryResult<i64> {
+                        let Self { ctx, args } = self;
+                        ctx.execute(args.to_operation(#model_name_string)).await.map(|res: BatchResult| res.count)
                     }
                 }
 
@@ -1619,144 +1534,41 @@ pub fn generate(root: &Root) -> Vec<TokenStream> {
                 }
 
                 impl<'a> Actions<'a> {
-                    pub fn create(&self, #(#required_args)* params: Vec<SetParam>) -> Create {
-                        let mut input_fields = params.into_iter().map(|p| p.to_field()).collect::<Vec<_>>();
-
+                    pub fn create(&self, #(#required_args)* mut params: Vec<SetParam>) -> Create {
                         #(#required_arg_pushes)*
 
-                        let query = Query {
-                            ctx: QueryContext::new(&self.client.executor, self.client.query_schema.clone()),
-                            name: String::new(),
-                            operation: "mutation".into(),
-                            method: "createOne".into(),
-                            model: #model_name_pascal_string.into(),
-                            outputs: _outputs(),
-                            inputs: vec![Input {
-                                name: "data".into(),
-                                fields: input_fields,
-                                ..Default::default()
-                            }]
-                        };
-
                         Create {
-                            query,
-                            with_params: vec![]
+                            ctx: QueryContext::new(&self.client.executor, self.client.query_schema.clone()),
+                            args: CreateArgs::new(params)
                         }
                     }
 
                     pub fn find_unique(&self, param: UniqueWhereParam) -> FindUnique {
-                        let param: WhereParam = param.into();
-                        let fields = transform_equals(vec![param.to_field()]);
-
-                        let query = Query {
-                            ctx: QueryContext::new(&self.client.executor, self.client.query_schema.clone()),
-                            name: String::new(),
-                            operation: "query".into(),
-                            method: "findUnique".into(),
-                            model: #model_name_pascal_string.into(),
-                            outputs: _outputs(),
-                            inputs: vec![Input {
-                                name: "where".into(),
-                                fields,
-                                ..Default::default()
-                            }]
-                        };
-
                         FindUnique {
-                            query,
-                            with_params: vec![]
+                            ctx: QueryContext::new(&self.client.executor, self.client.query_schema.clone()),
+                            args: FindUniqueArgs::new(param.into()),
                         }
                     }
 
                     pub fn find_first(&self, params: Vec<WhereParam>) -> FindFirst {
-                        let where_fields: Vec<Field> = params.into_iter().map(|param|
-                            param.to_field()
-                        ).collect();
-
-                        let inputs = if where_fields.len() > 0 {
-                            vec![Input {
-                                name: "where".into(),
-                                fields: vec![Field {
-                                    name: "AND".into(),
-                                    list: true,
-                                    wrap_list: true,
-                                    fields: Some(where_fields),
-                                    ..Default::default()
-                                }],
-                                ..Default::default()
-                            }]
-                        } else {
-                            Vec::new()
-                        };
-
-                        let query = Query {
-                            ctx: QueryContext::new(&self.client.executor, self.client.query_schema.clone()),
-                            name: String::new(),
-                            operation: "query".into(),
-                            method: "findFirst".into(),
-                            model: #model_name_pascal_string.into(),
-                            outputs: _outputs(),
-                            inputs
-                        };
-
                         FindFirst {
-                            query,
-                            order_by_params: vec![],
-                            with_params: vec![]
+                            ctx: QueryContext::new(&self.client.executor, self.client.query_schema.clone()),
+                            args: FindFirstArgs::new(params),
                         }
                     }
 
                     pub fn find_many(&self, params: Vec<WhereParam>) -> FindMany {
-                        let where_fields: Vec<Field> = params.into_iter().map(|param|
-                            param.to_field()
-                        ).collect();
-
-                        let inputs = if where_fields.len() > 0 {
-                            vec![Input {
-                                name: "where".into(),
-                                fields: where_fields,
-                                ..Default::default()
-                            }]
-                        } else {
-                            Vec::new()
-                        };
-
-                        let query = Query {
-                            ctx: QueryContext::new(&self.client.executor, self.client.query_schema.clone()),
-                            name: String::new(),
-                            operation: "query".into(),
-                            method: "findMany".into(),
-                            model: #model_name_pascal_string.into(),
-                            outputs: _outputs(),
-                            inputs
-                        };
-
                         FindMany {
-                            query,
-                            order_by_params: vec![],
-                            with_params: vec![]
+                            ctx: QueryContext::new(&self.client.executor, self.client.query_schema.clone()),
+                            args: FindManyArgs::new(params),
                         }
                     }
 
                     pub fn upsert(&self, param: UniqueWhereParam) -> Upsert {
-                        let param: WhereParam = param.into();
-                        let fields = transform_equals(vec![param.to_field()]);
-
-                        let query = Query {
+                        Upsert { 
                             ctx: QueryContext::new(&self.client.executor, self.client.query_schema.clone()),
-                            name: String::new(),
-                            operation: "mutation".into(),
-                            method: "upsertOne".into(),
-                            model: #model_name_pascal_string.into(),
-                            outputs: _outputs(),
-                            inputs: vec![Input {
-                                name: "where".into(),
-                                fields,
-                                ..Default::default()
-                            }]
-                        };
-
-                        Upsert { query }
+                            args: UpsertArgs::new(param.into()),
+                        }
                     }
                 }
             }
diff --git a/cli/src/generator/types.rs b/cli/src/generator/types.rs
index 09d360e3..57273862 100644
--- a/cli/src/generator/types.rs
+++ b/cli/src/generator/types.rs
@@ -1,6 +1,7 @@
 use convert_case::{Case, Casing};
 use quote::{__private::TokenStream, format_ident, quote};
 use serde::{Deserialize, Serialize};
+use syn::Ident;
 use std::collections::HashMap;
 
 use super::ast::{dmmf, AST};
@@ -13,7 +14,7 @@ pub struct Root<'a> {
     pub dmmf: dmmf::Document,
     pub datamodel: String,
     #[serde(skip)]
-    pub ast: Option<AST<'a>>
+    pub ast: Option<AST<'a>>,
 }
 
 fn default_package() -> String {
@@ -81,7 +82,6 @@ pub struct BinaryPaths {
 }
 
 #[derive(Serialize, Deserialize, Debug, Default, Clone)]
-#[serde(rename_all = "camelCase")]
 pub struct GraphQLType(pub String);
 
 impl GraphQLType {
@@ -99,16 +99,33 @@ impl GraphQLType {
         match string {
             "Int" => quote!(i32),
             "BigInt" => quote!(i64),
-            "Float" => quote!(f64),
-            "Decimal" => quote!(f64),
+            "Float" | "Decimal" => quote!(f64),
             "Boolean" => quote!(bool),
             "Bytes" => quote!(Vec<u8>),
-            "DateTime" => quote!(chrono::DateTime<chrono::Utc>),
+            "DateTime" => quote!(chrono::DateTime<chrono::FixedOffset>),
             "Json" => quote!(serde_json::Value),
+            "String" => quote!(String),
             _ => {
                 let ident = format_ident!("{}", string.to_case(Case::Pascal));
                 quote!(#ident)
             }
         }
     }
+
+    pub fn to_prisma_value(&self, var: &Ident) -> TokenStream {
+        match self.string() {
+            "Int" => quote!(PrismaValue::Int(#var as i64)),
+            "BigInt" => quote!(PrismaValue::BigInt(#var)),
+            "Float" | "Decimal" => {
+                quote!(PrismaValue::Float(bigdecimal::from_f64(#var).unwrap().normalized()))
+            }
+            "Boolean" => quote!(PrismaValue::Boolean(#var)),
+            "Bytes" => quote!(PrismaValue::Bytes(#var)),
+            "DateTime" => quote!(PrismaValue::DateTime(#var)),
+            "Json" => quote!(PrismaValue::Json(serde_json::to_string(&#var).unwrap())),
+            "String" => quote!(PrismaValue::String(#var)),
+            "QueryMode" => quote!(PrismaValue::String(#var.to_string())),
+            t => panic!("Unsupported type: {t}"),
+        }
+    }
 }
diff --git a/docs/05-fetch.md b/docs/05-fetch.md
index 96748d41..3fbc755f 100644
--- a/docs/05-fetch.md
+++ b/docs/05-fetch.md
@@ -1,6 +1,6 @@
 # Fetch
 
-The `fetch` method on relation fields allows you to include relation data in your queries, in addition to the original records. Without this you would need 2 separate queries, but `with` and `fetch` allow for it to be done in one.
+The `fetch` method on relation fields allows you to include relation data in your queries, in addition to the original records. Without this you would need 2 separate queries, but `with` and `fetch` allow for it to be done in one. Loading multiple relations can be done by calling `with` multiple times, each calling `fetch` for a different relation.
 
 Accessing a relation field on a model's data cannot be done like regular fields. Instead, an accessor function is available that wraps the relation data inside a `Result` which will be an `Err` if the relation has not been fetched using a `with` call.
 
@@ -65,7 +65,7 @@ use prisma::{comment, post};
 
 let post: Option<post::Data> = client
     .post()
-    .find_unique(post::id().equals("0".to_string()))
+    .find_unique(post::id::equals("0".to_string()))
     .with(post::comments::fetch())
     .exec()
     .await
@@ -76,6 +76,33 @@ let post: Option<post::Data> = client
 let comments: Result<Vec<comment::Data>, String> = post.comments();
 ```
 
+## Nested Relations
+
+Starting with Prisma Client Rust v0.4.1, relations can be fetched to an unlimited depth.
+
+In this example, a post is loaded with its comments, and each comment is loaded with the original post.
+
+```rust
+use prisam::{comment, post};
+
+let post: post::Data = client
+    .post()
+    .find_unique(post::id::equals("0".to_string()))
+    .with(post::comments::fetch().with(comment::post::fetch()))
+    .exec()
+    .await
+    .unwrap()
+    .unwrap();
+
+// Safe since post::comments::fetch() has been used
+for comment in post.comments().unwrap() {
+    // Safe since comment::post::fetch() has been used
+    let post = comment.post().unwrap();
+
+    assert_eq!(post.id, "0");
+}
+```
+
 ## Up Next
 
-Next, learn how to [paginate your queries](06-pagination.md).
\ No newline at end of file
+Next, learn how to [paginate your queries](06-pagination.md).
diff --git a/docs/06-pagination.md b/docs/06-pagination.md
index 31f59d89..45c7c905 100644
--- a/docs/06-pagination.md
+++ b/docs/06-pagination.md
@@ -1,6 +1,6 @@
 # Pagination
 
-Pagination allows you to alter which records are returned from `find_first` and `find_many` queries.
+Pagination allows you to alter which records are returned from `find_first` and `find_many` queries, and on many relations.
 
 All of these methods can used together, and the order they are used in does not matter.
 
@@ -47,7 +47,7 @@ let posts: Vec<post::Data> = client
     .take(5) // query will return the first 5 records, instead of every record
     .exec()
     .await
-    .unwrap()
+    .unwrap();
 
 ```
 
@@ -64,7 +64,7 @@ let posts: Vec<post::Data> = client
     .skip(2) // query will skip the first two records, returning the rest
     .exec()
     .await
-    .unwrap()
+    .unwrap();
 
 ```
 
@@ -81,11 +81,32 @@ let posts: Vec<post::Data> = client
     .cursor(post::id::cursor("abc".to_string()))
     .exec()
     .await
-    .unwrap()
+    .unwrap();
 ```
 
 The [`order_by` method](07-order-by.md) can be very useful when combined with cursor pagination.
 
+## Relation Pagination
+
+The above methods can be chained to `fetch` calls for many relations.
+
+```rust
+use prisma::post;
+
+let posts: Vec<post::Data> = client
+    .post()
+    .find_many(vec![])
+    .with(
+        post::comments::fetch(vec![])
+            .skip(10)
+            .take(5)
+            .cursor("abc".to_string()),
+    )
+    .exec()
+    .await
+    .unwrap();
+```
+
 ## Up Next
 
 Next, check out how to [order queries](07-order-by.md)
diff --git a/docs/07-order-by.md b/docs/07-order-by.md
index 0b69ff90..bd3884eb 100644
--- a/docs/07-order-by.md
+++ b/docs/07-order-by.md
@@ -3,6 +3,7 @@
 Ordering can be performed on any field, though it is recommended to only order by indexed fields for improved performance.
 
 Order is defined using a field module's `order` function, which takes a `Direction` enum provided by `prisma_client_rust`.
+It can be performed on `find_first` and `find_many` queries, as well as being chained onto `fetch` calls for many relations in a similar manner [relation pagination](06-pagination.md#relation-pagination).
 
 The examples use the following schema:
 
@@ -89,4 +90,4 @@ let posts: Vec<post::Data> = client
 
 ## Up Next
 
-Finding data is great, but let's look at how to [create records](08-create.md).
\ No newline at end of file
+Finding data is great, but let's look at how to [create records](08-create.md).
diff --git a/examples/simple/Cargo.toml b/examples/simple/Cargo.toml
index c486d045..503b80e0 100644
--- a/examples/simple/Cargo.toml
+++ b/examples/simple/Cargo.toml
@@ -8,4 +8,4 @@ default-run = "simple"
 prisma-client-rust = { path="../../" }
 prisma-client-rust-cli = {path ="../../cli"}
 serde = { version = "1.0", features = ["derive"] }
-tokio = {features=["macros"], version = "1.17.0"}
+tokio = {features=["macros","rt-multi-thread"], version = "1.17.0"}
diff --git a/examples/simple/src/main.rs b/examples/simple/src/main.rs
index aefb83cc..2c6c0723 100644
--- a/examples/simple/src/main.rs
+++ b/examples/simple/src/main.rs
@@ -1,10 +1,23 @@
 use crate::db::*;
 
+pub mod db2;
 pub mod db;
 
 #[tokio::main]
 pub async fn main() {
-    let client = db::new_client().await.unwrap();
+    let client = new_client().await.unwrap();
+
+    client
+        .user()
+        .find_many(vec![])
+        .with(
+            user::posts::fetch(vec![])
+                .with(post::user::fetch().with(user::posts::fetch(vec![])))
+                .skip(10)
+                .take(5),
+        )
+        .exec()
+        .await;
 
     let user = client
         .user()
@@ -32,27 +45,27 @@ pub async fn main() {
     println!("User: {:?}", user);
     println!("Post: {:?}", post);
 
-    let post_with_user = client
-        .post()
-        .find_unique(post::id::equals("post0".to_string()))
-        .with(post::user::fetch())
-        .exec()
-        .await
-        .unwrap()
-        .unwrap();
+    // let post_with_user = client
+    //     .post()
+    //     .find_unique(post::id::equals("post0".to_string()))
+    //     .with(post::user::fetch())
+    //     .exec()
+    //     .await
+    //     .unwrap()
+    //     .unwrap();
 
-    println!("Post user: {:?}", post_with_user.user().unwrap());
+    // println!("Post user: {:?}", post_with_user.user().unwrap());
 
-    let user_with_posts = client
-        .user()
-        .find_unique(user::id::equals("user0".to_string()))
-        .with(user::posts::fetch(vec![]))
-        .exec()
-        .await
-        .unwrap()
-        .unwrap();
+    // let user_with_posts = client
+    //     .user()
+    //     .find_unique(user::id::equals("user0".to_string()))
+    //     .with(user::posts::fetch(vec![]))
+    //     .exec()
+    //     .await
+    //     .unwrap()
+    //     .unwrap();
 
-    println!("User posts: {:?}", user_with_posts.posts().unwrap());
+    // println!("User posts: {:?}", user_with_posts.posts().unwrap());
 
     let deleted_posts = client
         .post()
@@ -63,12 +76,12 @@ pub async fn main() {
         .unwrap();
     println!("Deleted {} posts", deleted_posts);
 
-    let deleted_users_count = client
-        .user()
-        .find_many(vec![])
-        .delete()
-        .exec()
-        .await
-        .unwrap();
-    println!("Deleted {} users", deleted_users_count);
+    // let deleted_users_count = client
+    //     .user()
+    //     .find_many(vec![])
+    //     .delete()
+    //     .exec()
+    //     .await
+    //     .unwrap();
+    // println!("Deleted {} users", deleted_users_count);
 }
diff --git a/src/lib.rs b/src/lib.rs
index 5c0442d6..0d3c7a7c 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -9,7 +9,7 @@ pub use datamodel;
 use datamodel::datamodel_connector::Diagnostics;
 pub use prisma_models;
 pub use query_core;
-use query_core::CoreError;
+use query_core::{CoreError, Operation, QueryValue, Selection};
 pub use request_handlers;
 pub use serde_json;
 use thiserror::Error;
@@ -17,8 +17,15 @@ use thiserror::Error;
 pub type Executor = Box<dyn query_core::QueryExecutor + Send + Sync + 'static>;
 
 #[derive(Deserialize)]
-pub struct CountResult {
-    pub count: usize,
+pub struct BatchResult {
+    pub count: i64,
+}
+
+impl BatchResult {
+    pub fn selection() -> Selection {
+        let selection = Selection::builder("count");
+        selection.build()
+    }
 }
 
 #[derive(Serialize, Deserialize)]
@@ -29,6 +36,15 @@ pub enum Direction {
     Desc,
 }
 
+impl ToString for Direction {
+    fn to_string(&self) -> String {
+        match self {
+            Direction::Asc => "asc".to_string(),
+            Direction::Desc => "desc".to_string(),
+        }
+    }
+}
+
 #[derive(Debug, Error)]
 pub enum NewClientError {
     #[error("Error configuring database connection: {0}")]
@@ -67,3 +83,797 @@ macro_rules! or {
         $crate::operator::or(vec![$($x),+])
     };
 }
+
+pub enum SerializedWhereValue {
+    Object(Vec<(String, QueryValue)>),
+    List(Vec<QueryValue>),
+}
+
+pub type SerializedWhere = (String, SerializedWhereValue);
+pub fn transform_equals(
+    params: impl Iterator<Item = SerializedWhere>,
+) -> Vec<(String, QueryValue)> {
+    params
+        .map(|(field, value)| {
+            (
+                field,
+                match value {
+                    SerializedWhereValue::Object(mut params) => match params
+                        .iter()
+                        .position(|(key, _)| key == "equals")
+                        .map(|i| params.swap_remove(i))
+                    {
+                        Some((_, value)) => value,
+                        None => QueryValue::Object(params.into_iter().collect()),
+                    },
+                    SerializedWhereValue::List(values) => QueryValue::List(values),
+                },
+            )
+        })
+        .collect()
+}
+
+pub struct Args<With>
+where
+    With: Into<Selection>,
+{
+    pub with_params: Vec<With>,
+}
+
+impl<With> Args<With>
+where
+    With: Into<Selection>,
+{
+    pub fn new() -> Self {
+        Self {
+            with_params: vec![],
+        }
+    }
+
+    pub fn with(mut self, with: impl Into<With>) -> Self {
+        self.with_params.push(with.into());
+        self
+    }
+}
+
+pub struct FindUniqueArgs<Where, With>
+where
+    Where: Into<SerializedWhere>,
+    With: Into<Selection>,
+{
+    pub where_param: Where,
+    pub with_params: Vec<With>,
+}
+
+impl<Where, With> FindUniqueArgs<Where, With>
+where
+    Where: Into<SerializedWhere>,
+    With: Into<Selection>,
+{
+    pub fn new(where_param: Where) -> Self {
+        Self {
+            where_param,
+            with_params: vec![],
+        }
+    }
+
+    pub fn with(mut self, param: With) -> Self {
+        self.with_params.push(param);
+        self
+    }
+
+    pub fn to_operation(self, model: &str, mut scalar_selections: Vec<Selection>) -> Operation {
+        let Self {
+            where_param,
+            with_params,
+        } = self;
+
+        let mut selection = Selection::builder(format!("findUnique{}", model));
+
+        selection.alias("result");
+
+        selection.push_argument(
+            "where",
+            QueryValue::Object(
+                transform_equals(vec![where_param].into_iter().map(Into::into))
+                    .into_iter()
+                    .collect(),
+            ),
+        );
+
+        if with_params.len() > 0 {
+            scalar_selections.append(&mut with_params.into_iter().map(Into::into).collect());
+        }
+        selection.nested_selections(scalar_selections);
+
+        Operation::Read(selection.build())
+    }
+}
+
+pub struct FindManySelectionArgs {
+    pub arguments: Vec<(String, QueryValue)>,
+    pub nested_selections: Vec<Selection>,
+}
+
+impl FindManySelectionArgs {
+    pub fn new() -> Self {
+        Self {
+            arguments: vec![],
+            nested_selections: vec![],
+        }
+    }
+}
+pub struct FindManyArgs<Where, With, OrderBy, Cursor>
+where
+    Where: Into<SerializedWhere>,
+    With: Into<Selection>,
+    OrderBy: Into<(String, QueryValue)>,
+    Cursor: Into<(String, QueryValue)>,
+{
+    pub where_params: Vec<Where>,
+    pub with_params: Vec<With>,
+    pub order_by_params: Vec<OrderBy>,
+    pub cursor_params: Vec<Cursor>,
+    pub skip: Option<i64>,
+    pub take: Option<i64>,
+}
+
+impl<Where, With, OrderBy, Cursor> FindManyArgs<Where, With, OrderBy, Cursor>
+where
+    Where: Into<SerializedWhere>,
+    With: Into<Selection>,
+    OrderBy: Into<(String, QueryValue)>,
+    Cursor: Into<(String, QueryValue)>,
+{
+    pub fn new(where_params: Vec<Where>) -> Self {
+        Self {
+            where_params,
+            with_params: vec![],
+            order_by_params: vec![],
+            cursor_params: vec![],
+            skip: None,
+            take: None,
+        }
+    }
+
+    pub fn with(mut self, param: impl Into<With>) -> Self {
+        self.with_params.push(param.into());
+        self
+    }
+
+    pub fn order_by(mut self, param: impl Into<OrderBy>) -> Self {
+        self.order_by_params.push(param.into());
+        self
+    }
+
+    pub fn cursor(mut self, param: impl Into<Cursor>) -> Self {
+        self.cursor_params.push(param.into());
+        self
+    }
+
+    pub fn skip(mut self, skip: i64) -> Self {
+        self.skip = Some(skip);
+        self
+    }
+
+    pub fn take(mut self, take: i64) -> Self {
+        self.take = Some(take);
+        self
+    }
+
+    pub fn to_operation(self, model: &str, mut scalar_selections: Vec<Selection>) -> Operation {
+        let Self {
+            where_params,
+            with_params,
+            order_by_params,
+            cursor_params,
+            skip,
+            take,
+        } = self;
+
+        let mut selection = Selection::builder(format!("findMany{}", model));
+
+        selection.alias("result");
+
+        if where_params.len() > 0 {
+            selection.push_argument(
+                "where",
+                QueryValue::Object(
+                    transform_equals(where_params.into_iter().map(Into::into))
+                        .into_iter()
+                        .collect(),
+                ),
+            );
+        }
+
+        if with_params.len() > 0 {
+            scalar_selections.append(&mut with_params.into_iter().map(Into::into).collect());
+        }
+        selection.nested_selections(scalar_selections);
+
+        if order_by_params.len() > 0 {
+            selection.push_argument(
+                "orderBy".to_string(),
+                QueryValue::Object(
+                    order_by_params
+                        .into_iter()
+                        .map(Into::<(String, QueryValue)>::into)
+                        .collect(),
+                ),
+            );
+        }
+
+        if cursor_params.len() > 0 {
+            selection.push_argument(
+                "cursor".to_string(),
+                QueryValue::Object(
+                    cursor_params
+                        .into_iter()
+                        .map(Into::<(String, QueryValue)>::into)
+                        .collect(),
+                ),
+            );
+        }
+
+        skip.map(|skip| selection.push_argument("skip".to_string(), QueryValue::Int(skip as i64)));
+        take.map(|take| selection.push_argument("take".to_string(), QueryValue::Int(take as i64)));
+
+        Operation::Read(selection.build())
+    }
+}
+impl<Where, With, OrderBy, Cursor> From<FindManyArgs<Where, With, OrderBy, Cursor>>
+    for FindManySelectionArgs
+where
+    Where: Into<SerializedWhere>,
+    With: Into<Selection>,
+    OrderBy: Into<(String, QueryValue)>,
+    Cursor: Into<(String, QueryValue)>,
+{
+    fn from(args: FindManyArgs<Where, With, OrderBy, Cursor>) -> Self {
+        let FindManyArgs {
+            where_params,
+            with_params,
+            order_by_params,
+            cursor_params,
+            skip,
+            take,
+        } = args;
+
+        let mut selection_args = Self::new();
+
+        if with_params.len() > 0 {
+            selection_args.nested_selections = with_params.into_iter().map(Into::into).collect()
+        }
+
+        if where_params.len() > 0 {
+            selection_args.arguments.push((
+                "where".to_string(),
+                QueryValue::Object(
+                    transform_equals(where_params.into_iter().map(Into::into))
+                        .into_iter()
+                        .collect(),
+                ),
+            ));
+        }
+
+        if order_by_params.len() > 0 {
+            selection_args.arguments.push((
+                "orderBy".to_string(),
+                QueryValue::Object(
+                    order_by_params
+                        .into_iter()
+                        .map(Into::<(String, QueryValue)>::into)
+                        .collect(),
+                ),
+            ));
+        }
+
+        if cursor_params.len() > 0 {
+            selection_args.arguments.push((
+                "cursor".to_string(),
+                QueryValue::Object(
+                    cursor_params
+                        .into_iter()
+                        .map(Into::<(String, QueryValue)>::into)
+                        .collect(),
+                ),
+            ));
+        }
+
+        skip.map(|skip| {
+            selection_args
+                .arguments
+                .push(("skip".to_string(), QueryValue::Int(skip)))
+        });
+
+        take.map(|take| {
+            selection_args
+                .arguments
+                .push(("take".to_string(), QueryValue::Int(take)))
+        });
+
+        selection_args
+    }
+}
+pub struct FindFirstArgs<Where, With, OrderBy, Cursor>
+where
+    Where: Into<SerializedWhere>,
+    With: Into<Selection>,
+    OrderBy: Into<(String, QueryValue)>,
+    Cursor: Into<(String, QueryValue)>,
+{
+    pub where_params: Vec<Where>,
+    pub with_params: Vec<With>,
+    pub order_by_params: Vec<OrderBy>,
+    pub cursor_params: Vec<Cursor>,
+    pub skip: Option<i64>,
+    pub take: Option<i64>,
+}
+
+impl<Where, With, OrderBy, Cursor> FindFirstArgs<Where, With, OrderBy, Cursor>
+where
+    Where: Into<SerializedWhere>,
+    With: Into<Selection>,
+    OrderBy: Into<(String, QueryValue)>,
+    Cursor: Into<(String, QueryValue)>,
+{
+    pub fn new(where_params: Vec<Where>) -> Self {
+        Self {
+            where_params,
+            with_params: vec![],
+            order_by_params: vec![],
+            cursor_params: vec![],
+            skip: None,
+            take: None,
+        }
+    }
+
+    pub fn with(mut self, param: With) -> Self {
+        self.with_params.push(param);
+        self
+    }
+
+    pub fn order_by(mut self, param: OrderBy) -> Self {
+        self.order_by_params.push(param);
+        self
+    }
+
+    pub fn cursor(mut self, param: Cursor) -> Self {
+        self.cursor_params.push(param);
+        self
+    }
+
+    pub fn skip(mut self, skip: i64) -> Self {
+        self.skip = Some(skip);
+        self
+    }
+
+    pub fn take(mut self, take: i64) -> Self {
+        self.take = Some(take);
+        self
+    }
+
+    pub fn to_operation(self, model: &str, mut scalar_selections: Vec<Selection>) -> Operation {
+        let Self {
+            where_params,
+            with_params,
+            order_by_params,
+            cursor_params,
+            skip,
+            take,
+        } = self;
+
+        let mut selection = Selection::builder(format!("findFirst{}", model));
+
+        selection.alias("result");
+
+        if where_params.len() > 0 {
+            selection.push_argument(
+                "where",
+                QueryValue::Object(
+                    transform_equals(where_params.into_iter().map(Into::into))
+                        .into_iter()
+                        .collect(),
+                ),
+            );
+        }
+
+        if with_params.len() > 0 {
+            scalar_selections.append(&mut with_params.into_iter().map(Into::into).collect());
+        }
+        selection.nested_selections(scalar_selections);
+
+        if order_by_params.len() > 0 {
+            selection.push_argument(
+                "orderBy".to_string(),
+                QueryValue::Object(
+                    order_by_params
+                        .into_iter()
+                        .map(Into::<(String, QueryValue)>::into)
+                        .collect(),
+                ),
+            );
+        }
+
+        if cursor_params.len() > 0 {
+            selection.push_argument(
+                "cursor".to_string(),
+                QueryValue::Object(
+                    cursor_params
+                        .into_iter()
+                        .map(Into::<(String, QueryValue)>::into)
+                        .collect(),
+                ),
+            );
+        }
+
+        skip.map(|skip| selection.push_argument("skip".to_string(), QueryValue::Int(skip as i64)));
+        take.map(|take| selection.push_argument("take".to_string(), QueryValue::Int(take as i64)));
+
+        Operation::Read(selection.build())
+    }
+}
+
+pub struct CreateArgs<Set, With>
+where
+    Set: Into<(String, QueryValue)>,
+    With: Into<Selection>,
+{
+    pub set_params: Vec<Set>,
+    pub with_params: Vec<With>,
+}
+
+impl<Set, With> CreateArgs<Set, With>
+where
+    Set: Into<(String, QueryValue)>,
+    With: Into<Selection>,
+{
+    pub fn new(set_params: Vec<Set>) -> Self {
+        Self {
+            set_params,
+            with_params: vec![],
+        }
+    }
+
+    pub fn with(mut self, param: With) -> Self {
+        self.with_params.push(param);
+        self
+    }
+
+    pub fn to_operation(self, model: &str, mut scalar_selections: Vec<Selection>) -> Operation {
+        let Self {
+            set_params,
+            with_params,
+        } = self;
+
+        let mut selection = Selection::builder(format!("createOne{}", model));
+
+        selection.alias("result");
+
+        selection.push_argument(
+            "data",
+            QueryValue::Object(
+                set_params
+                    .into_iter()
+                    .map(Into::<(String, QueryValue)>::into)
+                    .collect(),
+            ),
+        );
+
+        if with_params.len() > 0 {
+            scalar_selections.append(&mut with_params.into_iter().map(Into::into).collect());
+        }
+        selection.nested_selections(scalar_selections);
+
+        Operation::Write(selection.build())
+    }
+}
+
+pub struct UpdateArgs<Where, Set, With>
+where
+    Where: Into<SerializedWhere>,
+    Set: Into<(String, QueryValue)>,
+    With: Into<Selection>,
+{
+    pub where_param: Where,
+    pub set_params: Vec<Set>,
+    pub with_params: Vec<With>,
+}
+impl<Where, Set, With> UpdateArgs<Where, Set, With>
+where
+    Where: Into<SerializedWhere>,
+    Set: Into<(String, QueryValue)>,
+    With: Into<Selection>,
+{
+    pub fn new(where_param: Where, set_params: Vec<Set>, with_params: Vec<With>) -> Self {
+        Self {
+            where_param,
+            set_params,
+            with_params,
+        }
+    }
+
+    pub fn with(mut self, param: With) -> Self {
+        self.with_params.push(param);
+        self
+    }
+
+    pub fn to_operation(self, model: &str, mut scalar_selections: Vec<Selection>) -> Operation {
+        let Self {
+            where_param,
+            set_params,
+            with_params,
+        } = self;
+
+        let mut selection = Selection::builder(format!("updateOne{}", model));
+
+        selection.alias("result");
+
+        selection.push_argument(
+            "where",
+            QueryValue::Object(
+                transform_equals(vec![where_param].into_iter().map(Into::into))
+                    .into_iter()
+                    .collect(),
+            ),
+        );
+
+        selection.push_argument(
+            "data",
+            QueryValue::Object(
+                set_params
+                    .into_iter()
+                    .map(Into::<(String, QueryValue)>::into)
+                    .collect(),
+            ),
+        );
+
+        if with_params.len() > 0 {
+            scalar_selections.append(&mut with_params.into_iter().map(Into::into).collect());
+        }
+        selection.nested_selections(scalar_selections);
+
+        Operation::Write(selection.build())
+    }
+}
+
+pub struct UpdateManyArgs<Where, Set>
+where
+    Where: Into<SerializedWhere>,
+    Set: Into<(String, QueryValue)>,
+{
+    pub where_params: Vec<Where>,
+    pub set_params: Vec<Set>,
+}
+impl<Where, Set> UpdateManyArgs<Where, Set>
+where
+    Where: Into<SerializedWhere>,
+    Set: Into<(String, QueryValue)>,
+{
+    pub fn new(where_params: Vec<Where>, set_params: Vec<Set>) -> Self {
+        Self {
+            where_params,
+            set_params,
+        }
+    }
+
+    pub fn to_operation(self, model: &str) -> Operation {
+        let Self {
+            where_params,
+            set_params,
+        } = self;
+
+        let mut selection = Selection::builder(format!("updateMany{}", model));
+
+        selection.alias("result");
+
+        selection.push_argument(
+            "data",
+            QueryValue::Object(
+                set_params
+                    .into_iter()
+                    .map(Into::<(String, QueryValue)>::into)
+                    .collect(),
+            ),
+        );
+
+        if where_params.len() > 0 {
+            selection.push_argument(
+                "where",
+                QueryValue::Object(
+                    transform_equals(where_params.into_iter().map(Into::into))
+                        .into_iter()
+                        .collect(),
+                ),
+            );
+        }
+
+        selection.push_nested_selection(BatchResult::selection());
+
+        Operation::Write(selection.build())
+    }
+}
+
+pub struct UpsertArgs<Where, Set, With>
+where
+    Where: Into<SerializedWhere>,
+    Set: Into<(String, QueryValue)>,
+    With: Into<Selection>,
+{
+    pub where_param: Where,
+    pub create_params: Vec<Set>,
+    pub update_params: Vec<Set>,
+    pub with_params: Vec<With>,
+}
+
+impl<Where, Set, With> UpsertArgs<Where, Set, With>
+where
+    Where: Into<SerializedWhere>,
+    Set: Into<(String, QueryValue)>,
+    With: Into<Selection>,
+{
+    pub fn new(where_param: Where) -> Self {
+        Self {
+            where_param,
+            create_params: vec![],
+            update_params: vec![],
+            with_params: vec![],
+        }
+    }
+
+    pub fn with(mut self, param: With) -> Self {
+        self.with_params.push(param);
+        self
+    }
+    
+    pub fn create(mut self, params: Vec<Set>) -> Self {
+        self.create_params = params;
+        self
+    }
+    
+    pub fn update(mut self, params: Vec<Set>) -> Self {
+        self.update_params = params;
+        self
+    }
+
+    pub fn to_operation(self, model: &str, mut scalar_selections: Vec<Selection>) -> Operation {
+        let Self {
+            where_param,
+            create_params,
+            update_params,
+            with_params,
+        } = self;
+
+        let mut selection = Selection::builder(format!("upsertOne{}", model));
+
+        selection.alias("result");
+        
+        if create_params.len() > 0 {
+            selection.push_argument(
+                "create",
+                QueryValue::Object(
+                    create_params
+                        .into_iter()
+                        .map(Into::<(String, QueryValue)>::into)
+                        .collect(),
+                ),
+            );
+        }
+        
+        if update_params.len() > 0 {
+            selection.push_argument(
+                "update",
+                QueryValue::Object(
+                    update_params
+                        .into_iter()
+                        .map(Into::<(String, QueryValue)>::into)
+                        .collect(),
+                ),
+            );
+        }
+
+        selection.push_argument(
+            "where",
+            QueryValue::Object(
+                transform_equals(vec![where_param].into_iter().map(Into::into))
+                    .into_iter()
+                    .collect(),
+            ),
+        );
+
+        if with_params.len() > 0 {
+            scalar_selections.append(&mut with_params.into_iter().map(Into::into).collect());
+        }
+        selection.nested_selections(scalar_selections);
+
+        Operation::Write(selection.build())
+    }
+}
+
+pub struct DeleteArgs<Where, With> {
+    pub where_param: Where,
+    pub with_params: Vec<With>,
+}
+impl<Where, With> DeleteArgs<Where, With>
+where
+    Where: Into<SerializedWhere>,
+    With: Into<Selection>,
+{
+    pub fn new(where_param: Where, with_params: Vec<With>) -> Self {
+        Self {
+            where_param,
+            with_params,
+        }
+    }
+
+    pub fn with(mut self, param: With) -> Self {
+        self.with_params.push(param);
+        self
+    }
+
+    pub fn to_operation(self, model: &str, mut scalar_selections: Vec<Selection>) -> Operation {
+        let Self {
+            where_param,
+            with_params,
+        } = self;
+
+        let mut selection = Selection::builder(format!("deleteOne{}", model));
+
+        selection.alias("result");
+
+        selection.push_argument(
+            "where",
+            QueryValue::Object(
+                transform_equals(vec![where_param].into_iter().map(Into::into))
+                    .into_iter()
+                    .collect(),
+            ),
+        );
+
+        if with_params.len() > 0 {
+            scalar_selections.append(&mut with_params.into_iter().map(Into::into).collect());
+        }
+        selection.nested_selections(scalar_selections);
+
+        Operation::Write(selection.build())
+    }
+}
+
+pub struct DeleteManyArgs<Where>
+where
+    Where: Into<SerializedWhere>,
+{
+    pub where_params: Vec<Where>,
+}
+
+impl<Where> DeleteManyArgs<Where>
+where
+    Where: Into<SerializedWhere>,
+{
+    pub fn new(where_params: Vec<Where>) -> Self {
+        Self { where_params }
+    }
+
+    pub fn to_operation(self, model: &str) -> Operation {
+        let Self { where_params } = self;
+
+        let mut selection = Selection::builder(format!("deleteMany{}", model));
+
+        selection.alias("result");
+
+        if where_params.len() > 0 {
+            selection.push_argument(
+                "where",
+                QueryValue::Object(
+                    transform_equals(where_params.into_iter().map(Into::into))
+                        .into_iter()
+                        .collect(),
+                ),
+            );
+        }
+
+        selection.push_nested_selection(BatchResult::selection());
+
+        Operation::Write(selection.build())
+    }
+}
diff --git a/src/operator.rs b/src/operator.rs
index 8e7135bb..402ec174 100644
--- a/src/operator.rs
+++ b/src/operator.rs
@@ -11,4 +11,4 @@ pub fn and<T: From<Operator<T>>>(params: Vec<T>) -> T {
 }
 pub fn or<T: From<Operator<T>>>(params: Vec<T>) -> T {
     Operator::Or(params).into()
-}
\ No newline at end of file
+}
diff --git a/src/query.rs b/src/query.rs
index b5486850..66152587 100644
--- a/src/query.rs
+++ b/src/query.rs
@@ -1,46 +1,10 @@
-use graphql_parser::parse_query;
-use query_core::QuerySchemaRef;
-use request_handlers::GraphQLProtocolAdapter;
+use query_core::{Operation, QuerySchemaRef};
 use serde::de::DeserializeOwned;
 use serde_json::Value;
 use thiserror::Error;
 
 use crate::Executor;
 
-#[derive(Debug, Default)]
-
-pub struct Input {
-    pub name: String,
-    pub fields: Vec<Field>,
-    pub value: Option<Value>,
-}
-
-#[derive(Debug, Default)]
-pub struct Output {
-    pub name: String,
-    pub inputs: Vec<Input>,
-    pub outputs: Vec<Output>,
-}
-
-impl Output {
-    pub fn new(name: &str) -> Self {
-        Self {
-            name: name.to_string(),
-            inputs: Vec::new(),
-            outputs: Vec::new(),
-        }
-    }
-}
-
-#[derive(Debug, Default)]
-pub struct Field {
-    pub name: String,
-    pub list: bool,
-    pub wrap_list: bool,
-    pub fields: Option<Vec<Field>>,
-    pub value: Option<serde_json::Value>,
-}
-
 pub struct QueryContext<'a> {
     executor: &'a Executor,
     schema: QuerySchemaRef,
@@ -50,16 +14,23 @@ impl<'a> QueryContext<'a> {
     pub fn new(executor: &'a Executor, schema: QuerySchemaRef) -> Self {
         Self { executor, schema }
     }
-}
 
-pub struct Query<'a> {
-    pub ctx: QueryContext<'a>,
-    pub operation: String,
-    pub name: String,
-    pub method: String,
-    pub model: String,
-    pub inputs: Vec<Input>,
-    pub outputs: Vec<Output>,
+    pub async fn execute<T: DeserializeOwned>(self, operation: Operation) -> Result<T> {
+        async fn inner<'a>(ctx: QueryContext<'a>, op: Operation) -> Result<Value> {
+            let data = ctx.executor.execute(None, op, ctx.schema, None).await?;
+
+            let ret = serde_json::to_value(data.data)?; // TODO: serialize without json conversion
+
+            Ok(ret)
+        }
+        
+        dbg!(&operation);
+
+        let value = inner(self, operation).await?;
+        let ret = serde_json::from_value(value)?;
+
+        Ok(ret)
+    }
 }
 
 #[derive(Debug, Error)]
@@ -78,167 +49,3 @@ pub enum Error {
 }
 
 pub type Result<T> = std::result::Result<T, Error>;
-
-impl<'a> Query<'a> {
-    pub async fn perform<T: DeserializeOwned>(self) -> Result<T> {
-        let query_string = self.build();
-
-        let document = parse_query(&query_string)?;
-        let operation = GraphQLProtocolAdapter::convert(document, None)?;
-
-        let data = self
-            .ctx
-            .executor
-            .execute(None, operation, self.ctx.schema, None)
-            .await?;
-
-        let value = serde_json::to_value(data.data)?; // TODO: serialize without json conversion
-        let ret = serde_json::from_value(value)?;
-
-        Ok(ret)
-    }
-
-    pub fn build(&self) -> String {
-        let mut string = String::new();
-
-        string.push_str(&format!("{} {}", self.operation, self.name));
-        string.push_str("{");
-        string.push_str("result: ");
-
-        string.push_str(&self.build_inner());
-
-        string.push_str("}");
-
-        return string;
-    }
-
-    fn build_inner(&self) -> String {
-        let mut string = String::new();
-
-        string.push_str(&format!("{}{}", self.method, self.model));
-
-        if self.inputs.len() > 0 {
-            string.push_str(&self.build_inputs(&self.inputs));
-        }
-
-        string.push_str(" ");
-
-        if self.outputs.len() > 0 {
-            string.push_str(&self.build_outputs(&self.outputs));
-        }
-
-        string
-    }
-
-    fn build_inputs(&self, inputs: &Vec<Input>) -> String {
-        let mut string = String::new();
-
-        string.push_str("(");
-
-        for input in inputs {
-            string.push_str(&input.name);
-
-            string.push_str(":");
-
-            let next = match &input.value {
-                Some(value) => serde_json::to_string(value)
-                    .expect(&format!("Failed to build input {}", input.name)),
-                None => self.build_fields(false, false, &input.fields),
-            };
-
-            string.push_str(&next);
-
-            string.push_str(",");
-        }
-
-        string.push_str(")");
-
-        string
-    }
-
-    fn build_outputs(&self, outputs: &Vec<Output>) -> String {
-        let mut string = String::new();
-
-        string.push_str("{");
-
-        for output in outputs {
-            string.push_str(&output.name);
-            string.push_str(" ");
-
-            if output.inputs.len() > 0 {
-                string.push_str(&self.build_inputs(&output.inputs));
-            }
-
-            if output.outputs.len() > 0 {
-                string.push_str(&self.build_outputs(&output.outputs));
-            }
-        }
-
-        string.push_str("}");
-
-        string
-    }
-
-    fn build_fields(&self, list: bool, wrap_list: bool, fields: &Vec<Field>) -> String {
-        let mut string = String::new();
-
-        if !list {
-            string.push_str("{");
-        }
-
-        for field in fields {
-            if wrap_list {
-                string.push_str("{");
-            }
-
-            if field.name != "" {
-                string.push_str(&field.name);
-                string.push_str(":")
-            }
-
-            if field.list {
-                string.push_str("[");
-            }
-
-            if let Some(fields) = &field.fields {
-                string.push_str(&self.build_fields(field.list, field.wrap_list, &fields));
-            }
-
-            if let Some(value) = &field.value {
-                string.push_str(
-                    &serde_json::to_string(&value)
-                        .expect(&format!("Failed to build field {}", field.name)),
-                );
-            }
-
-            if field.list {
-                string.push_str("]");
-            }
-
-            if wrap_list {
-                string.push_str("}");
-            }
-
-            string.push_str(",");
-        }
-
-        if !list {
-            string.push_str("}");
-        }
-
-        string
-    }
-}
-
-pub fn transform_equals(mut fields: Vec<Field>) -> Vec<Field> {
-    for mut field in &mut fields {
-        if let Some(fields) = &field.fields {
-            if let Some(inner) = fields.iter().find(|f| f.name == "equals") {
-                field.value = inner.value.clone();
-                field.fields = None;
-            }
-        }
-    }
-
-    fields
-}


Test patch:
diff --git a/integration-tests/schema.prisma b/integration-tests/schema.prisma
index 7bf9f934..eedf7326 100644
--- a/integration-tests/schema.prisma
+++ b/integration-tests/schema.prisma
@@ -19,7 +19,7 @@ model Post {
     author     User?      @relation(fields: [author_id], references: [id])
     author_id  String?
     categories Category[] @relation(references: [id])
-    
+
     @@unique([title, author_id])
 }
 
@@ -56,7 +56,7 @@ model Types {
     integer  Int      @default(0)
     datetime DateTime @default(now())
     // TODO: Figure out how to deserialize these
-    // bigint   BigInt   @default(0) 
+    // bigint   BigInt   @default(0)
     // float_   Float    @default(0)
     // decimal  Decimal  @default(1)
 }
diff --git a/integration-tests/tests/with.rs b/integration-tests/tests/with.rs
index cb446248..fcee7757 100644
--- a/integration-tests/tests/with.rs
+++ b/integration-tests/tests/with.rs
@@ -1,9 +1,6 @@
 use prisma_client_rust::{or, query::Error};
 
-use crate::{
-    db::*,
-    utils::*,
-};
+use crate::{db::*, utils::*};
 
 async fn setup(client: &PrismaClient) -> Result<String, Error> {
     let user = client
@@ -92,6 +89,24 @@ async fn find_unique_with() -> TestResult {
     cleanup(client).await
 }
 
+#[tokio::test]
+async fn find_unique_with_take() -> TestResult {
+    let client = client().await;
+
+    let user_id = setup(&client).await?;
+
+    let user = client
+        .user()
+        .find_unique(user::id::equals(user_id.clone()))
+        .with(user::posts::fetch(vec![]).take(1))
+        .exec()
+        .await?
+        .unwrap();
+    assert_eq!(user.posts().unwrap().len(), 1);
+
+    cleanup(client).await
+}
+
 #[tokio::test]
 async fn find_unique_with_where() -> TestResult {
     let client = client().await;
@@ -103,7 +118,9 @@ async fn find_unique_with_where() -> TestResult {
     let user = client
         .user()
         .find_unique(user::id::equals(user_id.clone()))
-        .with(user::posts::fetch(vec![post::created_at::equals(posts[0].created_at)]))
+        .with(user::posts::fetch(vec![post::created_at::equals(
+            posts[0].created_at,
+        )]))
         .exec()
         .await?
         .unwrap();
@@ -115,6 +132,47 @@ async fn find_unique_with_where() -> TestResult {
     cleanup(client).await
 }
 
+#[tokio::test]
+async fn find_unique_with_pagination() -> TestResult {
+    let client = client().await;
+
+    let user_id = setup(&client).await?;
+
+    let posts = client.post().find_many(vec![]).exec().await?;
+
+    let user = client
+        .user()
+        .find_unique(user::id::equals(user_id.clone()))
+        .with(
+            user::posts::fetch(vec![])
+                .cursor(post::id::cursor(posts[0].id.clone()))
+                .take(1)
+                .skip(1),
+        )
+        .exec()
+        .await?
+        .unwrap();
+    assert_eq!(user.posts().unwrap().len(), 1);
+    assert_eq!(user.posts().unwrap()[0].id, posts[1].id);
+
+    let user = client
+        .user()
+        .find_unique(user::id::equals(user_id.clone()))
+        .with(
+            user::posts::fetch(vec![])
+                .cursor(post::id::cursor(posts[1].id.clone()))
+                .take(-1)
+                .skip(1),
+        )
+        .exec()
+        .await?
+        .unwrap();
+    assert_eq!(user.posts().unwrap().len(), 1);
+    assert_eq!(user.posts().unwrap()[0].id, posts[0].id);
+
+    cleanup(client).await
+}
+
 #[tokio::test]
 async fn find_unique_with_nested_where_or() -> TestResult {
     let client = client().await;
@@ -149,87 +207,36 @@ async fn find_unique_with_nested_where_or() -> TestResult {
     cleanup(client).await
 }
 
-// TODO: Nested pagination
-// #[tokio::test]
-// async fn find_unique_with_take() -> TestResult {
-//     let client = client().await;
-
-//     let user_id = setup(&client).await?;
-
-//     let user = client
-//         .user()
-//         .find_unique(user::id().equals(user_id.clone()))
-//         .with(user::posts().fetch(vec![]).take(1))
-//         .exec()
-//         .await?
-//         .unwrap();
-//     assert_eq!(user.posts().unwrap().len(), 1);
-
-//     cleanup(client).await
-// }
-
-// #[tokio::test]
-// async fn find_unique_with_pagination() -> TestResult {
-//     let client = client().await;
-
-//     let user_id = setup(&client).await?
-
-//     let posts = client.post().find_many(vec![]).exec().await?;;
-
-//     let user = client
-//         .user()
-//         .find_unique(user::id().equals(user_id.clone()))
-//         .with(user::posts().fetch(vec![]).cursor(posts[0].id).take(1).skip(1))
-//         .exec()
-//         .await?
-//         .unwrap();
-//     assert_eq!(user.posts().unwrap().len(), 1);
-//     assert_eq!(user.posts().unwrap()[0].id, posts[1].id);
-
-//     let user = client
-//         .user()
-//         .find_unique(user::id().equals(user_id.clone()))
-//         .with(user::posts().fetch(vec![]).cursor(posts[0].id).take(-1).skip(1))
-//         .exec()
-//         .await?
-//         .unwrap();
-//     assert_eq!(user.posts().unwrap().len(), 1);
-//     assert_eq!(user.posts().unwrap()[0].id, posts[0].id);
-
-//     cleanup(client).await
-// }
+#[tokio::test]
+async fn find_unique_with_nested_with() -> TestResult {
+    let client = client().await;
 
-// #[tokio::test]
-// async fn find_unique_with_where() -> TestResult {
-//     let client = client().await;
+    let user_id = setup(&client).await?;
 
-//     let user_id = setup(&client).await?;
+    let posts = client.post().find_many(vec![]).exec().await?;
 
-//     let posts = client.post().find_many(vec![]).exec().await?;
+    let user = client
+        .user()
+        .find_unique(user::id::equals(user_id.clone()))
+        .with(
+            user::posts::fetch(vec![])
+                .with(post::categories::fetch(vec![]).with(category::posts::fetch(vec![]))),
+        )
+        .exec()
+        .await?
+        .unwrap();
+    assert!(user.profile().is_err());
 
-//     let user = client
-//         .user()
-//         .find_unique(user::id().equals(user_id.clone()))
-//         .with(
-//             user::posts().fetch(vec![]).with(
-//                 post::categories()
-//                     .fetch(vec![])
-//                     .with(category::posts().fetch(vec![])),
-//             ),
-//         )
-//         .exec()
-//         .await?
-//         .unwrap();
-//     assert!(user.profile().is_err());
+    for post in user.posts().unwrap() {
+        for category in post.categories().unwrap() {
+            assert!(category.posts().is_ok())
+        }
+    }
 
-//     for post in user.posts().unwrap() {
-//         for category in post.categories().unwrap() {
-//             assert!(category.posts().is_ok())
-//         }
-//     }
+    cleanup(client).await
+}
 
-//     cleanup(client).await
-// }
+// TODO: Nested create
 
 // #[tokio::test]
 // async fn create_with() -> TestResult {