diff --git a/README.md b/README.md index a8fa54af2..f46d6f433 100755 --- a/README.md +++ b/README.md @@ -2,11 +2,12 @@ # WPGraphQL -WebsiteDocs • ApiGen Code Docs +WebsiteDocs • ApiGen Code Docs GraphQL API for WordPress. -[![Build Status](https://travis-ci.org/wp-graphql/wp-graphql.svg?branch=master)](https://travis-ci.org/wp-graphql/wp-graphql) [![Coverage Status](https://coveralls.io/repos/github/wp-graphql/wp-graphql/badge.svg?branch=master)](https://coveralls.io/github/wp-graphql/wp-graphql?branch=master) +[![Build Status](https://travis-ci.org/wp-graphql/wp-graphql.svg?branch=master)](https://travis-ci.org/wp-graphql/wp-graphql) +[![Coverage Status](https://coveralls.io/repos/github/wp-graphql/wp-graphql/badge.svg?branch=master)](https://coveralls.io/github/wp-graphql/wp-graphql?branch=master) [![WPGraphQL on Slack](https://slackin-wpgraphql.herokuapp.com/badge.svg)](https://slackin-wpgraphql.herokuapp.com/) ------ diff --git a/src/Data/Config.php b/src/Data/Config.php index 9055ed916..cf50a9df6 100755 --- a/src/Data/Config.php +++ b/src/Data/Config.php @@ -1,4 +1,5 @@ get( 'graphql_cursor_offset' ) ) ) { + if ( defined( 'GRAPHQL_REQUEST' ) && GRAPHQL_REQUEST ) { - $cursor_offset = $query->get( 'graphql_cursor_offset' ); + $cursor_offset = ! empty( $query->query_vars['graphql_cursor_offset'] ) ? $query->query_vars['graphql_cursor_offset'] : 0; /** * Ensure the cursor_offset is a positive integer */ if ( is_integer( $cursor_offset ) && 0 < $cursor_offset ) { - $compare = $query->get( 'graphql_cursor_compare' ); - $compare = in_array( $compare, [ '>', '<' ], true ) ? $compare : '>'; + $compare = ! empty( $query->get( 'graphql_cursor_compare' ) ) ? $query->get( 'graphql_cursor_compare' ) : '>'; + $compare = in_array( $compare, [ '>', '<' ], true ) ? $compare : '>'; $compare_opposite = ( '<' === $compare ) ? '>' : '<'; // Get the $cursor_post @@ -78,13 +79,25 @@ public function graphql_wp_query_cursor_pagination_support( $where, \WP_Query $q * */ if ( ! empty( $cursor_post ) && ! empty( $cursor_post->post_date ) ) { - $where .= $wpdb->prepare( " AND {$wpdb->posts}.post_date {$compare}= %s AND NOT ( {$wpdb->posts}.post_date {$compare_opposite}= %s AND {$wpdb->posts}.ID {$compare_opposite}= %d )", esc_sql( $cursor_post->post_date ), esc_sql( $cursor_post->post_date ), absint( $cursor_offset ) ); + $orderby = $query->get( 'orderby' ); + if ( ! empty( $orderby ) && is_array( $orderby ) ) { + foreach ( $orderby as $by => $order ) { + $order_compare = ( 'ASC' === $order ) ? '>' : '<'; + $value = $cursor_post->{$by}; + if ( ! empty( $by ) && ! empty( $value ) ) { + $where .= $wpdb->prepare( " AND {$wpdb->posts}.{$by} {$order_compare} '{$value}'", $value ); + } + } + } else { + $where .= $wpdb->prepare( " AND {$wpdb->posts}.post_date {$compare}= %s AND NOT ( {$wpdb->posts}.post_date {$compare}= %s AND {$wpdb->posts}.ID {$compare_opposite}= %d )", esc_sql( $cursor_post->post_date ), esc_sql( $cursor_post->post_date ), absint( $cursor_offset ) ); + } } else { $where .= $wpdb->prepare( " AND {$wpdb->posts}.ID {$compare} %d", $cursor_offset ); } } } + return $where; } @@ -111,15 +124,18 @@ public function graphql_wp_term_query_cursor_pagination_support( array $pieces, */ if ( is_integer( $cursor_offset ) && 0 < $cursor_offset ) { - $compare = $args['graphql_cursor_compare']; + $compare = ! empty( $args['graphql_cursor_compare'] ) ? $args['graphql_cursor_compare'] : '>'; $compare = in_array( $compare, [ '>', '<' ], true ) ? $compare : '>'; - $compare_opposite = ( '<' === $compare ) ? '>' : '<'; + + $order_by = ! empty( $args['orderby'] ) ? $args['orderby'] : 'comment_date'; + $order = ! empty( $args['order'] ) ? $args['order'] : 'DESC'; + $order_compare = ( 'ASC' === $order ) ? '>' : '<'; // Get the $cursor_post $cursor_term = get_term( $cursor_offset ); if ( ! empty( $cursor_term ) && ! empty( $cursor_term->name ) ) { - $pieces['where'] .= sprintf( ' AND t.name %1$s= "%3$s" AND NOT ( t.name %2$s= "%3$s" AND t.term_id %2$s= %4$d )', $compare, $compare_opposite, $cursor_term->name, $cursor_offset ); + $pieces['where'] .= sprintf( " AND t.{$order_by} {$order_compare} '{$cursor_term->{$order_by}}'" ); } else { $pieces['where'] .= sprintf( ' AND t.term_id %1$s %2$d', $compare, $cursor_offset ); } @@ -134,37 +150,41 @@ public function graphql_wp_term_query_cursor_pagination_support( array $pieces, * This returns a modified version of the $pieces of the comment query clauses if the request is a GRAPHQL_REQUEST * and the query has a graphql_cursor_offset defined * - * @param array $pieces A compacted array of comment query clauses. - * @param \WP_Comment_Query $comment_query Current instance of WP_Comment_Query, passed by reference. + * @param array $pieces A compacted array of comment query clauses. + * @param \WP_Comment_Query $query Current instance of WP_Comment_Query, passed by reference. * * @return array $pieces */ - public function graphql_wp_comments_query_cursor_pagination_support( array $pieces, \WP_Comment_Query $comment_query ) { + public function graphql_wp_comments_query_cursor_pagination_support( array $pieces, \WP_Comment_Query $query ) { - if ( defined( 'GRAPHQL_REQUEST' ) && GRAPHQL_REQUEST ) { + if ( + defined( 'GRAPHQL_REQUEST' ) && GRAPHQL_REQUEST && + ( is_array( $query->query_vars ) && array_key_exists( 'graphql_cursor_offset', $query->query_vars ) ) + ) { - $cursor_offset = ! empty( $comment_query->query_vars['graphql_cursor_offset'] ) ? $comment_query->query_vars['graphql_cursor_offset'] : null; + $cursor_offset = $query->query_vars['graphql_cursor_offset']; /** * Ensure the cursor_offset is a positive integer */ if ( is_integer( $cursor_offset ) && 0 < $cursor_offset ) { - $compare = $comment_query->query_vars['graphql_cursor_compare']; + $compare = ! empty( $query->get( 'graphql_cursor_compare' ) ) ? $query->get( 'graphql_cursor_compare' ) : '>'; $compare = in_array( $compare, [ '>', '<' ], true ) ? $compare : '>'; - $compare_opposite = ( '<' === $compare ) ? '>' : '<'; + + $order_by = ! empty( $query->query_vars['order_by'] ) ? $query->query_vars['order_by'] : 'comment_date'; + $order = ! empty( $query->query_vars['order'] ) ? $query->query_vars['order'] : 'DESC'; + $order_compare = ( 'ASC' === $order ) ? '>' : '<'; // Get the $cursor_post $cursor_comment = get_comment( $cursor_offset ); - - if ( ! empty( $cursor_comment ) && ! empty( $cursor_comment->comment_date ) ) { - $pieces['where'] .= sprintf( ' AND comment_date %1$s= "%3$s" AND NOT ( comment_date %2$s= "%3$s" AND comment_ID %2$s= %4$d )', $compare, $compare_opposite, esc_html( $cursor_comment->comment_date ), absint( $cursor_offset ) ); + if ( ! empty( $cursor_comment ) ) { + $pieces['where'] .= sprintf( " AND {$order_by} {$order_compare} '{$cursor_comment->{$order_by}}'" ); } else { $pieces['where'] .= sprintf( ' AND comment_ID %1$s %2$d', $compare, $cursor_offset ); } } } - return $pieces; } diff --git a/src/Data/ConnectionResolver.php b/src/Data/ConnectionResolver.php index 9ce8b8152..b0574103c 100755 --- a/src/Data/ConnectionResolver.php +++ b/src/Data/ConnectionResolver.php @@ -33,7 +33,6 @@ public static function resolve( $source, $args, AppContext $context, ResolveInfo $query = static::get_query( $query_args ); $array_slice = self::get_array_slice( $query, $args ); $connection = static::get_connection( $query, $array_slice, $source, $args, $context, $info ); - /** * Filter the connection, and provide heaps of info to make it easy to filter very specific cases * @@ -78,7 +77,7 @@ public static function resolve( $source, $args, AppContext $context, ResolveInfo public static function get_connection( $query, array $array, $source, array $args, AppContext $context, ResolveInfo $info ) { $meta = self::get_array_meta( $query, $args ); - $connection = ArrayConnection::connectionFromArray( $array, $args, $meta ); + $connection = ArrayConnection::connectionFromArraySlice( $array, $args, $meta ); return $connection; @@ -93,12 +92,12 @@ public static function get_connection( $query, array $array, $source, array $arg * @return array */ public static function get_array_slice( $query, array $args ) { - $array_slice = []; + $info = self::get_query_info( $query ); $items = $info['items']; + $array_slice = []; if ( ! empty( $items ) && is_array( $items ) ) { foreach ( $items as $item ) { - if ( true === is_object( $item ) ) { switch ( true ) { case $item instanceof \WP_Comment: @@ -115,12 +114,11 @@ public static function get_array_slice( $query, array $args ) { $array_slice[] = $item; break; default: - $array_slice[] = $item; + $array_slice = $items; } } } } - return $array_slice; } @@ -171,7 +169,7 @@ public static function get_amount_requested( array $args ) { } } - return max( 0, absint( $amount_requested ) ); + return max( 0, $amount_requested ); } @@ -281,13 +279,14 @@ public static function get_query_info( $query ) { if ( true === is_object( $query ) ) { switch ( true ) { case $query instanceof \WP_Query: - $query_info['total_items'] = ! empty( $query->found_posts ) ? $query->found_posts : count( $query->posts ); - $query_info['items'] = $query->posts; - $query_info['request'] = $query->request; + $found_posts = $query->posts; + $query_info['total_items'] = ! empty( $found_posts ) ? count( $found_posts ) : null; + $query_info['items'] = $found_posts; break; case $query instanceof \WP_Comment_Query: - $query_info['total_items'] = $query->found_comments; - $query_info['items'] = $query->get_comments(); + $found_comments = $query->get_comments(); + $query_info['total_items'] = ! empty( $found_comments ) ? count( $found_comments ) : null; + $query_info['items'] = ! empty( $found_comments ) ? $found_comments : []; break; case $query instanceof \WP_Term_Query: $query_info['total_items'] = ! empty( $query->query_vars['taxonomy'] ) ? wp_count_terms( $query->query_vars['taxonomy'][0], $query->query_vars ) : 0; diff --git a/src/Router.php b/src/Router.php index db3b3dafe..1f4a79188 100755 --- a/src/Router.php +++ b/src/Router.php @@ -149,8 +149,9 @@ public static function resolve_http_request() { * @param string $key Header key. * @param string $value Header value. */ - public function send_header( $key, $value ) { - /* + public static function send_header( $key, $value ) { + + /** * Sanitize as per RFC2616 (Section 4.2): * * Any LWS that occurs between field-content MAY be replaced with a @@ -169,7 +170,7 @@ public function send_header( $key, $value ) { * * @param int $code HTTP status. */ - protected function set_status( $code ) { + protected static function set_status( $code ) { status_header( $code ); } @@ -262,7 +263,6 @@ public static function process_http_request() { try { - if ( isset( $_SERVER['REQUEST_METHOD'] ) && $_SERVER['REQUEST_METHOD'] === 'GET' ) { $data = [ @@ -271,6 +271,12 @@ public static function process_http_request() { 'variables' => isset( $_GET['variables'] ) ? $_GET['variables'] : '', ]; + /** + * Allow the data to be filtered + * + * @param array $data An array containing the pieces of the data of the GraphQL request + */ + $data = apply_filters( 'graphql_request_data', $data ); /** * If the variables are already formatted as an array use them. @@ -286,16 +292,12 @@ public static function process_http_request() { } $decoded_variables = $sanitized_variables; - /** - * If the variables are not an array, let's attempt to decode them and convert them to an array for - * use in the executor. - */ + /** + * If the variables are not an array, let's attempt to decode them and convert them to an array for + * use in the executor. + */ } else { $decoded_variables = json_decode( $data['variables'] ); - if ( empty( $decoded_variables ) ) { - $variables = preg_replace( '#(?
\{|\[|,)\s*(?(?:\w|_)+)\s*:#im', '$1"$2":', sanitize_text_field( $_GET['variables'] ) );
-						$decoded_variables = (array) json_decode( $variables );
-					}
 				}
 
 				$data['variables'] = ! empty( $decoded_variables ) && is_array( $decoded_variables ) ? $decoded_variables : null;
@@ -318,7 +320,6 @@ public static function process_http_request() {
 				$data = json_decode( self::get_raw_data(), true );
 			}
 
-
 			/**
 			 * If the $data is empty, catch an error.
 			 */
@@ -347,7 +348,6 @@ public static function process_http_request() {
 			 */
 			$graphql_results = do_graphql_request( $request, $operation_name, $variables );
 
-
 			/**
 			 * Ensure the $graphql_request is returned as a proper, populated array,
 			 * otherwise add an error to the result
diff --git a/src/Type/Comment/Connection/CommentConnectionArgs.php b/src/Type/Comment/Connection/CommentConnectionArgs.php
index 9bff6952d..31de07207 100755
--- a/src/Type/Comment/Connection/CommentConnectionArgs.php
+++ b/src/Type/Comment/Connection/CommentConnectionArgs.php
@@ -91,6 +91,22 @@ private static function fields() {
 					'type' => self::comments_orderby_enum(),
 					'description' => __( 'Field to order the comments by.', 'wp-graphql' ),
 				],
+				'order' => [
+					'type' => new WPEnumType([
+						'name' => 'commentsOrder',
+						'values' => [
+							[
+								'name' => 'ASC',
+								'value' => 'ASC',
+							],
+							[
+								'name' => 'DESC',
+								'value' => 'DESC',
+							],
+						],
+						'defaultValue' => 'DESC',
+					]),
+				],
 				'parent' => [
 					'type' => Types::int(),
 					'description' => __( 'Parent ID of comment to retrieve children of.', 'wp-graphql' ),
diff --git a/src/Type/Comment/Connection/CommentConnectionResolver.php b/src/Type/Comment/Connection/CommentConnectionResolver.php
index 937bcf5f1..051bd0ad6 100755
--- a/src/Type/Comment/Connection/CommentConnectionResolver.php
+++ b/src/Type/Comment/Connection/CommentConnectionResolver.php
@@ -28,55 +28,37 @@ class CommentConnectionResolver extends ConnectionResolver {
 	public static function get_query_args( $source, array $args, AppContext $context, ResolveInfo $info ) {
 
 		/**
-		 * Set the $query_args based on various defaults and primary input $args
+		 * Prepare for later use
 		 */
-		$query_args['no_found_rows'] = true;
+		$last  = ! empty( $args['last'] ) ? $args['last'] : null;
+		$first = ! empty( $args['first'] ) ? $args['first'] : null;
 
 		/**
-		 * Pass the graphql $args to the WP_Query
+		 * Don't calculate the total rows, it's not needed and can be expensive
 		 */
-		$query_args['graphql_args'] = $args;
+		$query_args['no_found_rows'] = true;
 
 		/**
-		 * Set the graphql_cursor_offset
+		 * Set the default comment_status for Comment Queries to be "approved"
 		 */
-		$query_args['graphql_cursor_offset'] = self::get_offset( $args );
+		$query_args['comment_status'] = 'approved';
 
 		/**
-		 * Set the cursor compare direction
+		 * Set the default comment_parent for Comment Queries to be "0" to only get top level comments
 		 */
-		if ( ! empty( $args['before'] ) ) {
-			$query_args['graphql_cursor_compare'] = '<';
-		} elseif ( ! empty( $args['after'] ) ) {
-			$query_args['graphql_cursor_compare'] = '>';
-		}
+		$query_args['parent'] = 0;
 
 		/**
-		 * Handle setting dynamic $query_args based on the source (higher level query)
+		 * Set the number, ensuring it doesn't exceed the amount set as the $max_query_amount
+		 *
+		 * @since 0.0.6
 		 */
-		if ( true === is_object( $source ) ) {
-			switch ( true ) {
-				case $source instanceof \WP_Post:
-					$query_args['post_id'] = absint( $source->ID );
-					break;
-				case $source instanceof \WP_User:
-					$query_args['user_id'] = absint( $source->ID );
-					break;
-				case $source instanceof \WP_Comment:
-					$query_args['parent'] = absint( $source->comment_ID );
-					break;
-				default:
-					break;
-			}
-		}
+		$query_args['number'] = min( max( absint( $first ), absint( $last ), 10 ), self::get_query_amount( $source, $args, $context, $info ) ) + 1;
 
 		/**
-		 * Set the orderby to orderby DATE, DESC by default
+		 * Set the default order
 		 */
-		$id_order              = ! empty( $query_args['graphql_cursor_compare'] ) && '>' === $query_args['graphql_cursor_compare'] ? 'ASC' : 'DESC';
-		$query_args['orderby'] = [
-			'comment_date' => $id_order,
-		];
+		$query_args['orderby'] = 'comment_date';
 
 		/**
 		 * Take any of the $args that were part of the GraphQL query and map their
@@ -95,15 +77,53 @@ public static function get_query_args( $source, array $args, AppContext $context
 		 *
 		 * @since 0.0.5
 		 */
-		$query_args = array_merge( $query_args, $input_fields );
+		if ( ! empty( $input_fields ) ) {
+			$query_args = array_merge( $query_args, $input_fields );
+		}
 
 		/**
-		 * Set the number, ensuring it doesn't exceed the amount set as the $max_query_amount
-		 *
-		 * @since 0.0.6
+		 * Throw an exception if the query is attempted to be queried by
+		 */
+		if ( 'comment__in' === $query_args['orderby'] && empty( $query_args['comment__in'] ) ) {
+			throw new \Exception( __( 'In order to sort by comment__in, an array of IDs must be passed as the commentIn argument', 'wp-graphql' ) );
+		}
+
+		/**
+		 * If there's no orderby params in the inputArgs, set order based on the first/last argument
+		 */
+		if ( empty( $query_args['order'] ) ) {
+			$query_args['order'] = ! empty( $last ) ? 'ASC' : 'DESC';
+		}
+
+		/**
+		 * Set the graphql_cursor_offset
+		 */
+		$query_args['graphql_cursor_offset']  = self::get_offset( $args );
+		$query_args['graphql_cursor_compare'] = ( ! empty( $last ) ) ? '>' : '<';
+
+		/**
+		 * Pass the graphql $args to the WP_Query
+		 */
+		$query_args['graphql_args'] = $args;
+
+		/**
+		 * Handle setting dynamic $query_args based on the source (higher level query)
 		 */
-		$pagination_increase = ! empty( $args['first'] ) && ( empty( $args['after'] ) && empty( $args['before'] ) ) ? 0 : 1;
-		$query_args['number'] = self::get_query_amount( $source, $args, $context, $info ) + absint( $pagination_increase );
+		if ( true === is_object( $source ) ) {
+			switch ( true ) {
+				case $source instanceof \WP_Post:
+					$query_args['post_id'] = absint( $source->ID );
+					break;
+				case $source instanceof \WP_User:
+					$query_args['user_id'] = absint( $source->ID );
+					break;
+				case $source instanceof \WP_Comment:
+					$query_args['parent'] = absint( $source->comment_ID );
+					break;
+				default:
+					break;
+			}
+		}
 
 		/**
 		 * Filter the query_args that should be applied to the query. This filter is applied AFTER the input args from
@@ -119,19 +139,18 @@ public static function get_query_args( $source, array $args, AppContext $context
 		 */
 		$query_args = apply_filters( 'graphql_comment_connection_query_args', $query_args, $source, $args, $context, $info );
 
-		/**
-		 * Ensure that the query is ordered by ID in addition to any other orderby options
-		 */
-		$query_args['orderby'] = array_merge( $query_args['orderby'], [
-			'ID' => esc_sql( $id_order ),
-		] );
-
 		return $query_args;
 	}
 
+	/**
+	 *
+	 * @param $query_args
+	 *
+	 * @return \WP_Comment_Query
+	 */
 	public static function get_query( $query_args ) {
-		$query = new \WP_Comment_Query( $query_args );
-
+		$query = new \WP_Comment_Query;
+		$query->query( $query_args );
 		return $query;
 	}
 
@@ -166,18 +185,15 @@ public static function get_connection( $query, array $items, $source, array $arg
 		/**
 		 * Get the edges from the $items
 		 */
-		$edges = self::get_edges( $items );
+		$edges = self::get_edges( $items, $source, $args, $context, $info );
 
 		/**
 		 * Find the first_edge and last_edge
 		 */
 		$first_edge = $edges ? $edges[0] : null;
 		$last_edge  = $edges ? $edges[ count( $edges ) - 1 ] : null;
-
-		$edges_to_return = $edges;
-
 		$connection = [
-			'edges'    => $edges_to_return,
+			'edges'    => $edges,
 			'pageInfo' => [
 				'hasPreviousPage' => $has_previous_page,
 				'hasNextPage'     => $has_next_page,
@@ -197,8 +213,15 @@ public static function get_connection( $query, array $items, $source, array $arg
 	 *
 	 * @return array
 	 */
-	public static function get_edges( $items ) {
+	public static function get_edges( $items, $source, $args, $context, $info ) {
 		$edges = [];
+		/**
+		 * If we're doing backward pagination we want to reverse the array before
+		 * returning it to the edges
+		 */
+		if ( ! empty( $args['last'] ) ) {
+			$items = array_reverse( $items );
+		}
 		if ( ! empty( $items ) && is_array( $items ) ) {
 			foreach ( $items as $item ) {
 				$edges[] = [
diff --git a/src/Type/Enum/PostTypeEnumType.php b/src/Type/Enum/PostTypeEnumType.php
index 7ff67196b..7218c38e3 100755
--- a/src/Type/Enum/PostTypeEnumType.php
+++ b/src/Type/Enum/PostTypeEnumType.php
@@ -4,10 +4,22 @@
 
 use GraphQL\Type\Definition\EnumType;
 
+/**
+ * Class PostTypeEnumType
+ *
+ * @package WPGraphQL\Type\Enum
+ */
 class PostTypeEnumType extends EnumType {
 
+	/**
+	 * Holds the values to be used for the Enum
+	 * @var array $values
+	 */
 	private static $values;
 
+	/**
+	 * PostTypeEnumType constructor.
+	 */
 	public function __construct() {
 
 		$config = [
@@ -20,6 +32,10 @@ public function __construct() {
 
 	}
 
+	/**
+	 * This returns an array of values to be used by the Enum
+	 * @return array|null
+	 */
 	private static function values() {
 
 		if ( null === self::$values ) {
@@ -40,7 +56,7 @@ private static function values() {
 			 */
 			foreach ( $allowed_post_types as $post_type ) {
 				self::$values[ $post_type ] = [
-					'name'  => strtoupper( $post_type ),
+					'name'  => strtoupper( get_post_type_object( $post_type )->graphql_single_name ),
 					'value' => $post_type,
 				];
 			}
diff --git a/src/Type/Enum/TaxonomyEnumType.php b/src/Type/Enum/TaxonomyEnumType.php
index 72159d8ed..d7af0f939 100755
--- a/src/Type/Enum/TaxonomyEnumType.php
+++ b/src/Type/Enum/TaxonomyEnumType.php
@@ -53,7 +53,7 @@ private static function values() {
 			 */
 			foreach ( $allowed_taxonomies as $taxonomy ) {
 				self::$values[ $taxonomy ] = [
-					'name'  => strtoupper( $taxonomy ),
+					'name'  => strtoupper( get_taxonomy( $taxonomy )->graphql_single_name ),
 					'value' => $taxonomy,
 				];
 			}
diff --git a/src/Type/Plugin/PluginType.php b/src/Type/Plugin/PluginType.php
index 1373ea0f5..ccec8666a 100755
--- a/src/Type/Plugin/PluginType.php
+++ b/src/Type/Plugin/PluginType.php
@@ -106,7 +106,7 @@ private static function fields() {
 						},
 					],
 					'version' => [
-						'type' => Types::string(),
+						'type' => Types::float(),
 						'description' => __( 'Current version of the plugin.', 'wp-graphql' ),
 						'resolve' => function( array $plugin, $args, AppContext $context, ResolveInfo $info ) {
 							return ! empty( $plugin['Version'] ) ? $plugin['Version'] : '';
diff --git a/src/Type/PostObject/Connection/PostObjectConnectionArgs.php b/src/Type/PostObject/Connection/PostObjectConnectionArgs.php
index 13329eb7d..c379d9123 100755
--- a/src/Type/PostObject/Connection/PostObjectConnectionArgs.php
+++ b/src/Type/PostObject/Connection/PostObjectConnectionArgs.php
@@ -316,38 +316,33 @@ private static function orderby_enum() {
 				'values' => [
 					[
 						'name'        => 'AUTHOR',
-						'value'       => 'author',
+						'value'       => 'post_author',
 						'description' => __( 'Order by author', 'wp-graphql' ),
 					],
 					[
 						'name'        => 'TITLE',
-						'value'       => 'title',
+						'value'       => 'post_title',
 						'description' => __( 'Order by title', 'wp-graphql' ),
 					],
 					[
 						'name'        => 'SLUG',
-						'value'       => 'name',
+						'value'       => 'post_name',
 						'description' => __( 'Order by slug', 'wp-graphql' ),
 					],
 					[
 						'name'        => 'MODIFIED',
-						'value'       => 'modified',
+						'value'       => 'post_modified',
 						'description' => __( 'Order by last modified date', 'wp-graphql' ),
 					],
 					[
-						'name'        => 'PARENT',
-						'value'       => 'parent',
-						'description' => __( 'Order by parent ID', 'wp-graphql' ),
-					],
-					[
-						'name'        => 'COMMENT_COUNT',
-						'value'       => 'comment_count',
-						'description' => __( 'Order by number of comments', 'wp-graphql' ),
+						'name'        => 'DATE',
+						'value'       => 'post_date',
+						'description' => __( 'Order by publish date', 'wp-graphql' ),
 					],
 					[
-						'name'        => 'RELEVANCE',
-						'value'       => 'relevance',
-						'description' => __( 'Order by search terms in the following order: First, whether the entire sentence is matched. Second, if all the search terms are within the titles. Third, if any of the search terms appear in the titles. And, fourth, if the full sentence appears in the contents.', 'wp-graphql' ),
+						'name'        => 'PARENT',
+						'value'       => 'post_parent',
+						'description' => __( 'Order by parent ID', 'wp-graphql' ),
 					],
 					[
 						'name'        => 'IN',
diff --git a/src/Type/PostObject/Connection/PostObjectConnectionArgsDateQuery.php b/src/Type/PostObject/Connection/PostObjectConnectionArgsDateQuery.php
index 365f9c55d..e4f0a41c8 100755
--- a/src/Type/PostObject/Connection/PostObjectConnectionArgsDateQuery.php
+++ b/src/Type/PostObject/Connection/PostObjectConnectionArgsDateQuery.php
@@ -102,13 +102,11 @@ private static function fields() {
 				],
 				'inclusive' => [
 					'type' => Types::boolean(),
-					'description' => __( 'For after/before, whether exact value should be
-												matched or not', 'wp-graphql' ),
+					'description' => __( 'For after/before, whether exact value should be matched or not', 'wp-graphql' ),
 				],
 				'compare' => [
 					'type' => Types::string(),
-					'description' => __( 'For after/before, whether exact value should be
-												matched or not', 'wp-graphql' ),
+					'description' => __( 'For after/before, whether exact value should be matched or not', 'wp-graphql' ),
 				],
 				'column' => [
 					'type' => self::column_enum(),
diff --git a/src/Type/PostObject/Connection/PostObjectConnectionDefinition.php b/src/Type/PostObject/Connection/PostObjectConnectionDefinition.php
index 4b78b1b68..4e81a89d4 100755
--- a/src/Type/PostObject/Connection/PostObjectConnectionDefinition.php
+++ b/src/Type/PostObject/Connection/PostObjectConnectionDefinition.php
@@ -25,46 +25,6 @@ class PostObjectConnectionDefinition {
 	 */
 	private static $connection;
 
-	/**
-	 * Stores the definition for the debug fields that can be used for introspection into the query
-	 *
-	 * @var mixed|null|\WPGraphQL\Type\WPObjectType $debug_fields
-	 */
-	public static $debug_fields;
-
-	/**
-	 * Configures the debug_fields
-	 *
-	 * @param $connection_name
-	 *
-	 * @return mixed|null
-	 */
-	public static function debug_fields( $connection_name ) {
-
-		if ( null === self::$debug_fields ) {
-			self::$debug_fields = [];
-		}
-
-		if ( empty( self::$debug_fields[ $connection_name ] ) ) :
-			self::$debug_fields[ $connection_name ] = new WPObjectType([
-				'name' => $connection_name . 'ConnectionDebug',
-				'fields' => [
-					'queryRequest' => [
-						'type' => Types::string(),
-						'description' => __( 'The request used to query items. Useful for debugging.', 'wp-graphql' ),
-					],
-					'totalItems' => [
-						'type' => Types::int(),
-						'description' => __( 'The total items matching the query. (NOTE: Using this field can degrade performance.)', 'wp-graphql' ),
-					],
-				],
-			]);
-		endif;
-
-		return ! empty( self::$debug_fields[ $connection_name ] ) ? self::$debug_fields[ $connection_name ] : null;
-
-	}
-
 	/**
 	 * Method that sets up the relay connection for post objects
 	 *
@@ -77,10 +37,6 @@ public static function debug_fields( $connection_name ) {
 	 */
 	public static function connection( $post_type_object ) {
 
-		if ( empty( $post_type_object->name ) ) {
-			return null;
-		}
-
 		if ( null === self::$connection ) {
 			self::$connection = [];
 		}
@@ -103,9 +59,6 @@ public static function connection( $post_type_object ) {
 								return $post_type_object;
 							},
 						],
-						'debug' => [
-							'type' => self::debug_fields( $post_type_object->graphql_plural_name ),
-						],
 					];
 				},
 			] );
@@ -129,6 +82,7 @@ public static function connection( $post_type_object ) {
 			 */
 			self::$connection[ $post_type_object->name ] = [
 				'type'        => $connection['connectionType'],
+				// Translators: the placeholder is the name of the post_type
 				'description' => sprintf( __( 'A collection of %s objects', 'wp-graphql' ), $post_type_object->graphql_plural_name ),
 				'args'        => array_merge( Relay::connectionArgs(), $args ),
 				'resolve'     => function( $source, array $args, AppContext $context, ResolveInfo $info ) use ( $post_type_object ) {
diff --git a/src/Type/PostObject/Connection/PostObjectConnectionResolver.php b/src/Type/PostObject/Connection/PostObjectConnectionResolver.php
index b4ce453ba..a4f656bf2 100755
--- a/src/Type/PostObject/Connection/PostObjectConnectionResolver.php
+++ b/src/Type/PostObject/Connection/PostObjectConnectionResolver.php
@@ -1,4 +1,5 @@
 ';
-		}
+		$query_args['posts_per_page'] = min( max( absint( $first ), absint( $last ), 10 ), self::get_query_amount( $source, $args, $context, $info ) ) + 1;
 
 		/**
-		 * Set the orderby to orderby DATE, DESC by default
+		 * Set the graphql_cursor_offset which is used by Config::graphql_wp_query_cursor_pagination_support
+		 * to filter the WP_Query to support cursor pagination
 		 */
-		$id_order              = ! empty( $query_args['graphql_cursor_compare'] ) && '>' === $query_args['graphql_cursor_compare'] ? 'ASC' : 'DESC';
-		$query_args['orderby'] = [
-			'date' => esc_sql( $id_order ),
-		];
+		$query_args['graphql_cursor_offset']  = self::get_offset( $args );
+		$query_args['graphql_cursor_compare'] = ( ! empty( $last ) ) ? '>' : '<';
 
 		/**
-		 * We only need to calculate the totalItems matching the query, if it's been specifically asked
-		 * for in the Query response.
+		 * Pass the graphql $args to the WP_Query
 		 */
-		$field_selection = $info->getFieldSelection( 10 );
-
-		if ( ! empty( $field_selection['debug']['totalItems'] ) ) {
-			$query_args['no_found_rows'] = false;
-		}
+		$query_args['graphql_args'] = $args;
 
 		/**
-		 * Take any of the input $args (under the "where" input) that were part of the GraphQL query and map and
-		 * sanitize their GraphQL input to apply to the WP_Query
+		 * Collect the input_fields and sanitize them to prepare them for sending to the WP_Query
 		 */
 		$input_fields = [];
 		if ( ! empty( $args['where'] ) ) {
@@ -109,27 +101,38 @@ public static function get_query_args( $source, array $args, AppContext $context
 		}
 
 		/**
-		 * Merge the default $query_args with the $args that were entered in the query.
-		 *
-		 * @since 0.0.5
+		 * Merge the input_fields with the default query_args
 		 */
 		if ( ! empty( $input_fields ) ) {
 			$query_args = array_merge( $query_args, $input_fields );
 		}
 
 		/**
-		 * Map the orderby Input args to the WP_Query args
+		 * Map the orderby inputArgs to the WP_Query
 		 */
 		if ( ! empty( $args['where']['orderby'] ) && is_array( $args['where']['orderby'] ) ) {
+			$query_args['orderby'] = [];
 			foreach ( $args['where']['orderby'] as $orderby_input ) {
 				if ( ! empty( $orderby_input['field'] ) ) {
-					$query_args['orderby'][ esc_sql( $orderby_input['field'] ) ] = ! empty( $orderby_input['order'] ) ? esc_sql( $orderby_input['order'] ) : 'DESC';
+					$query_args['orderby'] = [
+						esc_sql( $orderby_input['field'] ) => esc_sql( $orderby_input['order'] ),
+					];
 				}
 			}
 		}
 
 		/**
-		 * Handle setting dynamic $query_args based on the source (higher level query)
+		 * If there's no orderby params in the inputArgs, set order based on the first/last argument
+		 */
+		if ( empty( $query_args['orderby'] ) ) {
+			$query_args['order'] = ! empty( $last ) ? 'ASC' : 'DESC';
+		}
+
+		/**
+		 * Determine where we're at in the Graph and adjust the query context appropriately.
+		 *
+		 * For example, if we're querying for posts as a field of termObject query, this will automatically
+		 * set the query to pull posts that belong to that term.
 		 */
 		if ( true === is_object( $source ) ) :
 			switch ( true ) {
@@ -161,19 +164,15 @@ public static function get_query_args( $source, array $args, AppContext $context
 		}
 
 		/**
-		 * Ensure that the query is ordered by ID in addition to any other orderby options
-		 */
-		$query_args['orderby'] = array_merge( $query_args['orderby'], [
-			'ID' => esc_sql( $id_order ),
-		] );
-
-		/**
-		 * Set the posts_per_page, ensuring it doesn't exceed the amount set as the $max_query_amount
+		 * Filter the $query args to allow folks to customize queries programmatically
 		 *
-		 * @since 0.0.6
+		 * @param $query_args The args that will be passed to the WP_Query
+		 * @param $source     The source that's passed down the GraphQL queries
+		 * @param $args       The inputArgs on the field
+		 * @param $context    The AppContext passed down the GraphQL tree
+		 * @param $info       The ResolveInfo passed down the GraphQL tree
 		 */
-		$pagination_increase = ! empty( $args['first'] ) && ( empty( $args['after'] ) && empty( $args['before'] ) ) ? 0 : 1;
-		$query_args['posts_per_page'] = self::get_query_amount( $source, $args, $context, $info ) + absint( $pagination_increase );
+		$query_args = apply_filters( 'graphql_post_object_connection_query_args', $query_args, $source, $args, $context, $info );
 
 		return $query_args;
 
@@ -196,12 +195,12 @@ public static function get_query( $query_args ) {
 	 * This takes an array of items, the $args and the $query and returns the connection including
 	 * the edges and page info
 	 *
-	 * @param mixed $query The Query that was processed to get the connection data
-	 * @param array $items The array of items being connected
-	 * @param array $args  The $args that were passed to the query
-	 * @param mixed $source The source being passed down the resolve tree
-	 * @param AppContext $context The AppContext being passed down the resolve tree
-	 * @param ResolveInfo $info the ResolveInfo passed down the resolve tree
+	 * @param mixed       $query   The Query that was processed to get the connection data
+	 * @param array       $items   The array of items being connected
+	 * @param array       $args    The $args that were passed to the query
+	 * @param mixed       $source  The source being passed down the resolve tree
+	 * @param AppContext  $context The AppContext being passed down the resolve tree
+	 * @param ResolveInfo $info    the ResolveInfo passed down the resolve tree
 	 *
 	 * @return array
 	 */
@@ -212,17 +211,18 @@ public static function get_connection( $query, array $items, $source, array $arg
 		 */
 		$items = ! empty( $items ) && is_array( $items ) ? $items : [];
 
+		$info = self::get_query_info( $query );
+
 		/**
 		 * Set whether there is or is not another page
 		 */
-		$has_previous_page = ( ! empty( $args['last'] ) && count( $items ) > self::get_amount_requested( $args ) ) ? true : false;
-		$has_next_page     = ( ! empty( $args['first'] ) && count( $items ) > self::get_amount_requested( $args ) ) ? true : false;
+		$has_previous_page = ( ! empty( $args['last'] ) && ( $info['total_items'] >= self::get_query_amount( $source, $args, $context, $info ) ) ) ? true : false;
+		$has_next_page     = ( ! empty( $args['first'] ) && ( $info['total_items'] >= self::get_query_amount( $source, $args, $context, $info ) ) ) ? true : false;
 
 		/**
 		 * Slice the array to the amount of items that were requested
 		 */
-		$items = array_slice( $items, 0, self::get_amount_requested( $args ) );
-		$items = array_reverse( $items );
+		$items = array_slice( $items, 0, self::get_query_amount( $source, $args, $query, $info ) );
 
 		/**
 		 * Get the edges from the $items
@@ -232,9 +232,8 @@ public static function get_connection( $query, array $items, $source, array $arg
 		/**
 		 * Find the first_edge and last_edge
 		 */
-		$first_edge = $edges ? $edges[0] : null;
-		$last_edge  = $edges ? $edges[ count( $edges ) - 1 ] : null;
-
+		$first_edge      = $edges ? $edges[0] : null;
+		$last_edge       = $edges ? $edges[ count( $edges ) - 1 ] : null;
 		$edges_to_return = $edges;
 
 		/**
@@ -264,6 +263,14 @@ public static function get_connection( $query, array $items, $source, array $arg
 	public static function get_edges( $items, $source, $args, $context, $info ) {
 		$edges = [];
 
+		/**
+		 * If we're doing backward pagination we want to reverse the array before
+		 * returning it to the edges
+		 */
+		if ( ! empty( $args['last'] ) ) {
+			$items = array_reverse( $items );
+		}
+
 		if ( ! empty( $items ) && is_array( $items ) ) {
 			foreach ( $items as $item ) {
 				$edges[] = [
diff --git a/src/Type/PostObject/Mutation/PostObjectCreate.php b/src/Type/PostObject/Mutation/PostObjectCreate.php
index 46fa8c8dd..dca13328b 100644
--- a/src/Type/PostObject/Mutation/PostObjectCreate.php
+++ b/src/Type/PostObject/Mutation/PostObjectCreate.php
@@ -127,11 +127,9 @@ public static function mutate( \WP_Post_Type $post_type_object ) {
 
 			] );
 
-			return self::$mutation[ $post_type_object->graphql_single_name ];
-
 		endif; // End if().
 
-		return self::$mutation;
+		return ! empty( self::$mutation[ $post_type_object->graphql_single_name ] ) ? self::$mutation[ $post_type_object->graphql_single_name ] : null;
 
 	}
 
diff --git a/src/Type/PostObject/Mutation/PostObjectDelete.php b/src/Type/PostObject/Mutation/PostObjectDelete.php
index 5ad51bd3f..7b4ae4407 100644
--- a/src/Type/PostObject/Mutation/PostObjectDelete.php
+++ b/src/Type/PostObject/Mutation/PostObjectDelete.php
@@ -70,14 +70,6 @@ public static function mutate( \WP_Post_Type $post_type_object ) {
 				],
 				'mutateAndGetPayload' => function( $input ) use ( $post_type_object, $mutation_name ) {
 
-					/**
-					 * Throw an exception if there's no input
-					 */
-					if ( ( empty( $post_type_object->name ) ) || ( empty( $input ) || ! is_array( $input ) ) ) {
-						// Translators: The placeholder is the name of the post type for the object being deleted
-						throw new \Exception( sprintf( __( 'Mutation not processed. There was no input for the mutation or the %1$s was invalid', 'wp-graphql' ), $post_type_object->graphql_single_name ) );
-					}
-
 					/**
 					 * Get the ID from the global ID
 					 */
@@ -91,7 +83,9 @@ public static function mutate( \WP_Post_Type $post_type_object ) {
 						throw new \Exception( sprintf( __( 'Sorry, you are not allowed to delete %1$s', 'wp-graphql' ), $post_type_object->graphql_plural_name ) );
 					}
 
-					// Check if we should force delete or not
+					/**
+					 * Check if we should force delete or not
+					 */
 					$force_delete = ( ! empty( $input['forceDelete'] ) && true === $input['forceDelete'] ) ? true : false;
 
 					/**
@@ -103,17 +97,11 @@ public static function mutate( \WP_Post_Type $post_type_object ) {
 					 * If the post is already in the trash, and the forceDelete input was not passed,
 					 * don't remove from the trash
 					 */
-					if ( true !== $force_delete && 'trash' === $post_before_delete->post_status ) {
-						// Translators: the first placeholder is the post_type of the object being deleted and the second placeholder is the unique ID of that object
-						throw new \Exception( sprintf( __( 'The %1$s with id %2$s is already in the trash. To remove from the trash, use the forceDelete input', 'wp-graphql' ), $post_type_object->graphql_single_name, $input['id'] ) );
-					}
-
-					/**
-					 * If there's no post with the input ID, or the post with that ID is of a different post_type
-					 */
-					if ( empty( $post_before_delete ) || $post_before_delete->post_type !== $post_type_object->name ) {
-						// Translators: the first placeholder is the post_type of the object being deleted and the second placeholder is the unique ID of that object
-						throw new \Exception( sprintf( __( 'No %1$s with id %2$s exists', 'wp-graphql' ), $post_type_object->graphql_single_name, $input['id'] ) );
+					if ( 'trash' === $post_before_delete->post_status ) {
+						if ( true !== $force_delete ) {
+							// Translators: the first placeholder is the post_type of the object being deleted and the second placeholder is the unique ID of that object
+							throw new \Exception( sprintf( __( 'The %1$s with id %2$s is already in the trash. To remove from the trash, use the forceDelete input', 'wp-graphql' ), $post_type_object->graphql_single_name, $input['id'] ) );
+						}
 					}
 
 					/**
@@ -121,11 +109,6 @@ public static function mutate( \WP_Post_Type $post_type_object ) {
 					 */
 					$deleted = wp_delete_post( $id_parts['id'], $force_delete );
 
-					if ( empty( $deleted ) ) {
-						// Translators: the first placeholder is the post_type of the object being deleted and the second placeholder is the unique ID of that object
-						throw new \Exception( sprintf( __( 'The %1$s with id %2$s was not deleted', 'wp-graphql' ), $post_type_object->graphql_single_name, $input['id'] ) );
-					}
-
 					/**
 					 * If the post was moved to the trash, spoof the object's status before returning it
 					 */
@@ -141,11 +124,9 @@ public static function mutate( \WP_Post_Type $post_type_object ) {
 				},
 			] );
 
-			return self::$mutation[ $post_type_object->graphql_single_name ];
-
 		endif; // End if().
 
-		return self::$mutation;
+		return ! empty( self::$mutation[ $post_type_object->graphql_single_name ] ) ? self::$mutation[ $post_type_object->graphql_single_name ] : null;
 
 	}
 
diff --git a/src/Type/PostObject/Mutation/PostObjectUpdate.php b/src/Type/PostObject/Mutation/PostObjectUpdate.php
index 6514fdcf4..cacc1b5be 100644
--- a/src/Type/PostObject/Mutation/PostObjectUpdate.php
+++ b/src/Type/PostObject/Mutation/PostObjectUpdate.php
@@ -109,19 +109,7 @@ public static function mutate( \WP_Post_Type $post_type_object ) {
 					 * Throw an exception if the post failed to update
 					 */
 					if ( is_wp_error( $post_id ) ) {
-						$error_message = $post_id->get_error_message();
-						if ( ! empty( $error_message ) ) {
-							throw new \Exception( esc_html( $error_message ) );
-						} else {
-							throw new \Exception( __( 'The object failed to update but no error was provided', 'wp-graphql' ) );
-						}
-					}
-
-					/**
-					 * If the $post_id is empty, we should throw an exception
-					 */
-					if ( empty( $post_id ) ) {
-						throw new \Exception( __( 'The object failed to update', 'wp-graphql' ) );
+						throw new \Exception( __( 'The object failed to update but no error was provided', 'wp-graphql' ) );
 					}
 
 					/**
@@ -153,11 +141,9 @@ public static function mutate( \WP_Post_Type $post_type_object ) {
 				},
 			]);
 
-			return self::$mutation[ $post_type_object->graphql_single_name ];
-
 		endif; // End if().
 
-		return self::$mutation;
+		return ! empty( self::$mutation[ $post_type_object->graphql_single_name ] ) ? self::$mutation[ $post_type_object->graphql_single_name ] : null;
 
 	}
 
diff --git a/src/Type/PostObject/PostObjectQuery.php b/src/Type/PostObject/PostObjectQuery.php
index aea85ad97..3fe61209d 100755
--- a/src/Type/PostObject/PostObjectQuery.php
+++ b/src/Type/PostObject/PostObjectQuery.php
@@ -50,7 +50,6 @@ public static function root_query( $post_type_object ) {
 
 			return self::$root_query[ $post_type_object->name ];
 		endif;
-		return self::$root_query;
 	}
 
 }
diff --git a/src/Type/PostObject/PostObjectType.php b/src/Type/PostObject/PostObjectType.php
index c66c2dbb8..4a941d7da 100755
--- a/src/Type/PostObject/PostObjectType.php
+++ b/src/Type/PostObject/PostObjectType.php
@@ -347,6 +347,67 @@ private static function fields( $post_type_object ) {
 							return ! empty( $link ) ? $link : null;
 						},
 					],
+					'terms' => [
+						'type' => Types::list_of( Types::term_object_union() ),
+						'args' => [
+							'taxonomy' => [
+								'type' => Types::list_of( Types::taxonomy_enum() ),
+								'description' => __( 'Select which taxonomies to limit the results to', 'wp-graphql' ),
+								'defaultValue' => null,
+							],
+						],
+						// Translators: placeholder is the name of the post_type
+						'description' => sprintf( __( 'Terms connected to the %1$s', 'wp-graphql' ), $single_name ),
+						'resolve' => function( \WP_Post $post, $args, AppContext $context, ResolveInfo $info ) use ( $allowed_taxonomies ) {
+
+							$terms = [];
+							/**
+							 * If the $arg for taxonomies is populated, use it as the $allowed_taxonomies
+							 * otherwise use the default $allowed_taxonomies passed down
+							 */
+							$allowed_taxonomies = ! empty( $args['taxonomy'] ) ? [ $args['taxonomy'] ] : $allowed_taxonomies;
+							if ( ! empty( $allowed_taxonomies ) && is_array( $allowed_taxonomies ) ) {
+								foreach ( $allowed_taxonomies as $taxonomy ) {
+									$tax_terms = get_the_terms( $post->ID, $taxonomy );
+									if ( ! empty( $tax_terms ) && is_array( $tax_terms ) ) {
+										$terms = array_merge( $terms, $tax_terms );
+									}
+								}
+							}
+							return ! empty( $terms ) ? $terms : null;
+						},
+					],
+					'termNames' => [
+						'type' => Types::list_of( Types::string() ),
+						'args' => [
+							'taxonomy' => [
+								'type' => Types::taxonomy_enum(),
+								'description' => __( 'Select which taxonomies to limit the results to', 'wp-graphql' ),
+								'defaultValue' => null,
+							],
+						],
+						// Translators: placeholder is the name of the post_type
+						'description' => sprintf( __( 'Terms connected to the %1$s', 'wp-graphql' ), $single_name ),
+						'resolve' => function( \WP_Post $post, $args, AppContext $context, ResolveInfo $info ) use ( $allowed_taxonomies ) {
+
+							$terms = [];
+							/**
+							 * If the $arg for taxonomies is populated, use it as the $allowed_taxonomies
+							 * otherwise use the default $allowed_taxonomies passed down
+							 */
+							$allowed_taxonomies = ! empty( $args['taxonomy'] ) ? [ $args['taxonomy'] ] : $allowed_taxonomies;
+							if ( ! empty( $allowed_taxonomies ) && is_array( $allowed_taxonomies ) ) {
+								foreach ( $allowed_taxonomies as $taxonomy ) {
+									$tax_terms = get_the_terms( $post->ID, $taxonomy );
+									if ( ! empty( $tax_terms ) && is_array( $tax_terms ) ) {
+										$terms = array_merge( $terms, $tax_terms );
+									}
+								}
+							}
+							$term_names = wp_list_pluck( $terms, 'name' );
+							return ! empty( $term_names ) ? $term_names : null;
+						},
+					],
 				];
 
 				/**
diff --git a/src/Type/PostType/PostTypeType.php b/src/Type/PostType/PostTypeType.php
index 6ee3211f6..ccd8e0500 100755
--- a/src/Type/PostType/PostTypeType.php
+++ b/src/Type/PostType/PostTypeType.php
@@ -114,42 +114,42 @@ private static function fields() {
 						'type' => Types::boolean(),
 						'description' => __( 'Whether to exclude posts with this post type from front end search results.', 'wp-graphql' ),
 						'resolve' => function( \WP_Post_Type $post_type, $args, AppContext $context, ResolveInfo $info ) {
-							return ! empty( $post_type->exclude_from_search ) ? $post_type->exclude_from_search : false;
+							return ( true === $post_type->exclude_from_search ) ? true : false;
 						},
 					],
 					'publiclyQueryable' => [
 						'type' => Types::boolean(),
 						'description' => __( 'Whether queries can be performed on the front end for the post type as part of parse_request().', 'wp-graphql' ),
 						'resolve' => function( \WP_Post_Type $post_type, array $args, AppContext $context, ResolveInfo $info ) {
-							return ! empty( $post_type->publicly_queryable ) ? $post_type->publicly_queryable : null;
+							return ( true === $post_type->publicly_queryable ) ? true : false;
 						},
 					],
 					'showUi' => [
 						'type' => Types::boolean(),
 						'description' => __( 'Whether to generate and allow a UI for managing this post type in the admin.', 'wp-graphql' ),
 						'resolve' => function( \WP_Post_Type $post_type, array $args, AppContext $context, ResolveInfo $info ) {
-							return ! empty( $post_type->show_ui ) ? $post_type->show_ui : null;
+							return ( true === $post_type->show_ui ) ? true : false;
 						},
 					],
 					'showInMenu' => [
 						'type' => Types::boolean(),
 						'description' => __( 'Where to show the post type in the admin menu. To work, $show_ui must be true. If true, the post type is shown in its own top level menu. If false, no menu is shown. If a string of an existing top level menu (eg. "tools.php" or "edit.php?post_type=page"), the post type will be placed as a sub-menu of that.', 'wp-graphql' ),
 						'resolve' => function( \WP_Post_Type $post_type, array $args, AppContext $context, ResolveInfo $info ) {
-							return ! empty( $post_type->show_in_menu ) ? $post_type->show_in_menu : null;
+							return ( true === $post_type->show_in_menu ) ? true : false;
 						},
 					],
 					'showInNavMenus' => [
 						'type' => Types::boolean(),
 						'description' => __( 'Makes this post type available for selection in navigation menus.', 'wp-graphql' ),
 						'resolve' => function( \WP_Post_Type $post_type, array $args, AppContext $context, ResolveInfo $info ) {
-							return ! empty( $post_type->show_in_nav_menus ) ? $post_type->show_in_nav_menus : null;
+							return ( true === $post_type->show_in_nav_menus ) ? true : false;
 						},
 					],
 					'showInAdminBar' => [
 						'type' => Types::boolean(),
 						'description' => __( 'Makes this post type available via the admin bar.', 'wp-graphql' ),
 						'resolve' => function( \WP_Post_Type $post_type, array $args, AppContext $context, ResolveInfo $info ) {
-							return ! empty( $post_type->show_in_admin_bar ) ? $post_type->show_in_admin_bar : null;
+							return empty( true === $post_type->show_in_admin_bar ) ? true : false;
 						},
 					],
 					'menuPosition' => [
@@ -170,28 +170,28 @@ private static function fields() {
 						'type' => Types::boolean(),
 						'description' => __( 'Whether this content type should have archives. Content archives are generated by type and by date.', 'wp-graphql' ),
 						'resolve' => function( \WP_Post_Type $post_type, array $args, AppContext $context, ResolveInfo $info ) {
-							return ! empty( $post_type->has_archive ) ? $post_type->has_archive : null;
+							return ( true === $post_type->has_archive ) ? true : false;
 						},
 					],
 					'canExport' => [
 						'type' => Types::boolean(),
 						'description' => __( 'Whether this content type should can be exported.', 'wp-graphql' ),
 						'resolve' => function( \WP_Post_Type $post_type, array $args, AppContext $context, ResolveInfo $info ) {
-							return ! empty( $post_type->can_export ) ? $post_type->can_export : null;
+							return ( true === $post_type->can_export ) ? true : false;
 						},
 					],
 					'deleteWithUser' => [
 						'type' => Types::boolean(),
 						'description' => __( 'Whether delete this type of content when the author of it is deleted from the system.', 'wp-graphql' ),
 						'resolve' => function( \WP_Post_Type $post_type, array $args, AppContext $context, ResolveInfo $info ) {
-							return ! empty( $post_type->delete_with_user ) ? $post_type->delete_with_user : null;
+							return ( true === $post_type->delete_with_user ) ? true : false;
 						},
 					],
 					'showInRest' => [
 						'type' => Types::boolean(),
 						'description' => __( 'Whether to add the post type route in the REST API `wp/v2` namespace.', 'wp-graphql' ),
 						'resolve' => function( \WP_Post_Type $post_type, array $args, AppContext $context, ResolveInfo $info ) {
-							return ! empty( $post_type->show_in_rest ) ? $post_type->show_in_rest : null;
+							return ( true === $post_type->show_in_rest ) ? true : false;
 						},
 					],
 					'restBase' => [
@@ -209,10 +209,10 @@ private static function fields() {
 						},
 					],
 					'showInGraphql' => [
-						'type' => Types::string(),
+						'type' => Types::boolean(),
 						'description' => __( 'Whether to add the post type to the GraphQL Schema.', 'wp-graphql' ),
 						'resolve' => function( \WP_Post_Type $post_type, array $args, AppContext $context, ResolveInfo $info ) {
-							return ! empty( $post_type->show_in_graphql ) ? $post_type->show_in_graphql : null;
+							return ( true === $post_type->show_in_graphql ) ? true : false;
 						},
 					],
 					'graphqlSingleName' => [
@@ -231,11 +231,26 @@ private static function fields() {
 					],
 					'connectedTaxonomyNames' => [
 						'type' => Types::list_of( Types::string() ),
+						'args' => [
+							'taxonomies' => [
+								'type' => Types::list_of( Types::taxonomy_enum() ),
+								'description' => __( 'Select which taxonomies to limit the results to', 'wp-graphql' ),
+								'defaultValue' => null,
+							],
+						],
 						'description' => __( 'A list of Taxonomies associated with the post type', 'wp-graphql' ),
 						'resolve' => function( \WP_Post_Type $post_type_object, array $args, $context, ResolveInfo $info ) use ( $allowed_taxonomies ) {
 
 							$object_taxonomies = get_object_taxonomies( $post_type_object->name );
+
 							$taxonomy_names = [];
+
+							/**
+							 * If the $arg for taxonomies is populated, use it as the $allowed_taxonomies
+							 * otherwise use the default $allowed_taxonomies passed down
+							 */
+							$allowed_taxonomies = ! empty( $args['taxonomies'] ) && is_array( $args['taxonomies'] ) ? $args['taxonomies'] : $allowed_taxonomies;
+
 							if ( ! empty( $object_taxonomies ) && is_array( $object_taxonomies ) ) {
 								foreach ( $object_taxonomies as $taxonomy ) {
 									if ( in_array( $taxonomy, $allowed_taxonomies, true ) ) {
@@ -248,9 +263,24 @@ private static function fields() {
 					],
 					'connectedTaxonomies' => [
 						'type' => Types::list_of( Types::taxonomy() ),
+						'args' => [
+							'taxonomies' => [
+								'type' => Types::list_of( Types::taxonomy_enum() ),
+								'description' => __( 'Select which taxonomies to limit the results to', 'wp-graphql' ),
+								'defaultValue' => null,
+							],
+						],
 						'description' => __( 'List of Taxonomies connected to the Post Type', 'wp-graphql' ),
 						'resolve' => function( \WP_Post_Type $post_type_object, array $args, AppContext $context, ResolveInfo $info ) use ( $allowed_taxonomies ) {
+
 							$tax_objects = [];
+
+							/**
+							 * If the $arg for taxonomies is populated, use it as the $allowed_taxonomies
+							 * otherwise use the default $allowed_taxonomies passed down
+							 */
+							$allowed_taxonomies = ! empty( $args['taxonomies'] ) && is_array( $args['taxonomies'] ) ? $args['taxonomies'] : $allowed_taxonomies;
+
 							if ( ! empty( $allowed_taxonomies ) && is_array( $allowed_taxonomies ) ) {
 								foreach ( $allowed_taxonomies as $taxonomy ) {
 									if ( in_array( $taxonomy, get_object_taxonomies( $post_type_object->name ), true ) ) {
@@ -264,17 +294,6 @@ private static function fields() {
 					],
 				];
 
-				/**
-				 * Add connections for post_types that are registered to the taxonomy
-				 * @since 0.0.5
-				 */
-				if ( ! empty( $allowed_taxonomies ) && is_array( $allowed_taxonomies ) ) {
-					foreach ( $allowed_taxonomies as $taxonomy ) {
-						$tax_object = get_taxonomy( $taxonomy );
-						$fields[ $tax_object->graphql_plural_name ] = TermObjectConnectionDefinition::connection( $tax_object );
-					}
-				}
-
 
 				/**
 				 * Pass the fields through a filter to allow for hooking in and adjusting the shape
diff --git a/src/Type/RootQueryType.php b/src/Type/RootQueryType.php
index a91cb52e1..d33deeafd 100755
--- a/src/Type/RootQueryType.php
+++ b/src/Type/RootQueryType.php
@@ -186,13 +186,6 @@ public function __construct() {
 		$config = [
 			'name' => 'rootQuery',
 			'fields' => $fields,
-			'resolveField' => function( $value, $args, $context, ResolveInfo $info ) {
-				if ( method_exists( $this, $info->fieldName ) ) {
-					return $this->{$info->fieldName}( $value, $args, $context, $info );
-				} else {
-					return $value->{$info->fieldName};
-				}
-			},
 		];
 
 		/**
@@ -203,27 +196,6 @@ public function __construct() {
 
 	}
 
-	/**
-	 * post_type
-	 * This sets up the post_type entry point for the root query
-	 * @return array
-	 * @since 0.0.5
-	 */
-	public static function post_type() {
-		return [
-			'type' => Types::post_type(),
-			'description' => __( 'A WordPress Post Type', 'wp-graphql' ),
-			'args' => [
-				'id' => Types::non_null( Types::id() ),
-			],
-			'resolve' => function( $source, array $args, $context, ResolveInfo $info ) {
-				$id_components = Relay::fromGlobalId( $args['id'] );
-
-				return DataSource::resolve_post_type( $id_components['id'] );
-			},
-		];
-	}
-
 	/**
 	 * theme
 	 * This sets up the theme entry point for the root query
@@ -245,27 +217,6 @@ public static function theme() {
 		];
 	}
 
-	/**
-	 * taxonomy
-	 * This sets up the taxonomy entry point for the root query
-	 * @return array
-	 * @since 0.0.5
-	 */
-	public static function taxonomy() {
-		return [
-			'type' => Types::taxonomy(),
-			'description' => __( 'A taxonomy object', 'wp-graphql' ),
-			'args' => [
-				'id' => Types::non_null( Types::id() ),
-			],
-			'resolve' => function( $source, array $args, $context, ResolveInfo $info ) {
-				$id_components = Relay::fromGlobalId( $args['id'] );
-
-				return DataSource::resolve_taxonomy( $id_components['id'] );
-			},
-		];
-	}
-
 	/**
 	 * viewer
 	 * This sets up the viewer entry point for the root query
@@ -277,12 +228,7 @@ public static function viewer() {
 			'type' => Types::user(),
 			'description' => __( 'Returns the current user', 'wp-graphql' ),
 			'resolve' => function( $source, array $args, AppContext $context, ResolveInfo $info ) {
-
-				if ( ! ( $context->viewer instanceof \WP_User ) ) {
-					throw new \Exception( __( 'The current viewer is invalid', 'wp-graphql' ) );
-				}
-
-				return false !== $context->viewer->ID ? DataSource::resolve_user( $context->viewer->ID ) : null;
+				return ( false !== $context->viewer->ID ) ? DataSource::resolve_user( $context->viewer->ID ) : null;
 			},
 		];
 	}
diff --git a/src/Type/Taxonomy/TaxonomyType.php b/src/Type/Taxonomy/TaxonomyType.php
index ce2f095be..ccab8d261 100755
--- a/src/Type/Taxonomy/TaxonomyType.php
+++ b/src/Type/Taxonomy/TaxonomyType.php
@@ -102,49 +102,49 @@ private function fields() {
 						'type' => Types::boolean(),
 						'description' => __( 'Whether to generate and allow a UI for managing terms in this taxonomy in the admin', 'wp-graphql' ),
 						'resolve' => function( \WP_Taxonomy $taxonomy, array $args, AppContext $context, ResolveInfo $info ) {
-							return ! empty( $taxonomy->show_ui ) ? $taxonomy->show_ui : null;
+							return ( true === $taxonomy->show_ui ) ? true : false;
 						},
 					],
 					'showInMenu' => [
 						'type' => Types::boolean(),
 						'description' => __( 'Whether to show the taxonomy in the admin menu', 'wp-graphql' ),
 						'resolve' => function( \WP_Taxonomy $taxonomy, array $args, AppContext $context, ResolveInfo $info ) {
-							return ! empty( $taxonomy->show_in_menu ) ? $taxonomy->show_in_menu : null;
+							return ( true === $taxonomy->show_in_menu ) ? true : false;
 						},
 					],
 					'showInNavMenus' => [
 						'type' => Types::boolean(),
 						'description' => __( 'Whether the taxonomy is available for selection in navigation menus.', 'wp-graphql' ),
 						'resolve' => function( \WP_Taxonomy $taxonomy, array $args, AppContext $context, ResolveInfo $info ) {
-							return ! empty( $taxonomy->show_in_nav_menus ) ? $taxonomy->show_in_nav_menus : null;
+							return ( true === $taxonomy->show_in_nav_menus ) ? true : false;
 						},
 					],
 					'showCloud' => [
 						'type' => Types::boolean(),
 						'description' => __( 'Whether to show the taxonomy as part of a tag cloud widget. This field is equivalent to WP_Taxonomy->show_tagcloud', 'wp-graphql' ),
 						'resolve' => function( \WP_Taxonomy $taxonomy, array $args, AppContext $context, ResolveInfo $info ) {
-							return ! empty( $taxonomy->show_tagcloud ) ? $taxonomy->show_tagcloud : null;
+							return ( true === $taxonomy->show_tagcloud ) ? true : false;
 						},
 					],
 					'showInQuickEdit' => [
 						'type' => Types::boolean(),
 						'description' => __( 'Whether to show the taxonomy in the quick/bulk edit panel.', 'wp-graphql' ),
 						'resolve' => function( \WP_Taxonomy $taxonomy, array $args, AppContext $context, ResolveInfo $info ) {
-							return ! empty( $taxonomy->show_in_quick_edit ) ? $taxonomy->show_in_quick_edit : null;
+							return ( true === $taxonomy->show_in_quick_edit ) ? true : false;
 						},
 					],
 					'showInAdminColumn' => [
 						'type' => Types::boolean(),
 						'description' => __( 'Whether to display a column for the taxonomy on its post type listing screens.', 'wp-graphql' ),
 						'resolve' => function( \WP_Taxonomy $taxonomy, array $args, AppContext $context, ResolveInfo $info ) {
-							return ! empty( $taxonomy->show_admin_column ) ? $taxonomy->show_admin_column : null;
+							return ( true === $taxonomy->show_admin_column ) ? true : false;
 						},
 					],
 					'showInRest' => [
 						'type' => Types::boolean(),
 						'description' => __( 'Whether to add the post type route in the REST API `wp/v2` namespace.', 'wp-graphql' ),
 						'resolve' => function( \WP_Taxonomy $taxonomy, array $args, AppContext $context, ResolveInfo $info ) {
-							return ! empty( $taxonomy->show_in_rest ) ? $taxonomy->show_in_rest : null;
+							return ( true === $taxonomy->show_in_rest ) ? true : false;
 						},
 					],
 					'restBase' => [
@@ -162,10 +162,10 @@ private function fields() {
 						},
 					],
 					'showInGraphql' => [
-						'type' => Types::string(),
+						'type' => Types::boolean(),
 						'description' => __( 'Whether to add the post type to the GraphQL Schema.', 'wp-graphql' ),
 						'resolve' => function( \WP_Taxonomy $taxonomy, array $args, AppContext $context, ResolveInfo $info ) {
-							return ! empty( $taxonomy->show_in_graphql ) ? $taxonomy->show_in_graphql : null;
+							return ( true === $taxonomy->show_in_graphql ) ? true : false;
 						},
 					],
 					'graphqlSingleName' => [
@@ -184,9 +184,23 @@ private function fields() {
 					],
 					'connectedPostTypeNames' => [
 						'type' => Types::list_of( Types::string() ),
+						'args' => [
+							'types' => [
+								'type' => Types::list_of( Types::post_type_enum() ),
+								'description' => __( 'Select which post types to limit the results to', 'wp-graphql' ),
+								'defaultValue' => null,
+							],
+						],
 						'description' => __( 'A list of Post Types associated with the taxonomy', 'wp-graphql' ),
 						'resolve' => function( \WP_Taxonomy $taxonomy, array $args, AppContext $context, ResolveInfo $info ) use ( $allowed_post_types ) {
 							$post_type_names = [];
+
+							/**
+							 * If the types $arg is populated, use that to filter the $allowed_post_types,
+							 * otherwise use the default $allowed_post_types passed down
+							 */
+							$allowed_post_types = ! empty( $args['types'] ) && is_array( $args['types'] ) ? $args['types'] : $allowed_post_types;
+
 							$connected_post_types = $taxonomy->object_type;
 							if ( ! empty( $connected_post_types ) && is_array( $connected_post_types ) ) {
 								foreach ( $connected_post_types as $post_type ) {
@@ -200,9 +214,24 @@ private function fields() {
 					],
 					'connectedPostTypes' => [
 						'type' => Types::list_of( Types::post_type() ),
+						'args' => [
+							'types' => [
+								'type' => Types::list_of( Types::post_type_enum() ),
+								'description' => __( 'Select which post types to limit the results to', 'wp-graphql' ),
+								'defaultValue' => null,
+							],
+						],
 						'description' => __( 'List of Post Types connected to the Taxonomy', 'wp-graphql' ),
 						'resolve' => function( \WP_Taxonomy $taxonomy, array $args, AppContext $context, ResolveInfo $info ) use ( $allowed_post_types ) {
+
 							$post_type_objects = [];
+
+							/**
+							 * If the types $arg is populated, use that to filter the $allowed_post_types,
+							 * otherwise use the default $allowed_post_types passed down
+							 */
+							$allowed_post_types = ! empty( $args['types'] ) && is_array( $args['types'] ) ? $args['types'] : $allowed_post_types;
+
 							$connected_post_types = ! empty( $taxonomy->object_type ) ? $taxonomy->object_type : [];
 							if ( ! empty( $allowed_post_types ) && is_array( $allowed_post_types ) ) {
 								foreach ( $allowed_post_types as $post_type ) {
@@ -217,17 +246,6 @@ private function fields() {
 					],
 				];
 
-				/**
-				 * Add connections for post_types that are registered to the taxonomy
-				 * @since 0.0.5
-				 */
-				if ( ! empty( $allowed_post_types ) && is_array( $allowed_post_types ) ) {
-					foreach ( $allowed_post_types as $post_type ) {
-						$post_type_object = get_post_type_object( $post_type );
-						$fields[ $post_type_object->graphql_plural_name ] = PostObjectConnectionDefinition::connection( $post_type_object );
-					}
-				}
-
 				/**
 				 * This prepares the fields by sorting them and applying a filter for adjusting the schema.
 				 * Because these fields are implemented via a closure the prepare_fields needs to be applied
diff --git a/src/Type/TermObject/Connection/TermObjectConnectionArgs.php b/src/Type/TermObject/Connection/TermObjectConnectionArgs.php
index 9a5e174dd..5fd1010f2 100755
--- a/src/Type/TermObject/Connection/TermObjectConnectionArgs.php
+++ b/src/Type/TermObject/Connection/TermObjectConnectionArgs.php
@@ -45,10 +45,6 @@ private static function fields() {
 
 		if ( null === self::$fields ) :
 			self::$fields = [
-				'taxonomy' => [
-					'type' => Types::list_of( Types::string() ),
-					'description' => __( 'Array of taxonomy names, to which results should be limited.', 'wp-graphql' ),
-				],
 				'objectIds' => [
 					'type' => Types::list_of( Types::int() ),
 					'description' => __( 'Array of object IDs. Results will be limited to terms associated with these objects.', 'wp-graphql' ),
diff --git a/src/Type/TermObject/Connection/TermObjectConnectionDefinition.php b/src/Type/TermObject/Connection/TermObjectConnectionDefinition.php
index 86e6358e2..629b6a4aa 100755
--- a/src/Type/TermObject/Connection/TermObjectConnectionDefinition.php
+++ b/src/Type/TermObject/Connection/TermObjectConnectionDefinition.php
@@ -32,10 +32,6 @@ class TermObjectConnectionDefinition {
 	 */
 	public static function connection( $taxonomy_object ) {
 
-		if ( empty( $taxonomy_object->name ) ) {
-			return;
-		}
-
 		if ( null === self::$connection ) {
 			self::$connection = [];
 		}
diff --git a/src/Type/TermObject/Connection/TermObjectConnectionResolver.php b/src/Type/TermObject/Connection/TermObjectConnectionResolver.php
index 6abfaa71a..818e48490 100755
--- a/src/Type/TermObject/Connection/TermObjectConnectionResolver.php
+++ b/src/Type/TermObject/Connection/TermObjectConnectionResolver.php
@@ -50,38 +50,34 @@ public static function get_query_args( $source, array $args, AppContext $context
 		$query_args['taxonomy'] = ! empty( self::$taxonomy ) ? self::$taxonomy : 'category';
 
 		/**
-		 * Pass the graphql $args to the WP_Query
+		 * Prepare for later use
 		 */
-		$query_args['graphql_args'] = $args;
+		$last  = ! empty( $args['last'] ) ? $args['last'] : null;
+		$first = ! empty( $args['first'] ) ? $args['first'] : null;
 
 		/**
-		 * Set the graphql_cursor_offset
+		 * Set the default parent for TermObject Queries to be "0" to only get top level terms
 		 */
-		$query_args['graphql_cursor_offset'] = self::get_offset( $args );
+		$query_args['parent'] = 0;
 
 		/**
-		 * Set the cursor compare direction
+		 * Set hide_empty as false by default
 		 */
-		if ( ! empty( $args['before'] ) ) {
-			$query_args['graphql_cursor_compare'] = '<';
-		} elseif ( ! empty( $args['after'] ) ) {
-			$query_args['graphql_cursor_compare'] = '>';
-		}
+		$query_args['hide_empty'] = false;
 
 		/**
-		 * Orderby Name by default
+		 * Set the number, ensuring it doesn't exceed the amount set as the $max_query_amount
 		 */
-		$query_args['orderby'] = 'count';
+		$query_args['number'] = min( max( absint( $first ), absint( $last ), 10 ), self::get_query_amount( $source, $args, $context, $info ) ) + 1;
 
 		/**
-		 * Set the order
+		 * Orderby Name by default
 		 */
-		$order               = ! empty( $query_args['graphql_cursor_compare'] ) && '>' === $query_args['graphql_cursor_compare'] ? 'ASC' : 'DESC';
-		$query_args['order'] = $order;
+		$query_args['orderby'] = 'name';
 
 		/**
-		 * Take any of the $args that were part of the GraphQL query and map their GraphQL names to
-		 * the WP_Term_Query names to be used in the WP_Term_Query
+		 * Take any of the $args that were part of the GraphQL query and map their
+		 * GraphQL names to the WP_Term_Query names to be used in the WP_Term_Query
 		 *
 		 * @since 0.0.5
 		 */
@@ -91,12 +87,32 @@ public static function get_query_args( $source, array $args, AppContext $context
 		}
 
 		/**
-		 * Merge the default $query_args with the $args that were entered in the query.
+		 * Merge the default $query_args with the $args that were entered
+		 * in the query.
 		 *
 		 * @since 0.0.5
 		 */
-		$query_args = array_merge( $query_args, $input_fields );
+		if ( ! empty( $input_fields ) ) {
+			$query_args = array_merge( $query_args, $input_fields );
+		}
+
+		/**
+		 * If there's no orderby params in the inputArgs, set order based on the first/last argument
+		 */
+		if ( empty( $query_args['order'] ) ) {
+			$query_args['order'] = ! empty( $last ) ? 'DESC' : 'ASC';
+		}
+
+		/**
+		 * Set the graphql_cursor_offset
+		 */
+		$query_args['graphql_cursor_offset']  = self::get_offset( $args );
+		$query_args['graphql_cursor_compare'] = ( ! empty( $last ) ) ? '>' : '<';
 
+		/**
+		 * Pass the graphql $args to the WP_Query
+		 */
+		$query_args['graphql_args'] = $args;
 
 		/**
 		 * If the source of the Query is a Post object, adjust the query args to only query terms
@@ -104,15 +120,29 @@ public static function get_query_args( $source, array $args, AppContext $context
 		 *
 		 * @since 0.0.5
 		 */
-		if ( $source instanceof \WP_Post ) {
-			$query_args['object_ids'] = $source->ID;
+		if ( true === is_object( $source ) ) {
+			switch ( true ) {
+				case $source instanceof \WP_Post:
+					$query_args['object_ids'] = $source->ID;
+					break;
+				default:
+					break;
+			}
 		}
 
 		/**
-		 * Set the number, ensuring it doesn't exceed the amount set as the $max_query_amount
+		 * Filter the query_args that should be applied to the query. This filter is applied AFTER the input args from
+		 * the GraphQL Query have been applied and has the potential to override the GraphQL Query Input Args.
+		 *
+		 * @param array       $query_args array of query_args being passed to the
+		 * @param mixed       $source     source passed down from the resolve tree
+		 * @param array       $args       array of arguments input in the field as part of the GraphQL query
+		 * @param AppContext  $context    object passed down the resolve tree
+		 * @param ResolveInfo $info       info about fields passed down the resolve tree
+		 *
+		 * @since 0.0.6
 		 */
-		$pagination_increase  = ! empty( $args['first'] ) && ( empty( $args['after'] ) && empty( $args['before'] ) ) ? 0 : 1;
-		$query_args['number'] = self::get_query_amount( $source, $args, $context, $info ) + $pagination_increase;
+		$query_args = apply_filters( 'graphql_term_object_connection_query_args', $query_args, $source, $args, $context, $info );
 
 		return $query_args;
 
@@ -127,7 +157,6 @@ public static function get_query_args( $source, array $args, AppContext $context
 	 */
 	public static function get_query( $query_args ) {
 		$query = new \WP_Term_Query( $query_args );
-
 		return $query;
 	}
 
@@ -160,12 +189,11 @@ public static function get_connection( $query, array $items, $source, array $arg
 		 * Slice the array to the amount of items that were requested
 		 */
 		$items = array_slice( $items, 0, self::get_amount_requested( $args ) );
-		$items = array_reverse( $items );
 
 		/**
 		 * Get the edges from the $items
 		 */
-		$edges = self::get_edges( $items );
+		$edges = self::get_edges( $items, $source, $args, $context, $info );
 
 		/**
 		 * Find the first_edge and last_edge
@@ -200,8 +228,17 @@ public static function get_connection( $query, array $items, $source, array $arg
 	 *
 	 * @return array
 	 */
-	public static function get_edges( $items ) {
+	public static function get_edges( $items, $source, $args, $context, $info ) {
 		$edges = [];
+
+		/**
+		 * If we're doing backward pagination we want to reverse the array before
+		 * returning it to the edges
+		 */
+		if ( ! empty( $args['last'] ) ) {
+			$items = array_reverse( $items );
+		}
+
 		if ( ! empty( $items ) && is_array( $items ) ) {
 			foreach ( $items as $item ) {
 				$edges[] = [
@@ -240,7 +277,7 @@ public static function sanitize_input_fields( array $args, $source, array $all_a
 			'descriptionLike'     => 'description__like',
 			'padCounts'           => 'pad_counts',
 			'childOf'             => 'child_of',
-			'cacheDomain'         => 'cacheDomain',
+			'cacheDomain'         => 'cache_domain',
 			'updateTermMetaCache' => 'update_term_meta_cache',
 		];
 
diff --git a/src/Type/TermObject/Mutation/TermObjectCreate.php b/src/Type/TermObject/Mutation/TermObjectCreate.php
index 4eb80784b..f04d3b5db 100644
--- a/src/Type/TermObject/Mutation/TermObjectCreate.php
+++ b/src/Type/TermObject/Mutation/TermObjectCreate.php
@@ -45,13 +45,6 @@ public static function mutate( \WP_Taxonomy $taxonomy ) {
 				],
 				'mutateAndGetPayload' => function( $input ) use ( $taxonomy, $mutation_name ) {
 
-					/**
-					 * Throw an exception if there's no input
-					 */
-					if ( ( empty( $taxonomy->name ) ) || ( empty( $input ) || ! is_array( $input ) ) ) {
-						throw new \Exception( __( 'Mutation not processed. There was no input for the mutation or the taxonomy was invalid', 'wp-graphql' ) );
-					}
-
 					/**
 					 * Ensure the user can edit_terms
 					 */
@@ -115,12 +108,9 @@ public static function mutate( \WP_Taxonomy $taxonomy ) {
 				},
 			] );
 
-			return self::$mutation[ $taxonomy->graphql_single_name ];
-
 		endif; // End if().
 
-		return self::$mutation;
-
+		return ! empty( self::$mutation[ $taxonomy->graphql_single_name ] ) ? self::$mutation[ $taxonomy->graphql_single_name ] : null;
 	}
 
 	/**
diff --git a/src/Type/TermObject/Mutation/TermObjectDelete.php b/src/Type/TermObject/Mutation/TermObjectDelete.php
index 159dc0428..04f00e865 100644
--- a/src/Type/TermObject/Mutation/TermObjectDelete.php
+++ b/src/Type/TermObject/Mutation/TermObjectDelete.php
@@ -61,12 +61,8 @@ public static function mutate( \WP_Taxonomy $taxonomy ) {
 				],
 				'mutateAndGetPayload' => function( $input ) use ( $taxonomy, $mutation_name ) {
 
-					if ( empty( $taxonomy->name ) || empty( $input ) || ! is_array( $input ) ) {
-						// Translators: The placeholder is the name of the taxonomy for the term being deleted
-						throw new \Exception( sprintf( __( 'Mutation not processed. There was no input for the mutation or the %1$s was invalid', 'wp-graphql' ), $taxonomy->graphql_single_name ) );
-					}
-
 					$id_parts = Relay::fromGlobalId( $input['id'] );
+
 					if ( ! empty( $id_parts['id'] ) && absint( $id_parts['id'] ) ) {
 						$term_id = absint( $id_parts['id'] );
 					} else {
@@ -123,11 +119,9 @@ public static function mutate( \WP_Taxonomy $taxonomy ) {
 				},
 			]);
 
-			return self::$mutation[ $taxonomy->graphql_single_name ];
-
 		endif; // End if().
 
-		return self::$mutation;
+		return ! empty( self::$mutation[ $taxonomy->graphql_single_name ] ) ? self::$mutation[ $taxonomy->graphql_single_name ] : null;
 
 	}
 }
diff --git a/src/Type/TermObject/Mutation/TermObjectMutation.php b/src/Type/TermObject/Mutation/TermObjectMutation.php
index e4d519f56..a4ed213c5 100644
--- a/src/Type/TermObject/Mutation/TermObjectMutation.php
+++ b/src/Type/TermObject/Mutation/TermObjectMutation.php
@@ -120,12 +120,19 @@ public static function prepare_object( $input, \WP_Taxonomy $taxonomy, $mutation
 			$parent_id_parts = ! empty( $input['parentId'] ) ? Relay::fromGlobalId( $input['parentId'] ) : null;
 
 			/**
-			 * If the term
+			 * Ensure that the ID passed in is a valid GlobalID
 			 */
-			if ( is_array( $parent_id_parts ) && ! empty( $parent_id_parts['id'] ) && is_int( $parent_id_parts['id'] ) ) {
+			if ( is_array( $parent_id_parts ) && ! empty( $parent_id_parts['id'] ) ) {
+
+				/**
+				 * Get the Term ID from the global ID
+				 */
 				$parent_id = $parent_id_parts['id'];
 
-				$parent_term = get_term( absint( $parent_id, $taxonomy->name ) );
+				/**
+				 * Ensure there's actually a parent term to be associated with
+				 */
+				$parent_term = get_term( absint( $parent_id ), $taxonomy->name );
 
 				if ( ! $parent_term || is_wp_error( $parent_term ) ) {
 					throw new \Exception( __( 'The parent does not exist', 'wp-graphql' ) );
diff --git a/src/Type/TermObject/Mutation/TermObjectUpdate.php b/src/Type/TermObject/Mutation/TermObjectUpdate.php
index 93f177f73..bec14ff4c 100644
--- a/src/Type/TermObject/Mutation/TermObjectUpdate.php
+++ b/src/Type/TermObject/Mutation/TermObjectUpdate.php
@@ -42,11 +42,6 @@ public static function mutate( \WP_Taxonomy $taxonomy ) {
 				],
 				'mutateAndGetPayload' => function( $input ) use ( $taxonomy, $mutation_name ) {
 
-					if ( empty( $input['id'] ) ) {
-						// Translators: The placeholder is the name of the taxonomy for the term being edited
-						throw new \Exception( sprintf( __( 'ID is required to update the %1$s', 'wp-graphql' ), $taxonomy->graphql_single_name ) );
-					}
-
 					/**
 					 * Get the ID parts
 					 */
@@ -102,13 +97,8 @@ public static function mutate( \WP_Taxonomy $taxonomy ) {
 						 * Respond with any errors
 						 */
 						if ( is_wp_error( $update ) ) {
-							$error_message = $update->get_error_message();
-							if ( ! empty( $error_message ) ) {
-								throw new \Exception( esc_html( $error_message ) );
-							} else {
-								// Translators: The placeholder is the name of the taxonomy for the term being deleted
-								throw new \Exception( sprintf( __( 'The %1$s failed to update', 'wp-graphql' ), $taxonomy->name ) );
-							}
+							// Translators: the placeholder is the name of the taxonomy
+							throw new \Exception( sprintf( __( 'The %1$s failed to update', 'wp-graphql' ), $taxonomy->name ) );
 						}
 					}
 
@@ -131,11 +121,9 @@ public static function mutate( \WP_Taxonomy $taxonomy ) {
 				},
 			] );
 
-			return self::$mutation[ $taxonomy->graphql_single_name ];
-
 		endif; // End if().
 
-		return self::$mutation;
+		return ! empty( self::$mutation[ $taxonomy->graphql_single_name ] ) ?  self::$mutation[ $taxonomy->graphql_single_name ] : null;
 
 	}
 
diff --git a/src/Type/TermObject/TermObjectQuery.php b/src/Type/TermObject/TermObjectQuery.php
index 4a76a4c1e..14c4ac9fd 100755
--- a/src/Type/TermObject/TermObjectQuery.php
+++ b/src/Type/TermObject/TermObjectQuery.php
@@ -50,7 +50,6 @@ public static function root_query( $taxonomy_object ) {
 
 			return self::$root_query[ $taxonomy_object->name ];
 		endif;
-		return self::$root_query;
 	}
 
 }
diff --git a/src/Type/TermObject/TermObjectType.php b/src/Type/TermObject/TermObjectType.php
index f3363cd24..d4aa1f061 100755
--- a/src/Type/TermObject/TermObjectType.php
+++ b/src/Type/TermObject/TermObjectType.php
@@ -5,6 +5,7 @@
 use GraphQL\Type\Definition\ResolveInfo;
 use GraphQLRelay\Relay;
 use WPGraphQL\AppContext;
+use WPGraphQL\Data\DataSource;
 use WPGraphQL\Type\PostObject\Connection\PostObjectConnectionDefinition;
 use WPGraphQL\Type\WPObjectType;
 use WPGraphQL\Types;
@@ -129,20 +130,6 @@ private static function fields( $taxonomy_object ) {
 							return ! empty( $term->term_id ) ? absint( $term->term_id ) : null;
 						},
 					],
-					'ancestors' => [
-						'type' => Types::list_of( Types::term_object( $taxonomy_object->name ) ),
-						'description' => esc_html__( 'The ancestors of the object', 'wp-graphql' ),
-						'resolve' => function( \WP_Term $term, $args, AppContext $context, ResolveInfo $info ) {
-							$ancestors = [];
-							$ancestor_ids = get_ancestors( $term->term_id, $term->taxonomy );
-							if ( ! empty( $ancestor_ids ) ) {
-								foreach ( $ancestor_ids as $ancestor_id ) {
-									$ancestors[] = get_term( $ancestor_id );
-								}
-							}
-							return ! empty( $ancestors ) ? $ancestors : null;
-						},
-					],
 					'count' => [
 						'type' => Types::int(),
 						'description' => __( 'The number of objects connected to the object', 'wp-graphql' ),
@@ -205,6 +192,35 @@ private static function fields( $taxonomy_object ) {
 					],
 				];
 
+				/**
+				 * For hierarchical taxonomies, provide parent and ancestor fields
+				 */
+				if ( true === $taxonomy_object->hierarchical ) {
+					$fields['parent'] = [
+						'type' => Types::term_object( $taxonomy_object->name ),
+						'description' => __( 'The parent object', 'wp-graphql' ),
+						'resolve' => function( \WP_Term $term, $args, AppContext $context, ResolveInfo $info ) {
+							return ! empty( $term->parent ) ? DataSource::resolve_term_object( $term->parent, $term->taxonomy ) : null;
+						},
+					];
+
+					$fields['ancestors'] = [
+						'type' => Types::list_of( Types::term_object( $taxonomy_object->name ) ),
+						'description' => esc_html__( 'The ancestors of the object', 'wp-graphql' ),
+						'resolve' => function( \WP_Term $term, $args, AppContext $context, ResolveInfo $info ) {
+							$ancestors = [];
+							$ancestor_ids = get_ancestors( $term->term_id, $term->taxonomy );
+							if ( ! empty( $ancestor_ids ) ) {
+								foreach ( $ancestor_ids as $ancestor_id ) {
+									$ancestors[] = get_term( $ancestor_id );
+								}
+							}
+							return ! empty( $ancestors ) ? $ancestors : null;
+						},
+					];
+
+				}
+
 				/**
 				 * Add connections for post_types that are registered to the taxonomy
 				 *
diff --git a/src/Type/Theme/ThemeType.php b/src/Type/Theme/ThemeType.php
index 554cdb10d..79df2df79 100755
--- a/src/Type/Theme/ThemeType.php
+++ b/src/Type/Theme/ThemeType.php
@@ -103,7 +103,7 @@ private static function fields() {
 						'type' => Types::string(),
 						'description' => __( 'A URI if the theme has a website associated with it. The Theme URI is handy for directing users to a theme site for support etc. This field is equivalent to WP_Theme->get( "ThemeURI" ).', 'wp-graphql' ),
 						'resolve' => function( \WP_Theme $theme, $args, AppContext $context, ResolveInfo $info ) {
-							$theme_uri = $theme->get('ThemeURI');
+							$theme_uri = $theme->get( 'ThemeURI' );
 
 							return ! empty( $theme_uri ) ? $theme_uri : null;
 						},
@@ -120,7 +120,7 @@ private static function fields() {
 						'type' => Types::string(),
 						'description' => __( 'URI for the author/company website. This field is equivalent to WP_Theme->get( "AuthorURI" ).', 'wp-graphql' ),
 						'resolve' => function( \WP_Theme $theme, $args, AppContext $context, ResolveInfo $info ) {
-							$author_uri = $theme->get('AuthorURI');
+							$author_uri = $theme->get( 'AuthorURI' );
 
 							return ! empty( $author_uri ) ? $author_uri : null;
 						},
@@ -130,7 +130,7 @@ private static function fields() {
 						'description' => __( 'URI for the author/company website. This field is equivalent to WP_Theme->get( "Tags" ).', 'wp-graphql' ),
 					],
 					'version' => [
-						'type' => Types::string(),
+						'type' => Types::float(),
 						'description' => __( 'The current version of the theme. This field is equivalent to WP_Theme->get( "Version" ).', 'wp-graphql' ),
 					],
 				];
diff --git a/src/Type/Union/PostObjectUnionType.php b/src/Type/Union/PostObjectUnionType.php
index ec8b12209..1307f3f40 100755
--- a/src/Type/Union/PostObjectUnionType.php
+++ b/src/Type/Union/PostObjectUnionType.php
@@ -84,11 +84,4 @@ public function getPossibleTypes() {
 
 	}
 
-	/**
-	 * @return callable|null
-	 */
-	public function getResolveTypeFn() {
-		return $this->resolveTypeFn;
-	}
-
 }
diff --git a/src/Type/Union/TermObjectUnionType.php b/src/Type/Union/TermObjectUnionType.php
new file mode 100644
index 000000000..eca664006
--- /dev/null
+++ b/src/Type/Union/TermObjectUnionType.php
@@ -0,0 +1,76 @@
+ 'termObjectUnion',
+			'types' => function() {
+				return self::getPossibleTypes();
+			},
+			'resolveType' => function( $value ) {
+				return ! empty( $value->taxonomy ) ? Types::term_object( $value->taxonomy ) : null;
+			},
+		];
+
+		parent::__construct( $config );
+
+	}
+
+	/**
+	 * This defines the possible types that can be resolved by this union
+	 *
+	 * @return array|null An array of possible types that can be resolved by the union
+	 */
+	public function getPossibleTypes() {
+
+		if ( null === self::$possible_types ) {
+			self::$possible_types = [];
+		}
+
+		$allowed_taxonomies = \WPGraphQL::$allowed_taxonomies;
+		if ( ! empty( $allowed_taxonomies ) && is_array( $allowed_taxonomies ) ) {
+			foreach ( $allowed_taxonomies as $allowed_taxonomy ) {
+				if ( empty( self::$possible_types[ $allowed_taxonomy ] ) ) {
+					self::$possible_types[ $allowed_taxonomy ] = Types::term_object( $allowed_taxonomy );
+				}
+			}
+		}
+
+		/**
+		 * Filter the possible_types as it's possible some systems might set things like "parent_id" to a different
+		 * object than a post_type, and might want to be able to hook in and add a non postObject type to the possible
+		 * resolveTypes.
+		 *
+		 * @param array $possible_types  An array of possible types that can be resolved for the union
+		 * @since 0.0.6
+		 */
+		self::$possible_types = apply_filters( 'graphql_term_object_union_possible_types', self::$possible_types );
+
+		return ! empty( self::$possible_types ) ? self::$possible_types : null;
+
+	}
+
+}
diff --git a/src/Type/User/Connection/UserConnectionResolver.php b/src/Type/User/Connection/UserConnectionResolver.php
index ad45cf757..b7cffe539 100755
--- a/src/Type/User/Connection/UserConnectionResolver.php
+++ b/src/Type/User/Connection/UserConnectionResolver.php
@@ -57,17 +57,6 @@ public static function get_query_args( $source, array $args, AppContext $context
 			$query_args['count_total'] = true;
 		}
 
-		if ( true === is_object( $source ) ) {
-			switch ( true ) {
-				case $source instanceof \WP_Post:
-					$query_args['include'] = [ $source->post_author ];
-					break;
-				case $source instanceof \WP_Comment:
-					$query_args['include'] = [ $source->user_ID ];
-					break;
-			}
-		}
-
 		/**
 		 * Set the number, ensuring it doesn't exceed the amount set as the $max_query_amount
 		 */
diff --git a/src/Types.php b/src/Types.php
index 3f6bdcf24..034ab4c56 100755
--- a/src/Types.php
+++ b/src/Types.php
@@ -1,4 +1,5 @@
 assertEquals( $expected, $actual );
+
+	}
+}
diff --git a/tests/test-avatar-object-queries.php b/tests/test-avatar-object-queries.php
index 9674525db..69024d348 100644
--- a/tests/test-avatar-object-queries.php
+++ b/tests/test-avatar-object-queries.php
@@ -48,11 +48,13 @@ public function testAvatarQuery() {
 
 		/**
 		 * Create the query string to pass to the $query
+		 * Set the size to 0 to make sure that it defaults back to 96 as it has to be a positive
+		 * integer
 		 */
 		$query = "
 		query {
 			user(id: \"{$global_id}\") {
-				avatar {
+				avatar(size:0 rating:G forceDefault:true) {
 					default,
 					extraAttr,
 					forceDefault,
@@ -81,7 +83,7 @@ public function testAvatarQuery() {
 					'avatar' => [
 						'default'      => 'mm',
 						'extraAttr'    => null,
-						'forceDefault' => false,
+						'forceDefault' => true,
 						'foundAvatar'  => true,
 						'height'       => 96,
 						'rating'       => 'g',
diff --git a/tests/test-comment-connection-queries.php b/tests/test-comment-connection-queries.php
index 95af4e02d..a7be2a59d 100644
--- a/tests/test-comment-connection-queries.php
+++ b/tests/test-comment-connection-queries.php
@@ -1,4 +1,5 @@
 post_id = $this->factory->post->create();
 
-		$this->current_time     = strtotime( '- 1 day' );
-		$this->current_date     = date( 'Y-m-d H:i:s', $this->current_time );
-		$this->current_date_gmt = gmdate( 'Y-m-d H:i:s', $this->current_time );
-		$this->admin            = $this->factory->user->create( [
+		$this->current_time        = strtotime( '- 1 day' );
+		$this->current_date        = date( 'Y-m-d H:i:s', $this->current_time );
+		$this->current_date_gmt    = gmdate( 'Y-m-d H:i:s', $this->current_time );
+		$this->admin               = $this->factory->user->create( [
 			'role' => 'administrator',
 		] );
 		$this->created_comment_ids = $this->create_comments();
@@ -74,171 +74,170 @@ public function create_comments() {
 		// Create 20 comments
 		$created_comments = [];
 		for ( $i = 1; $i <= 20; $i ++ ) {
-			$created_comments[ $i ] = $this->createCommentObject( [ 'comment_content' => $i ] );
+			$date                   = date( 'Y-m-d H:i:s', strtotime( "-1 day +{$i} minutes" ) );
+			$created_comments[ $i ] = $this->createCommentObject( [
+				'comment_content' => $i,
+				'comment_date'    => $date,
+			] );
 		}
 
 		return $created_comments;
 	}
 
-	/**
-	 * This runs a comments query looking for the last 10 comments before the 10th created item from the create_comments method
-	 */
-	public function testCommentConnectionQueryWithBeforeCursor() {
-		/**
-		 * Get the array of created comment IDs
-		 */
-		$created_comments = $this->created_comment_ids;
-
-		/**
-		 * Get a cursor for the 10th comment to use in the next query
-		 */
-		$before_cursor = \GraphQLRelay\Connection\ArrayConnection::offsetToCursor( $created_comments[10] );
-
-		/**
-		 * Query 10 comments, starting at the 10th one from $created_comments
-		 */
-		$query = '
-		{
-		  comments(last:10, before: "' . $before_cursor . '") {
-		    edges {
-		      node {
-		        id
-		        content
-		      }
-		    }
-		  }
-		}
-		';
-
-		/**
-		 * Run the GraphQL query
-		 */
-		$actual = do_graphql_request( $query );
+	public function commentsQuery( $variables ) {
+		$query = 'query commentsQuery($first:Int $last:Int $after:String $before:String $where:commentArgs){
+			comments( first:$first last:$last after:$after before:$before where:$where ) {
+				pageInfo {
+					hasNextPage
+					hasPreviousPage
+					startCursor
+					endCursor
+				}
+				edges {
+					cursor
+					node {
+						id
+						commentId
+						content
+						date
+					}
+				}
+			}
+		}';
+
+		return do_graphql_request( $query, 'commentsQuery', $variables );
+	}
 
-		/**
-		 * Ensure we're getting comments back
-		 */
-		$edges = $actual['data']['comments']['edges'];
-		$this->assertNotEmpty( $edges );
+	public function testFirstComment() {
 
-		/**
-		 * Verify the node data
-		 */
-		$edge_count = 9;
-		foreach ( $edges as $edge ) {
+		$variables = [
+			'first' => 1,
+		];
 
-			// Ensure each edge has a node
-			$this->assertArrayHasKey( 'node', $edge );
+		$results        = $this->commentsQuery( $variables );
+		$comments_query = new WP_Comment_Query;
+		$comments       = $comments_query->query( [
+			'comment_status' => 'approved',
+			'number'         => 1,
+			'order'          => 'DESC',
+			'comment_status' => 'approved',
+			'orderby'        => 'comment_date',
+			'comment_parent' => 0,
+		] );
+		$first_comment  = $comments[0];
 
-			// Ensure each node that was returned, matches the Id of the created comment in the order that
-			// the comments were created
-			$this->assertEquals( $edge['node']['content'], ( string ) $edge_count );
+		$expected_cursor = \GraphQLRelay\Connection\ArrayConnection::offsetToCursor( $first_comment->comment_ID );
+		$this->assertNotEmpty( $results );
+		$this->assertEquals( 1, count( $results['data']['comments']['edges'] ) );
+		$this->assertEquals( $first_comment->comment_ID, $results['data']['comments']['edges'][0]['node']['commentId'] );
+		$this->assertEquals( $expected_cursor, $results['data']['comments']['edges'][0]['cursor'] );
+		$this->assertEquals( $expected_cursor, $results['data']['comments']['pageInfo']['startCursor'] );
+		$this->assertEquals( $expected_cursor, $results['data']['comments']['pageInfo']['endCursor'] );
 
-			$edge_count --;
-		}
 	}
 
-	/**
-	 * This runs a comments query looking for the last 10 comments after the 20th created item from the create_comments method
-	 */
-	public function testCommentConnectionQueryWithAfterCursor() {
-		$created_comments = $this->created_comment_ids;
+	public function testForwardPagination() {
 
 		/**
-		 * Get a cursor for the 10th comment to use in the next query
+		 * Create the cursor for the comment with the oldest comment_date
 		 */
-		$after_cursor = \GraphQLRelay\Connection\ArrayConnection::offsetToCursor( $created_comments[10] );
+		$first_comment_cursor = \GraphQLRelay\Connection\ArrayConnection::offsetToCursor( $this->created_comment_ids[20] );
 
 		/**
-		 * Query 10 comments, starting at the 10th one from $created_comments
+		 * Set the variables to use in the GraphQL Query
 		 */
-		$query = '
-		{
-		  comments(first:10, after: "' . $after_cursor . '") {
-		    edges {
-		      node {
-		        id
-		        content
-		      }
-		    }
-		  }
-		}
-		';
+		$variables = [
+			'first' => 1,
+			'after' => $first_comment_cursor,
+		];
 
 		/**
-		 * Run the GraphQL query
+		 * Run the GraphQL Query
 		 */
-		$actual = $actual = do_graphql_request( $query );
+		$results = $this->commentsQuery( $variables );
 
-		/**
-		 * Ensure we're getting comments back
-		 */
-		$edges = $actual['data']['comments']['edges'];
-		$this->assertNotEmpty( $edges );
+		$comments_query  = new WP_Comment_Query;
+		$comments        = $comments_query->query( [
+			'comment_status' => 'approved',
+			'number'         => 1,
+			'offset'         => 1,
+			'order'          => 'DESC',
+			'orderby'        => 'comment_date',
+			'comment_parent' => 0,
+		] );
+		$second_comment  = $comments[0];
+		$expected_cursor = \GraphQLRelay\Connection\ArrayConnection::offsetToCursor( $second_comment->comment_ID );
+		$this->assertNotEmpty( $results );
+		$this->assertEquals( 1, count( $results['data']['comments']['edges'] ) );
+		$this->assertEquals( $second_comment->comment_ID, $results['data']['comments']['edges'][0]['node']['commentId'] );
+		$this->assertEquals( $expected_cursor, $results['data']['comments']['edges'][0]['cursor'] );
+		$this->assertEquals( $expected_cursor, $results['data']['comments']['pageInfo']['startCursor'] );
+		$this->assertEquals( $expected_cursor, $results['data']['comments']['pageInfo']['endCursor'] );
 
-		/**
-		 * Verify the node data
-		 */
-		$edge_count = 11;
-		foreach ( $edges as $edge ) {
+	}
 
-			// Ensure each edge has a node
-			$this->assertArrayHasKey( 'node', $edge );
+	public function testLastComment() {
 
-			// Ensure each node that was returned, matches the Id of the created comment in the order that
-			// the comments were created
-			$this->assertEquals( $edge['node']['content'], ( string ) $edge_count );
+		$variables = [
+			'last' => 1,
+		];
 
-			$edge_count ++;
-		}
-	}
+		$results = $this->commentsQuery( $variables );
 
-	/**
-	 * Tests a comments query, ensuring the 10 most recent comments come back
-	 */
-	public function testCommentConnectionQuery() {
+		$comments_query = new WP_Comment_Query;
+		$comments       = $comments_query->query( [
+			'comment_status' => 'approved',
+			'number'         => 1,
+			'order'          => 'ASC',
+			'orderby'        => 'comment_date',
+			'comment_parent' => 0,
+		] );
+		$last_comment  = $comments[0];
 
-		$created_comments = $this->created_comment_ids;
+		$expected_cursor = \GraphQLRelay\Connection\ArrayConnection::offsetToCursor( $last_comment->comment_ID );
+		$this->assertNotEmpty( $results );
+		$this->assertEquals( 1, count( $results['data']['comments']['edges'] ) );
+		$this->assertEquals( $last_comment->comment_ID, $results['data']['comments']['edges'][0]['node']['commentId'] );
+		$this->assertEquals( $expected_cursor, $results['data']['comments']['edges'][0]['cursor'] );
+		$this->assertEquals( $expected_cursor, $results['data']['comments']['pageInfo']['startCursor'] );
+		$this->assertEquals( $expected_cursor, $results['data']['comments']['pageInfo']['endCursor'] );
 
-		/**
-		 * Create the query string to pass to the $query
-		 */
-		$query = '
-		{
-		  comments {
-		    edges {
-		      node {
-		        id
-		        content
-		      }
-		    }
-		  }
-		}
-		';
 
-		$actual = do_graphql_request( $query );
+	}
 
-		/**
-		 * Ensure we're getting comments back
-		 */
-		$edges = $actual['data']['comments']['edges'];
-		$this->assertNotEmpty( $edges );
+	public function testBackwardPagination() {
 
 		/**
-		 * Verify the node data
+		 * Create the cursor for the comment with the newest comment_date
 		 */
-		$edge_count = 20;
-		foreach ( $edges as $edge ) {
+		$last_comment_cursor = \GraphQLRelay\Connection\ArrayConnection::offsetToCursor( $this->created_comment_ids[1] );
+
+		$variables = [
+			'last'   => 1,
+			'before' => $last_comment_cursor,
+		];
 
-			// Ensure each edge has a node
-			$this->assertArrayHasKey( 'node', $edge );
+		$results = $this->commentsQuery( $variables );
 
+		$comments_query = new WP_Comment_Query;
+		$comments       = $comments_query->query( [
+			'comment_status' => 'approved',
+			'number'         => 1,
+			'offset'         => 1,
+			'order'          => 'ASC',
+			'orderby'        => 'comment_date',
+			'comment_parent' => 0,
+		] );
+		$second_to_last_comment  = $comments[0];
 
-			// Ensure each node that was returned, matches the Id of the created comment in the order that
-			// the comments were created
-			$this->assertEquals( $edge['node']['content'], ( string ) $edge_count );
+		$expected_cursor = \GraphQLRelay\Connection\ArrayConnection::offsetToCursor( $second_to_last_comment->comment_ID );
+
+		$this->assertNotEmpty( $results );
+		$this->assertEquals( 1, count( $results['data']['comments']['edges'] ) );
+		$this->assertEquals( $second_to_last_comment->comment_ID, $results['data']['comments']['edges'][0]['node']['commentId'] );
+		$this->assertEquals( $expected_cursor, $results['data']['comments']['edges'][0]['cursor'] );
+		$this->assertEquals( $expected_cursor, $results['data']['comments']['pageInfo']['startCursor'] );
+		$this->assertEquals( $expected_cursor, $results['data']['comments']['pageInfo']['endCursor'] );
 
-			$edge_count --;
-		}
 	}
 }
diff --git a/tests/test-media-item-queries.php b/tests/test-media-item-queries.php
new file mode 100644
index 000000000..ad0576005
--- /dev/null
+++ b/tests/test-media-item-queries.php
@@ -0,0 +1,315 @@
+current_time     = strtotime( '- 1 day' );
+		$this->current_date     = date( 'Y-m-d H:i:s', $this->current_time );
+		$this->current_date_gmt = gmdate( 'Y-m-d H:i:s', $this->current_time );
+		$this->admin            = $this->factory->user->create( [
+			'role' => 'administrator',
+		] );
+		$this->subscriber       = $this->factory->user->create( [
+			'role' => 'subscriber',
+		] );
+
+	}
+
+	/**
+	 * Runs after each method.
+	 *
+	 * @since 0.0.5
+	 */
+	public function tearDown() {
+		parent::tearDown();
+	}
+
+	public function createPostObject( $args ) {
+
+		/**
+		 * Set up the $defaults
+		 */
+		$defaults = [
+			'post_author'  => $this->admin,
+			'post_content' => 'Test page content',
+			'post_excerpt' => 'Test excerpt',
+			'post_status'  => 'publish',
+			'post_title'   => 'Test Title',
+			'post_type'    => 'post',
+			'post_date'    => $this->current_date,
+		];
+
+		/**
+		 * Combine the defaults with the $args that were
+		 * passed through
+		 */
+		$args = array_merge( $defaults, $args );
+
+		/**
+		 * Create the page
+		 */
+		$post_id = $this->factory->post->create( $args );
+
+		/**
+		 * Update the _edit_last and _edit_lock fields to simulate a user editing the page to
+		 * test retrieving the fields
+		 *
+		 * @since 0.0.5
+		 */
+		update_post_meta( $post_id, '_edit_lock', $this->current_time . ':' . $this->admin );
+		update_post_meta( $post_id, '_edit_last', $this->admin );
+
+		/**
+		 * Return the $id of the post_object that was created
+		 */
+		return $post_id;
+
+	}
+
+	/**
+	 * testPostQuery
+	 *
+	 * This tests creating a single post with data and retrieving said post via a GraphQL query
+	 *
+	 * @since 0.0.5
+	 */
+	public function testMediaItemQuery() {
+
+		/**
+		 * Create a post to set as the attachment's parent
+		 */
+		$post_id = $this->createPostObject( [
+			'post_type' => 'post',
+		] );
+
+		/**
+		 * Create an attachment with a post set as it's parent
+		 */
+		$attachment_id = $this->createPostObject( [
+			'post_type'   => 'attachment',
+			'post_parent' => $post_id,
+		] );
+
+		$meta_data = [
+			'width' => 300,
+			'height' => 300,
+			'file' => 'example.jpg',
+			'sizes' => [
+				'thumbnail' => [
+					'file' => 'example-thumbnail.jpg',
+					'width' => 150,
+					'height' => 150,
+					'mime-type' => 'image/jpeg',
+					'source_url' => 'example-thumbnail.jpg',
+				],
+			],
+			'image_meta' => [
+				'aperture' => 0,
+				'credit' => 'some photographer',
+				'camera' => 'some camera',
+				'caption' => 'some caption',
+				'created_timestamp' => strtotime( $this->current_date ),
+				'copyright' => 'Copyright WPGraphQL',
+				'focal_length' => 0,
+				'iso' => 0,
+				'shutter_speed' => 0,
+				'title' => 'some title',
+				'orientation' => 'some orientation',
+				'keywords' => [
+					'keyword1',
+					'keyword2',
+				],
+			],
+		];
+
+		update_post_meta( $attachment_id, '_wp_attachment_metadata', $meta_data );
+
+		/**
+		 * Create the global ID based on the post_type and the created $id
+		 */
+		$attachment_global_id = \GraphQLRelay\Relay::toGlobalId( 'attachment', $attachment_id );
+		$post_global_id = \GraphQLRelay\Relay::toGlobalId( 'post', $post_id );
+
+		/**
+		 * Create the query string to pass to the $query
+		 */
+		$query = "
+		query {
+			mediaItem(id: \"{$attachment_global_id}\") {
+				altText
+				author{
+				  id
+				}
+				caption
+				commentCount
+				commentStatus
+				comments{
+				  edges{
+				    node{
+				      id
+				    }
+				  }
+				}
+				content
+				date
+				dateGmt
+				description
+				desiredSlug
+				editLast{
+				  userId
+				}
+				editLock{
+				  editTime
+				}
+				enclosure
+				excerpt
+				guid
+				id
+				link
+				mediaDetails{
+				  file
+				  height
+				  meta{
+				    aperture
+				    credit
+				    camera
+				    caption
+				    createdTimestamp
+				    copyright
+				    focalLength
+				    iso
+				    shutterSpeed
+				    title
+				    orientation
+				    keywords
+				  }
+				  sizes{
+				    name
+				    file
+				    width
+				    height
+				    mimeType
+				    sourceUrl
+				  }
+				  width
+				}
+				mediaItemId
+				mediaType
+				menuOrder
+				mimeType
+				modified
+				modifiedGmt
+				parent{
+				  ...on post{
+				    id
+				  }
+				}
+				pingStatus
+				slug
+				sourceUrl
+				status
+				title
+				toPing
+			}
+		}";
+
+		/**
+		 * Run the GraphQL query
+		 */
+		$actual = do_graphql_request( $query );
+
+		$mediaItem = $actual['data']['mediaItem'];
+
+		$this->assertNotEmpty( $mediaItem );
+
+		$this->assertTrue( ( null === $mediaItem['altText'] || is_string( $mediaItem['altText'] ) ) );
+		$this->assertTrue( ( null === $mediaItem['author'] || is_string( $mediaItem['author']['id'] ) ) );
+		$this->assertTrue( ( null === $mediaItem['caption'] || is_string( $mediaItem['caption'] ) ) );
+		$this->assertTrue( ( null === $mediaItem['commentCount'] || is_int( $mediaItem['commentCount'] ) ) );
+		$this->assertTrue( ( null === $mediaItem['commentStatus'] || is_string( $mediaItem['commentStatus'] ) ) );
+		$this->assertTrue( ( empty( $mediaItem['comments']['edges'] ) || is_string( $mediaItem['comments']['edges'] ) ) );
+		$this->assertTrue( ( null === $mediaItem['content'] || is_string( $mediaItem['content'] ) ) );
+		$this->assertTrue( ( null === $mediaItem['date'] || is_string( $mediaItem['date'] ) ) );
+		$this->assertTrue( ( null === $mediaItem['dateGmt'] || is_string( $mediaItem['dateGmt'] ) ) );
+		$this->assertTrue( ( null === $mediaItem['description'] || is_string( $mediaItem['description'] ) ) );
+		$this->assertTrue( ( null === $mediaItem['desiredSlug'] || is_string( $mediaItem['desiredSlug'] ) ) );
+		$this->assertTrue( ( empty( $mediaItem['editLast'] ) || is_integer( $mediaItem['editLast']['userId'] ) ) );
+		$this->assertTrue( ( empty( $mediaItem['editLock'] ) || is_string( $mediaItem['editLock']['editTime'] ) ) );
+		$this->assertTrue( ( null === $mediaItem['enclosure'] || is_string( $mediaItem['enclosure'] ) ) );
+		$this->assertTrue( ( null === $mediaItem['excerpt'] || is_string( $mediaItem['excerpt'] ) ) );
+		$this->assertTrue( ( null === $mediaItem['guid'] || is_string( $mediaItem['guid'] ) ) );
+		$this->assertEquals( $attachment_global_id, $mediaItem['id'] );
+		$this->assertEquals( $attachment_id, $mediaItem['mediaItemId'] );
+		$this->assertTrue( ( null === $mediaItem['mediaType'] || is_string( $mediaItem['mediaType'] ) ) );
+		$this->assertTrue( ( null === $mediaItem['menuOrder'] || is_integer( $mediaItem['menuOrder'] ) ) );
+		$this->assertTrue( ( null === $mediaItem['mimeType'] || is_string( $mediaItem['mimeType'] ) ) );
+		$this->assertTrue( ( null === $mediaItem['modified'] || is_string( $mediaItem['modified'] ) ) );
+		$this->assertTrue( ( null === $mediaItem['modifiedGmt'] || is_string( $mediaItem['modifiedGmt'] ) ) );
+		$this->assertTrue( ( null === $mediaItem['pingStatus'] || is_string( $mediaItem['pingStatus'] ) ) );
+		$this->assertTrue( ( empty( $mediaItem['pinged'] ) || is_array( $mediaItem['pinged'] ) ) );
+		$this->assertTrue( ( null === $mediaItem['slug'] || is_string( $mediaItem['slug'] ) ) );
+		$this->assertTrue( ( null === $mediaItem['sourceUrl'] || is_string( $mediaItem['sourceUrl'] ) ) );
+		$this->assertTrue( ( null === $mediaItem['status'] || is_string( $mediaItem['status'] ) ) );
+		$this->assertTrue( ( null === $mediaItem['title'] || is_string( $mediaItem['title'] ) ) );
+		$this->assertTrue( ( empty( $mediaItem['toPing'] ) || is_array( $mediaItem['toPing'] ) ) );
+
+		$this->assertEquals(
+			[
+				'id' => $post_global_id,
+			],
+			$mediaItem['parent']
+		);
+
+		$this->assertNotEmpty( $mediaItem['mediaDetails'] );
+		$mediaDetails = $mediaItem['mediaDetails'];
+		$this->assertEquals( $meta_data['file'], $mediaDetails['file'] );
+		$this->assertEquals( $meta_data['height'], $mediaDetails['height'] );
+		$this->assertEquals( $meta_data['width'], $mediaDetails['width'] );
+
+		$this->assertNotEmpty( $mediaDetails['meta'] );
+		$meta = $mediaDetails['meta'];
+
+		$this->assertEquals( $meta_data['image_meta']['aperture'],  $meta['aperture'] );
+		$this->assertEquals( $meta_data['image_meta']['credit'],  $meta['credit'] );
+		$this->assertEquals( $meta_data['image_meta']['camera'],  $meta['camera'] );
+		$this->assertEquals( $meta_data['image_meta']['caption'],  $meta['caption'] );
+		$this->assertEquals( $meta_data['image_meta']['created_timestamp'],  $meta['createdTimestamp'] );
+		$this->assertEquals( $meta_data['image_meta']['copyright'],  $meta['copyright'] );
+		$this->assertEquals( $meta_data['image_meta']['focal_length'],  $meta['focalLength'] );
+		$this->assertEquals( $meta_data['image_meta']['iso'],  $meta['iso'] );
+		$this->assertEquals( $meta_data['image_meta']['shutter_speed'],  $meta['shutterSpeed'] );
+		$this->assertEquals( $meta_data['image_meta']['title'],  $meta['title'] );
+		$this->assertEquals( $meta_data['image_meta']['orientation'],  $meta['orientation'] );
+
+		$this->assertNotEmpty( $meta_data['image_meta']['keywords'] );
+		$keywords = $meta_data['image_meta']['keywords'];
+		$this->assertEquals( 'keyword1', $keywords[0] );
+		$this->assertEquals( 'keyword2', $keywords[1] );
+
+		$this->assertNotEmpty( $meta_data['sizes'] );
+		$sizes = $mediaDetails['sizes'];
+		$this->assertEquals( 'thumbnail', $sizes[0]['name'] );
+		$this->assertEquals( 'example-thumbnail.jpg', $sizes[0]['file'] );
+		$this->assertEquals( 150, $sizes[0]['height'] );
+		$this->assertEquals( 150, $sizes[0]['width'] );
+		$this->assertEquals( 'image/jpeg', $sizes[0]['mimeType'] );
+		$this->assertEquals( 'example-thumbnail.jpg', $sizes[0]['sourceUrl'] );
+
+	}
+}
diff --git a/tests/test-plugin-queries.php b/tests/test-plugin-queries.php
index 7002b8561..39bab783e 100755
--- a/tests/test-plugin-queries.php
+++ b/tests/test-plugin-queries.php
@@ -62,22 +62,15 @@ public function testPluginsQuery() {
 
 		$actual = do_graphql_request( $query );
 
-		$expected = [
-			'data' => [
-				'plugins' => [
-					'edges' => [
-						[
-							'node' => [
-								'id' => 'cGx1Z2luOkhlbGxvIERvbGx5',
-								'name' => 'Hello Dolly',
-							],
-						],
-					],
-				],
-			],
-		];
-
-		$this->assertEquals( $expected, $actual );
+		/**
+		 * We don't really care what the specifics are because the default plugins could change at any time
+		 * and we don't care to maintain the exact match, we just want to make sure we are
+		 * properly getting a theme back in the query
+		 */
+		$this->assertNotEmpty( $actual['data']['plugins']['edges'] );
+		$this->assertNotEmpty( $actual['data']['plugins']['edges'][0]['node']['id'] );
+		$this->assertNotEmpty( $actual['data']['plugins']['edges'][0]['node']['name'] );
+
 
 	}
 
@@ -92,22 +85,46 @@ public function testPluginQuery() {
 		  plugin(id: "cGx1Z2luOkhlbGxvIERvbGx5"){
 		    id
 		    name
+		    author
+		    authorUri
+		    description
+		    name
+		    pluginUri
+		    version
 		  }
 		}
 		';
 
 		$actual = do_graphql_request( $query );
 
-		$expected = [
-			'data' => [
-				'plugin' => [
-					'id' => 'cGx1Z2luOkhlbGxvIERvbGx5',
-					'name' => 'Hello Dolly',
-				],
-			],
-		];
+		/**
+		 * We don't really care what the specifics are because the default plugins could change at any time
+		 * and we don't care to maintain the exact match, we just want to make sure we are
+		 * properly getting a theme back in the query
+		 */
+		$this->assertNotEmpty( $actual['data']['plugin']['id'] );
+		$this->assertNotEmpty( $actual['data']['plugin']['name'] );
+
+		$plugin_id = $actual['data']['plugin']['id'];
+		$this->assertTrue( ( is_string( $plugin_id ) || null === $plugin_id ) );
+
+		$plugin_name = $actual['data']['plugin']['name'];
+		$this->assertTrue( ( is_string( $plugin_name ) || null === $plugin_name ) );
+
+		$plugin_author = $actual['data']['plugin']['author'];
+		$this->assertTrue( ( is_string( $plugin_author ) || null === $plugin_author ) );
+
+		$plugin_author_uri = $actual['data']['plugin']['authorUri'];
+		$this->assertTrue( ( is_string( $plugin_author_uri ) || null === $plugin_author_uri ) );
+
+		$plugin_description = $actual['data']['plugin']['description'];
+		$this->assertTrue( ( is_string( $plugin_description ) || null === $plugin_description ) );
+
+		$plugin_uri = $actual['data']['plugin']['pluginUri'];
+		$this->assertTrue( ( is_string( $plugin_uri ) || null === $plugin_uri ) );
 
-		$this->assertEquals( $expected, $actual );
+		$plugin_version = $actual['data']['plugin']['version'];
+		$this->assertTrue( ( is_float( $plugin_version ) || null === $plugin_version ) );
 
 	}
 
diff --git a/tests/test-post-connection-queries.php b/tests/test-post-connection-queries.php
index 133b1f9bf..8af3e826f 100644
--- a/tests/test-post-connection-queries.php
+++ b/tests/test-post-connection-queries.php
@@ -29,7 +29,7 @@ public function setUp() {
 		$this->admin            = $this->factory->user->create( [
 			'role' => 'administrator',
 		] );
-		$this->created_post_ids    = $this->create_posts();
+		$this->created_post_ids = $this->create_posts();
 
 	}
 
@@ -93,13 +93,14 @@ public function create_posts() {
 
 		// Create 20 posts
 		$created_posts = [];
-		for ( $i = 1; $i <= 20; $i ++ ) {
+		for ( $i = 1; $i <= 200; $i ++ ) {
 			// Set the date 1 minute apart for each post
 			$date                = date( 'Y-m-d H:i:s', strtotime( "-1 day +{$i} minutes" ) );
 			$created_posts[ $i ] = $this->createPostObject( [
 				'post_type'   => 'post',
 				'post_date'   => $date,
 				'post_status' => 'publish',
+				'post_title'  => $i,
 			] );
 		}
 
@@ -107,177 +108,171 @@ public function create_posts() {
 
 	}
 
-	/**
-	 * This runs a posts query looking for the last 10 posts before the 10th created item from the create_posts method
-	 */
-	public function testPostConnectionQueryWithBeforeCursor() {
+	public function postsQuery( $variables ) {
+
+		$query = 'query postsQuery($first:Int $last:Int $after:String $before:String $where:queryArgs){
+			posts( first:$first last:$last after:$after before:$before where:$where ) {
+				pageInfo {
+					hasNextPage
+					hasPreviousPage
+					startCursor
+					endCursor
+				}
+				edges {
+					cursor
+					node {
+						id
+						postId
+						title
+						date
+					}
+				}
+			}
+		}';
+
+		return do_graphql_request( $query, 'postsQuery', $variables );
 
-		/**
-		 * Get the array of created post IDs
-		 */
-		$created_posts = $this->created_post_ids;
+	}
 
-		/**
-		 * Get a cursor for the 10th post to use in the next query
-		 */
-		$before_cursor = \GraphQLRelay\Connection\ArrayConnection::offsetToCursor( $created_posts[10] );
+	public function testfirstPost() {
 
 		/**
-		 * Query 10 posts, starting at the 10th one from $created_posts
+		 * Here we're querying the first post in our dataset
 		 */
-		$query = '
-		{
-		  posts(last:10, before: "' . $before_cursor . '") {
-		    edges {
-		      node {
-		        id
-		        title
-		        postId
-		        date
-		      }
-		    }
-		  }
-		}
-		';
+		$variables = [
+			'first' => 1,
+		];
+		$results   = $this->postsQuery( $variables );
 
 		/**
-		 * Run the GraphQL query
+		 * Let's query the first post in our data set so we can test against it
 		 */
-		$actual = $actual = do_graphql_request( $query );
+		$first_post      = new WP_Query( [
+			'posts_per_page' => 1,
+		] );
+		$first_post_id   = $first_post->posts[0]->ID;
+		$expected_cursor = \GraphQLRelay\Connection\ArrayConnection::offsetToCursor( $first_post_id );
+		$this->assertNotEmpty( $results );
+		$this->assertEquals( 1, count( $results['data']['posts']['edges'] ) );
+		$this->assertEquals( $first_post_id, $results['data']['posts']['edges'][0]['node']['postId'] );
+		$this->assertEquals( $expected_cursor, $results['data']['posts']['edges'][0]['cursor'] );
+		$this->assertEquals( $expected_cursor, $results['data']['posts']['pageInfo']['startCursor'] );
+		$this->assertEquals( $expected_cursor, $results['data']['posts']['pageInfo']['endCursor'] );
+
+		$this->forwardPagination( $expected_cursor );
+
+	}
 
+	public function testLastPost() {
 		/**
-		 * Ensure we're getting posts back
+		 * Here we're trying to query the last post in our dataset
 		 */
-		$edges = $actual['data']['posts']['edges'];
-		$this->assertNotEmpty( $edges );
+		$variables = [
+			'last' => 1,
+		];
+		$results   = $this->postsQuery( $variables );
 
 		/**
-		 * Verify the node data
+		 * Let's query the last post in our data set so we can test against it
 		 */
-		$edge_count = 1;
-		foreach ( $edges as $edge ) {
+		$last_post    = new WP_Query( [
+			'posts_per_page' => 1,
+			'order'          => 'ASC',
+		] );
+		$last_post_id = $last_post->posts[0]->ID;
 
-			// Ensure each edge has a node
-			$this->assertArrayHasKey( 'node', $edge );
+		$expected_cursor = \GraphQLRelay\Connection\ArrayConnection::offsetToCursor( $last_post_id );
 
-			// Ensure each node that was returned, matches the Id of the created post in the order that
-			// the posts were created
-			$this->assertEquals( $edge['node']['postId'], $created_posts[ $edge_count ] );
+		$this->assertNotEmpty( $results );
+		$this->assertEquals( 1, count( $results['data']['posts']['edges'] ) );
+		$this->assertEquals( $last_post_id, $results['data']['posts']['edges'][0]['node']['postId'] );
+		$this->assertEquals( $expected_cursor, $results['data']['posts']['edges'][0]['cursor'] );
+		$this->assertEquals( $expected_cursor, $results['data']['posts']['pageInfo']['startCursor'] );
+		$this->assertEquals( $expected_cursor, $results['data']['posts']['pageInfo']['endCursor'] );
 
-			$edge_count ++;
-		}
+		$this->backwardPagination( $expected_cursor );
 
 	}
 
-	/**
-	 * This runs a posts query looking for the last 10 posts after the 20th created item from the create_posts method
-	 */
-	public function testPostConnectionQueryWithAfterCursor() {
-
-		$created_posts = $this->created_post_ids;
+	public function forwardPagination( $cursor ) {
 
-		/**
-		 * Get a cursor for the 10th post to use in the next query
-		 */
-		$after_cursor = \GraphQLRelay\Connection\ArrayConnection::offsetToCursor( $created_posts[10] );
-
-		/**
-		 * Query 10 posts, starting at the 10th one from $created_posts
-		 */
-		$query = '
-		{
-		  posts(first:10, after: "' . $after_cursor . '") {
-		    edges {
-		      node {
-		        id
-		        title
-		        postId
-		        date
-		      }
-		    }
-		  }
-		}
-		';
+		$variables = [
+			'first' => 1,
+			'after' => $cursor,
+		];
 
-		/**
-		 * Run the GraphQL query
-		 */
-		$actual = $actual = do_graphql_request( $query );
+		$results = $this->postsQuery( $variables );
 
-		/**
-		 * Ensure we're getting posts back
-		 */
-		$edges = $actual['data']['posts']['edges'];
-		$this->assertNotEmpty( $edges );
+		$second_post     = new WP_Query( [
+			'posts_per_page' => 1,
+			'paged'          => 2,
+		] );
+		$second_post_id  = $second_post->posts[0]->ID;
+		$expected_cursor = \GraphQLRelay\Connection\ArrayConnection::offsetToCursor( $second_post_id );
+		$this->assertNotEmpty( $results );
+		$this->assertEquals( 1, count( $results['data']['posts']['edges'] ) );
+		$this->assertEquals( $second_post_id, $results['data']['posts']['edges'][0]['node']['postId'] );
+		$this->assertEquals( $expected_cursor, $results['data']['posts']['edges'][0]['cursor'] );
+		$this->assertEquals( $expected_cursor, $results['data']['posts']['pageInfo']['startCursor'] );
+		$this->assertEquals( $expected_cursor, $results['data']['posts']['pageInfo']['endCursor'] );
+	}
 
-		/**
-		 * Verify the node data
-		 */
-		$edge_count = 20;
-		foreach ( $edges as $edge ) {
+	public function backwardPagination( $cursor ) {
 
-			// Ensure each edge has a node
-			$this->assertArrayHasKey( 'node', $edge );
+		$variables = [
+			'last'   => 1,
+			'before' => $cursor,
+		];
 
-			// Ensure each node that was returned, matches the Id of the created post in the order that
-			// the posts were created
-			$this->assertEquals( $edge['node']['postId'], $created_posts[ $edge_count ] );
+		$results = $this->postsQuery( $variables );
 
-			$edge_count --;
-		}
+		$second_to_last_post    = new WP_Query( [
+			'posts_per_page' => 1,
+			'paged'          => 2,
+			'order'          => 'ASC',
+		] );
+		$second_to_last_post_id = $second_to_last_post->posts[0]->ID;
+		$expected_cursor        = \GraphQLRelay\Connection\ArrayConnection::offsetToCursor( $second_to_last_post_id );
+		$this->assertNotEmpty( $results );
+		$this->assertEquals( 1, count( $results['data']['posts']['edges'] ) );
+		$this->assertEquals( $second_to_last_post_id, $results['data']['posts']['edges'][0]['node']['postId'] );
+		$this->assertEquals( $expected_cursor, $results['data']['posts']['edges'][0]['cursor'] );
+		$this->assertEquals( $expected_cursor, $results['data']['posts']['pageInfo']['startCursor'] );
+		$this->assertEquals( $expected_cursor, $results['data']['posts']['pageInfo']['endCursor'] );
 
 	}
 
-	/**
-	 * Tests a posts query, ensuring the 10 most recent posts come back
-	 */
-	public function testPostConnectionQuery() {
-
-		$created_posts = $this->created_post_ids;
-
-		/**
-		 * Create the query string to pass to the $query
-		 */
-		$query = '
-		{
-		  posts {
-		    edges {
-		      node {
-		        id
-		        title
-		        postId
-		        date
-		      }
-		    }
-		  }
-		}
-		';
-
-		$actual = do_graphql_request( $query );
+	public function testMaxQueryAmount() {
+		$variables = [
+			'first' => 150,
+		];
+		$results   = $this->postsQuery( $variables );
+		$this->assertNotEmpty( $results );
 
 		/**
-		 * Ensure we're getting posts back
+		 * The max that can be queried by default is 100 items
 		 */
-		$edges = $actual['data']['posts']['edges'];
-		$this->assertNotEmpty( $edges );
+		$this->assertCount( 100, $results['data']['posts']['edges'] );
+		$this->assertTrue( $results['data']['posts']['pageInfo']['hasNextPage'] );
 
 		/**
-		 * Verify the node data
+		 * Test the filter to make sure it's capping the results properly
 		 */
-		$edge_count = 11;
-		foreach ( $edges as $edge ) {
-
-			// Ensure each edge has a node
-			$this->assertArrayHasKey( 'node', $edge );
+		add_filter( 'graphql_connection_max_query_amount', function() {
+			return 20;
+		} );
 
+		$variables = [
+			'first' => 150,
+		];
+		$results   = $this->postsQuery( $variables );
 
-			// Ensure each node that was returned, matches the Id of the created post in the order that
-			// the posts were created
-			$this->assertEquals( $edge['node']['postId'], $created_posts[ $edge_count ] );
-
-			$edge_count ++;
-		}
+		add_filter( 'graphql_connection_max_query_amount', function() {
+			return 100;
+		} );
 
+		$this->assertCount( 20, $results['data']['posts']['edges'] );
+		$this->assertTrue( $results['data']['posts']['pageInfo']['hasNextPage'] );
 	}
 
-}
\ No newline at end of file
+}
diff --git a/tests/test-post-object-mutations.php b/tests/test-post-object-mutations.php
index c725679f6..d73ae83c6 100644
--- a/tests/test-post-object-mutations.php
+++ b/tests/test-post-object-mutations.php
@@ -34,6 +34,10 @@ public function setUp() {
 			'role' => 'author',
 		] );
 
+		$this->author = $this->factory->user->create( [
+			'role' => 'author',
+		] );
+
 		$this->admin = $this->factory->user->create( [
 			'role' => 'administrator',
 		] );
diff --git a/tests/test-post-object-queries.php b/tests/test-post-object-queries.php
index b8324ce97..6cf84b666 100755
--- a/tests/test-post-object-queries.php
+++ b/tests/test-post-object-queries.php
@@ -94,6 +94,14 @@ public function testPostQuery() {
 			'post_type' => 'post',
 		] );
 
+		/**
+		 * Create a featured image and attach it to the post
+		 */
+		$featured_image_id = $this->createPostObject( [
+			'post_type' => 'attachment',
+		] );
+		update_post_meta( $post_id, '_thumbnail_id', $featured_image_id );
+
 		/**
 		 * Create the global ID based on the post_type and the created $id
 		 */
@@ -126,12 +134,20 @@ public function testPostQuery() {
 				}
 				enclosure
 				excerpt
+				status
 				link
 				menuOrder
 				postId
 				slug
 				toPing
+				pinged
+				modified
+				modifiedGmt
 				title
+				guid
+				featuredImage{
+					mediaItemId
+				}
 			}
 		}";
 
@@ -167,12 +183,20 @@ public function testPostQuery() {
 					],
 					'enclosure' => null,
 					'excerpt' => apply_filters( 'the_excerpt', apply_filters( 'get_the_excerpt', 'Test excerpt' ) ),
+					'status' => 'publish',
 					'link' => get_permalink( $post_id ),
 					'menuOrder' => null,
 					'postId' => $post_id,
 					'slug' => 'test-title',
 					'toPing' => null,
+					'pinged' => null,
+					'modified' => get_post( $post_id )->post_modified,
+					'modifiedGmt' => get_post( $post_id )->post_modified_gmt,
 					'title' => 'Test Title',
+					'guid' => get_post( $post_id )->guid,
+					'featuredImage' => [
+						'mediaItemId' => $featured_image_id,
+					],
 				],
 			],
 		];
@@ -346,10 +370,11 @@ public function testPostQueryWithTags() {
 
 		// Create a comment and assign it to post.
 		$tag_id = $this->factory->tag->create( [
-			'name' => 'A tag',
+			'name' => 'Test Tag',
 		] );
 
-		wp_set_object_terms( $post_id, $tag_id, 'post_tag' );
+		wp_delete_object_term_relationships( $post_id, [ 'post_tag', 'category' ] );
+		wp_set_object_terms( $post_id, $tag_id, 'post_tag', true );
 
 		/**
 		 * Create the global ID based on the post_type and the created $id
@@ -371,6 +396,13 @@ public function testPostQueryWithTags() {
 						}
 					}
 				}
+				tagNames:termNames(taxonomy:TAG)
+				terms{
+				  ...on tag{
+				    name
+				  }
+				}
+				termNames
 			}
 		}";
 
@@ -391,11 +423,18 @@ public function testPostQueryWithTags() {
 							[
 								'node' => [
 									'tagId' => $tag_id,
-									'name' => 'A tag',
+									'name' => 'Test Tag',
 								],
 							],
 						],
 					],
+					'tagNames' => [ 'Test Tag' ],
+					'terms' => [
+						[
+							'name' => 'Test Tag',
+						],
+					],
+					'termNames' => [ 'Test Tag' ],
 				],
 			],
 		];
diff --git a/tests/test-post-type-object-queries.php b/tests/test-post-type-object-queries.php
index 1ba185a56..2aa2431cc 100644
--- a/tests/test-post-type-object-queries.php
+++ b/tests/test-post-type-object-queries.php
@@ -58,13 +58,6 @@ public function testPostTypeQueryForPosts() {
 			posts {
 				postTypeInfo {
 					canExport
-					categories {
-						edges {
-							node {
-								name
-							}
-						}
-					}
 					connectedTaxonomies {
 						name
 					}
@@ -100,6 +93,7 @@ public function testPostTypeQueryForPosts() {
 						setFeaturedImage
 						removeFeaturedImage
 						useFeaturedImage
+						menuName
 						filterItemsList
 						itemsListNavigation
 						itemsList
@@ -107,13 +101,6 @@ public function testPostTypeQueryForPosts() {
 					menuIcon
 					menuPosition
 					name
-					tags {
-						edges {
-							node {
-								name
-							}
-						}
-					}
 					public
 					publiclyQueryable
 					restBase
@@ -141,12 +128,13 @@ public function testPostTypeQueryForPosts() {
 				'posts' => [
 					'postTypeInfo' => [
 						'canExport' => true,
-						'categories' => [
-							'edges' => [],
-						],
 						'connectedTaxonomies' => [
-							[ 'name' => 'category' ],
-							[ 'name' => 'post_tag' ],
+							[
+								'name' => 'category'
+							],
+							[
+								'name' => 'post_tag'
+							],
 						],
 						'connectedTaxonomyNames' => [ 'category', 'post_tag' ],
 						'deleteWithUser' => true,
@@ -154,7 +142,7 @@ public function testPostTypeQueryForPosts() {
 						'excludeFromSearch' => false,
 						'graphqlPluralName' => 'posts',
 						'graphqlSingleName' => 'post',
-						'hasArchive' => null,
+						'hasArchive' => false,
 						'hierarchical' => false,
 						'id' => $global_id,
 						'label' => 'Posts',
@@ -180,6 +168,7 @@ public function testPostTypeQueryForPosts() {
 							'setFeaturedImage' => 'Set featured image',
 							'removeFeaturedImage' => 'Remove featured image',
 							'useFeaturedImage' => null,
+							'menuName' => 'Posts',
 							'filterItemsList' => 'Filter posts list',
 							'itemsListNavigation' => 'Posts list navigation',
 							'itemsList' => 'Posts list',
@@ -187,15 +176,12 @@ public function testPostTypeQueryForPosts() {
 						'menuIcon' => null,
 						'menuPosition' => 5,
 						'name' => 'post',
-						'tags' => [
-							'edges' => [],
-						],
 						'public' => true,
 						'publiclyQueryable' => true,
 						'restBase' => 'posts',
 						'restControllerClass' => 'WP_REST_Posts_Controller',
-						'showInAdminBar' => true,
-						'showInGraphql' => 'true',
+						'showInAdminBar' => false,
+						'showInGraphql' => true,
 						'showInMenu' => true,
 						'showInNavMenus' => true,
 						'showInRest' => true,
@@ -208,94 +194,6 @@ public function testPostTypeQueryForPosts() {
 		$this->assertEquals( $expected, $actual );
 	}
 
-	/**
-	 * testPostTypeQueryForPostConnections
-	 *
-	 * This tests connections for the post post type.
-	 *
-	 * @since 0.0.5
-	 */
-	public function testPostTypeQueryForPostConnections() {
-
-		/**
-		 * Create a post
-		 */
-		$post_id = $this->factory->post->create();
-
-		// Create a comment and assign it to postType.
-		$tag_id = $this->factory->tag->create( [ 'name' => 'A tag' ] );
-		$category_id = $this->factory->category->create( [ 'name' => 'A category' ] );
-
-		wp_set_object_terms( $post_id, $tag_id, 'post_tag' );
-		wp_set_object_terms( $post_id, $category_id, 'category' );
-
-		/**
-		 * Create the query string to pass to the $query
-		 */
-		$query = "
-		query {
-			posts {
-				postTypeInfo {
-					tags {
-						edges {
-							node {
-								tagId
-								name
-							}
-						}
-					}
-					categories {
-						edges {
-							node {
-								categoryId
-								name
-							}
-						}
-					}
-				}
-			}
-		}";
-
-		/**
-		 * Run the GraphQL query
-		 */
-		$actual = do_graphql_request( $query );
-
-		/**
-		 * Establish the expectation for the output of the query
-		 */
-		$expected = [
-			'data' => [
-				'posts' => [
-					'postTypeInfo' => [
-						'tags' => [
-							'edges' => [
-								[
-									'node' => [
-										'tagId' => $tag_id,
-										'name' => 'A tag',
-									],
-								],
-							],
-						],
-						'categories' => [
-							'edges' => [
-								[
-									'node' => [
-										'categoryId' => $category_id,
-										'name' => 'A category',
-									],
-								],
-							],
-						],
-					],
-				],
-			],
-		];
-
-		$this->assertEquals( $expected, $actual );
-	}
-
 	/**
 	 * testPostTypeQueryForPages
 	 *
@@ -317,13 +215,6 @@ public function testPostTypeQueryForPages() {
 			pages {
 				postTypeInfo {
 					canExport
-					categories {
-						edges {
-							node {
-								name
-							}
-						}
-					}
 					connectedTaxonomies {
 						name
 					}
@@ -340,13 +231,6 @@ public function testPostTypeQueryForPages() {
 					menuIcon
 					menuPosition
 					name
-					tags {
-						edges {
-							node {
-								name
-							}
-						}
-					}
 					public
 					publiclyQueryable
 					restBase
@@ -374,9 +258,6 @@ public function testPostTypeQueryForPages() {
 				'pages' => [
 					'postTypeInfo' => [
 						'canExport' => true,
-						'categories' => [
-							'edges' => [],
-						],
 						'connectedTaxonomies' => null,
 						'connectedTaxonomyNames' => null,
 						'deleteWithUser' => true,
@@ -384,22 +265,19 @@ public function testPostTypeQueryForPages() {
 						'excludeFromSearch' => false,
 						'graphqlPluralName' => 'pages',
 						'graphqlSingleName' => 'page',
-						'hasArchive' => null,
+						'hasArchive' => false,
 						'hierarchical' => true,
 						'id' => $global_id,
 						'label' => 'Pages',
 						'menuIcon' => null,
 						'menuPosition' => 20,
 						'name' => 'page',
-						'tags' => [
-							'edges' => [],
-						],
 						'public' => true,
-						'publiclyQueryable' => null,
+						'publiclyQueryable' => false,
 						'restBase' => 'pages',
 						'restControllerClass' => 'WP_REST_Posts_Controller',
-						'showInAdminBar' => true,
-						'showInGraphql' => 'true',
+						'showInAdminBar' => false,
+						'showInGraphql' => true,
 						'showInMenu' => true,
 						'showInNavMenus' => true,
 						'showInRest' => true,
@@ -412,80 +290,6 @@ public function testPostTypeQueryForPages() {
 		$this->assertEquals( $expected, $actual );
 	}
 
-	/**
-	 * testPostTypeQueryForPageConnections
-	 *
-	 * This tests post type page connections.
-	 *
-	 * @since 0.0.5
-	 */
-	public function testPostTypeQueryForPageConnections() {
-
-		/**
-		 * Create a post
-		 */
-		$post_id = $this->factory->post->create( [ 'post_type' => 'page' ] );
-
-		// Create a comment and assign it to postType.
-		$tag_id = $this->factory->tag->create( [ 'name' => 'A tag' ] );
-		$category_id = $this->factory->category->create( [ 'name' => 'A category' ] );
-
-		wp_set_object_terms( $post_id, $tag_id, 'post_tag' );
-		wp_set_object_terms( $post_id, $category_id, 'category' );
-
-		/**
-		 * Create the query string to pass to the $query
-		 */
-		$query = "
-		query {
-			pages {
-				postTypeInfo {
-					tags {
-						edges {
-							node {
-								tagId
-								name
-							}
-						}
-					}
-					categories {
-						edges {
-							node {
-								categoryId
-								name
-							}
-						}
-					}
-				}
-			}
-		}";
-
-		/**
-		 * Run the GraphQL query
-		 */
-		$actual = do_graphql_request( $query );
-
-		/**
-		 * Establish the expectation for the output of the query
-		 */
-		$expected = [
-			'data' => [
-				'pages' => [
-					'postTypeInfo' => [
-						'tags' => [
-							'edges' => [],
-						],
-						'categories' => [
-							'edges' => [],
-						],
-					],
-				],
-			],
-		];
-
-		$this->assertEquals( $expected, $actual );
-	}
-
 	/**
 	 * testPostTypeQueryForMedia
 	 *
@@ -507,13 +311,6 @@ public function testPostTypeQueryForMedia() {
 			mediaItems {
 				postTypeInfo {
 					canExport
-					categories {
-						edges {
-							node {
-								name
-							}
-						}
-					}
 					connectedTaxonomies {
 						name
 					}
@@ -530,13 +327,6 @@ public function testPostTypeQueryForMedia() {
 					menuIcon
 					menuPosition
 					name
-					tags {
-						edges {
-							node {
-								name
-							}
-						}
-					}
 					public
 					publiclyQueryable
 					restBase
@@ -564,9 +354,6 @@ public function testPostTypeQueryForMedia() {
 				'mediaItems' => [
 					'postTypeInfo' => [
 						'canExport' => true,
-						'categories' => [
-							'edges' => [],
-						],
 						'connectedTaxonomies' => null,
 						'connectedTaxonomyNames' => null,
 						'deleteWithUser' => true,
@@ -574,22 +361,19 @@ public function testPostTypeQueryForMedia() {
 						'excludeFromSearch' => false,
 						'graphqlPluralName' => 'mediaItems',
 						'graphqlSingleName' => 'mediaItem',
-						'hasArchive' => null,
+						'hasArchive' => false,
 						'hierarchical' => false,
 						'id' => $global_id,
 						'label' => 'Media',
 						'menuIcon' => null,
 						'menuPosition' => null,
 						'name' => 'attachment',
-						'tags' => [
-							'edges' => [],
-						],
 						'public' => true,
 						'publiclyQueryable' => true,
 						'restBase' => 'media',
 						'restControllerClass' => 'WP_REST_Attachments_Controller',
-						'showInAdminBar' => true,
-						'showInGraphql' => 'true',
+						'showInAdminBar' => false,
+						'showInGraphql' => true,
 						'showInMenu' => true,
 						'showInNavMenus' => null,
 						'showInRest' => true,
@@ -601,78 +385,4 @@ public function testPostTypeQueryForMedia() {
 
 		$this->assertEquals( $expected, $actual );
 	}
-
-	/**
-	 * testPostTypeQueryForMediaConnections
-	 *
-	 * This tests connections for the media post type ( attachments ).
-	 *
-	 * @since 0.0.5
-	 */
-	public function testPostTypeQueryForMediaConnections() {
-
-		/**
-		 * Create a post
-		 */
-		$post_id = $this->factory->post->create( [ 'post_type' => 'page' ] );
-
-		// Create a comment and assign it to postType.
-		$tag_id = $this->factory->tag->create( [ 'name' => 'A tag' ] );
-		$category_id = $this->factory->category->create( [ 'name' => 'A category' ] );
-
-		wp_set_object_terms( $post_id, $tag_id, 'post_tag' );
-		wp_set_object_terms( $post_id, $category_id, 'category' );
-
-		/**
-		 * Create the query string to pass to the $query
-		 */
-		$query = "
-		query {
-			pages {
-				postTypeInfo {
-					tags {
-						edges {
-							node {
-								tagId
-								name
-							}
-						}
-					}
-					categories {
-						edges {
-							node {
-								categoryId
-								name
-							}
-						}
-					}
-				}
-			}
-		}";
-
-		/**
-		 * Run the GraphQL query
-		 */
-		$actual = do_graphql_request( $query );
-
-		/**
-		 * Establish the expectation for the output of the query
-		 */
-		$expected = [
-			'data' => [
-				'pages' => [
-					'postTypeInfo' => [
-						'tags' => [
-							'edges' => [],
-						],
-						'categories' => [
-							'edges' => [],
-						],
-					],
-				],
-			],
-		];
-
-		$this->assertEquals( $expected, $actual );
-	}
 }
diff --git a/tests/test-router.php b/tests/test-router.php
index f31f8c46d..54c65a9bc 100644
--- a/tests/test-router.php
+++ b/tests/test-router.php
@@ -33,18 +33,78 @@ public function testAddQueryVar() {
 	}
 
 	public function testGetRawData() {
-
 		$router = new \WPGraphQL\Router();
-
 		global $HTTP_RAW_POST_DATA;
-		if ( ! isset( $HTTP_RAW_POST_DATA ) ) {
-			$HTTP_RAW_POST_DATA = file_get_contents( 'php://input' );
-		}
+		$actual = $router->get_raw_data();
+		$this->assertEquals( $actual, $HTTP_RAW_POST_DATA );
+	}
 
+	public function testGetRawDataEmptyGlobal() {
+		$router = new \WPGraphQL\Router();
+		global $HTTP_RAW_POST_DATA;
+		$HTTP_RAW_POST_DATA = null;
 		$actual = $router->get_raw_data();
 		$this->assertEquals( $actual, $HTTP_RAW_POST_DATA );
 	}
 
+	/**
+	 * Test the "send_header" method in the Router class
+	 * @see: https://github.com/sebastianbergmann/phpunit/issues/720
+	 * @runInSeparateProcess
+	 */
+	public function testSendHeader() {
+		$router = new \WPGraphQL\Router();
+		$router::send_header( 'some_key', 'some_value' );
+		if ( function_exists( 'xdebug_get_headers' ) ) {
+			$this->assertContains( 'some_key: some_value', xdebug_get_headers() );
+		}
+	}
+
+	public function testAddRewriteRule() {
+
+		global $wp_rewrite;
+		\WPGraphQL\Router::add_rewrite_rule();
+		flush_rewrite_rules();
+
+		$this->assertContains( 'index.php?' . \WPGraphQL\Router::$route . '=true', $wp_rewrite->extra_rules_top );
+
+	}
+
+	/**
+	 * @runInSeparateProcess
+	 */
+	public function testSetHeadersNoCache() {
+
+		$router = new \WPGraphQL\Router();
+		$router::set_headers( '200' );
+
+		$headers = xdebug_get_headers();
+
+		$this->assertContains( 'Access-Control-Allow-Origin: *', $headers );
+		$this->assertContains( 'Content-Type: application/json ; charset=' . get_option( 'blog_charset' ), $headers );
+		$this->assertContains( 'X-Robots-Tag: noindex', $headers );
+		$this->assertContains( 'X-Content-Type-Options: nosniff', $headers );
+		$this->assertContains( 'Access-Control-Allow-Headers: Authorization, Content-Type', $headers );
+		$this->assertContains( 'X-hacker: If you\'re reading this, you should visit github.com/wp-graphql and contribute!', $headers );
+
+	}
+
+	/**
+	 * @runInSeparateProcess
+	 */
+	public function testSetHeadersWithCache() {
+
+		add_filter( 'graphql_send_nocache_headers', function() {
+			return true;
+		} );
+
+		$router = new \WPGraphQL\Router();
+		$router::set_headers( '200' );
+		$headers = xdebug_get_headers();
+		$this->assertContains( 'Cache-Control: no-cache, must-revalidate, max-age=0', $headers );
+
+	}
+
 	/**
 	 * This tests the WPGraphQL Router resolving HTTP requests.
 	 */
@@ -62,7 +122,59 @@ public function testResolveRequest() {
 		 * Filter the request data
 		 */
 		add_filter( 'graphql_request_data', function( $data ) {
-			$data['query'] = 'query{ posts{ edges{ node{ id } } } }';
+			$data['query'] = 'query getPosts($first:Int){ posts(first:$first){ edges{ node{ id } } } }';
+			$data['variables'] = [ 'first' => 1 ];
+			$data['operationName'] = 'getPosts';
+			return $data;
+		} );
+
+		/**
+		 * Set the query var to "graphql" so we can mock like we're visiting the endpoint via
+		 */
+		set_query_var( 'graphql', true );
+		$GLOBALS['wp']->query_vars['graphql'] = true;
+
+		/**
+		 * Instantiate the router
+		 */
+		$router = new \WPGraphQL\Router();
+
+		/**
+		 * Process the request using our filtered data
+		 */
+		$router::resolve_http_request();
+
+		/**
+		 * Make sure the constant gets defined when it's a GraphQL Request
+		 */
+		$this->assertTrue( defined( 'GRAPHQL_HTTP_REQUEST' ) );
+		$this->assertEquals( true, GRAPHQL_HTTP_REQUEST );
+
+		/**
+		 * Make sure the actions we expect to be firing are firing
+		 */
+		$this->assertNotFalse( did_action( 'graphql_process_http_request' ) );
+		$this->assertNotFalse( did_action( 'graphql_process_http_request_response' ) );
+
+	}
+
+	public function testResolveHttpRequestWithJsonVariables() {
+
+		/**
+		 * Create a test a query
+		 */
+		$this->factory->post->create([
+			'post_title' => 'test',
+			'post_status' => 'publish',
+		]);
+
+		/**
+		 * Filter the request data
+		 */
+		add_filter( 'graphql_request_data', function( $data ) {
+			$data['query'] = 'query getPosts($first:Int){ posts(first:$first){ edges{ node{ id } } } }';
+			$data['variables'] = wp_json_encode( [ 'first' => 1 ] );
+			$data['operationName'] = 'getPosts';
 			return $data;
 		} );
 
@@ -96,4 +208,58 @@ public function testResolveRequest() {
 
 	}
 
+	/**
+	 * This tests the resolve_http_request method for a route that's not the
+	 * /graphql endpoint to make sure that graphql isn't improperly initiated
+	 * when it's not supposed to be.
+	 */
+	public function testResolveHttpRequestWrongQueryVars() {
+
+		set_query_var( 'graphql', false );
+		$GLOBALS['wp']->query_vars['graphql'] = false;
+
+		/**
+		 * Instantiate the router
+		 */
+		$router = new \WPGraphQL\Router();
+
+		/**
+		 * Process the request using our filtered data
+		 */
+		$this->assertNull( $router::resolve_http_request() );
+
+	}
+
+	/**
+	 */
+	public function testResolveRequestWithNoData() {
+
+		/**
+		 * Filter the request data
+		 */
+		add_filter( 'graphql_request_data', function( $data ) {
+			return [];
+		} );
+
+		/**
+		 * Set the query var to "graphql" so we can mock like we're visiting the endpoint via
+		 */
+		set_query_var( 'graphql', true );
+		$GLOBALS['wp']->query_vars['graphql'] = true;
+
+		/**
+		 * Instantiate the router
+		 */
+		$router = new \WPGraphQL\Router();
+
+		/**
+		 * Process the request using our filtered data
+		 */
+		$response = $router::resolve_http_request();
+
+		$this->assertTrue( defined( 'GRAPHQL_HTTP_REQUEST' ) );
+		$this->assertEquals( true, GRAPHQL_HTTP_REQUEST );
+
+	}
+
 }
diff --git a/tests/test-taxonomy-enum.php b/tests/test-taxonomy-enum.php
new file mode 100644
index 000000000..e69de29bb
diff --git a/tests/test-taxonomy-object-queries.php b/tests/test-taxonomy-object-queries.php
index d696c8860..85107dc6b 100644
--- a/tests/test-taxonomy-object-queries.php
+++ b/tests/test-taxonomy-object-queries.php
@@ -60,13 +60,6 @@ public function testTaxonomyQueryForCategories() {
 					id
 					label
 					name
-					posts {
-						edges {
-							node {
-								id
-							}
-						}
-					}
 					public
 					restBase
 					restControllerClass
@@ -105,13 +98,12 @@ public function testTaxonomyQueryForCategories() {
 						'id' => $global_id,
 						'label' => 'Categories',
 						'name' => 'category',
-						'posts' => [ 'edges' => [] ],
 						'public' => true,
 						'restBase' => 'categories',
 						'restControllerClass' => 'WP_REST_Terms_Controller',
 						'showCloud' => true,
 						'showInAdminColumn' => true,
-						'showInGraphql' => 'true',
+						'showInGraphql' => true,
 						'showInMenu' => true,
 						'showInNavMenus' => true,
 						'showInQuickEdit' => true,
@@ -151,13 +143,6 @@ public function testTaxonomyQueryForTags() {
 					id
 					label
 					name
-					posts {
-						edges {
-							node {
-								id
-							}
-						}
-					}
 					public
 					restBase
 					restControllerClass
@@ -196,13 +181,12 @@ public function testTaxonomyQueryForTags() {
 						'id' => $global_id,
 						'label' => 'Tags',
 						'name' => 'post_tag',
-						'posts' => [ 'edges' => [] ],
 						'public' => true,
 						'restBase' => 'tags',
 						'restControllerClass' => 'WP_REST_Terms_Controller',
 						'showCloud' => true,
 						'showInAdminColumn' => true,
-						'showInGraphql' => 'true',
+						'showInGraphql' => true,
 						'showInMenu' => true,
 						'showInNavMenus' => true,
 						'showInQuickEdit' => true,
@@ -242,13 +226,6 @@ public function testTaxonomyQueryCategoryConnections() {
 			categories {
 				taxonomyInfo {
 					name
-					posts {
-						edges {
-							node {
-								postId
-							}
-						}
-					}
 				}
 			}
 		}";
@@ -268,13 +245,6 @@ public function testTaxonomyQueryCategoryConnections() {
 				'categories' => [
 					'taxonomyInfo' => [
 						'name' => 'category',
-						'posts' => [
-							'edges' => [
-								[
-									'node' => [ 'postId' => $post_id ],
-								],
-							],
-						],
 					],
 				],
 			],
@@ -307,13 +277,6 @@ public function testTaxonomyQueryTagsConnections() {
 			tags {
 				taxonomyInfo {
 					name
-					posts {
-						edges {
-							node {
-								postId
-							}
-						}
-					}
 				}
 			}
 		}";
@@ -333,13 +296,6 @@ public function testTaxonomyQueryTagsConnections() {
 				'tags' => [
 					'taxonomyInfo' => [
 						'name' => 'post_tag',
-						'posts' => [
-							'edges' => [
-								[
-									'node' => [ 'postId' => $post_id ],
-								],
-							],
-						],
 					],
 				],
 			],
diff --git a/tests/test-term-object-connection-queries.php b/tests/test-term-object-connection-queries.php
new file mode 100644
index 000000000..bd41af53b
--- /dev/null
+++ b/tests/test-term-object-connection-queries.php
@@ -0,0 +1,283 @@
+current_time     = strtotime( '- 1 day' );
+		$this->current_date     = date( 'Y-m-d H:i:s', $this->current_time );
+		$this->current_date_gmt = gmdate( 'Y-m-d H:i:s', $this->current_time );
+		$this->admin            = $this->factory->user->create( [
+			'role' => 'administrator',
+		] );
+		$this->created_term_ids = $this->create_terms();
+	}
+
+	/**
+	 * Runs after each method.
+	 *
+	 * @since 0.0.5
+	 */
+	public function tearDown() {
+		parent::tearDown();
+	}
+
+	public function createTermObject( $args ) {
+
+		/**
+		 * Set up the $defaults
+		 */
+		$defaults = [
+			'taxonomy' => 'category',
+			'description' => 'just a description',
+		];
+
+		/**
+		 * Combine the defaults with the $args that were
+		 * passed through
+		 */
+		$args = array_merge( $defaults, $args );
+
+		/**
+		 * Create the page
+		 */
+		$term_id = $this->factory->term->create( $args );
+
+		/**
+		 * Return the $id of the post_object that was created
+		 */
+		return $term_id;
+
+	}
+
+	/**
+	 * Creates several posts (with different timestamps) for use in cursor query tests
+	 *
+	 * @return array
+	 */
+	public function create_terms() {
+
+		// Create 20 posts
+		$created_terms = [];
+		for ( $i = 1; $i <= 20; $i ++ ) {
+			$term_id = $this->createTermObject( [
+				'taxonomy'   => 'category',
+				'description' => $i,
+				'name'  => $i,
+			] );
+			$created_terms[ $i ] = $term_id;
+		}
+		return $created_terms;
+
+	}
+
+	public function categoriesQuery( $variables ) {
+
+		$query = 'query categoriesQuery($first:Int $last:Int $after:String $before:String $where:termArgs){
+			categories( first:$first last:$last after:$after before:$before where:$where ) {
+				pageInfo {
+					hasNextPage
+					hasPreviousPage
+					startCursor
+					endCursor
+				}
+				edges {
+					cursor
+					node {
+						id
+                        categoryId
+				        name
+				        description
+				        slug
+					}
+				}
+			}
+		}';
+
+		return do_graphql_request( $query, 'categoriesQuery', $variables );
+
+	}
+
+	public function testfirstCategory() {
+		/**
+		 * Here we're querying the first category in our dataset
+		 */
+		$variables = [
+			'first' => 1,
+		];
+		$results   = $this->categoriesQuery( $variables );
+
+		/**
+		 * Let's query the first post in our data set so we can test against it
+		 */
+		$query = new WP_Term_Query([
+			'taxonomy' => 'category',
+			'number' => 1,
+			'parent' => 0,
+			'orderby' => 'name',
+			'order' => 'ASC',
+			'hide_empty' => false,
+		]);
+		$terms = $query->get_terms();
+
+		$first_term_id   = $terms[0]->term_id;
+		$expected_cursor = \GraphQLRelay\Connection\ArrayConnection::offsetToCursor( $first_term_id );
+		$this->assertNotEmpty( $results );
+		$this->assertEquals( 1, count( $results['data']['categories']['edges'] ) );
+		$this->assertEquals( $first_term_id, $results['data']['categories']['edges'][0]['node']['categoryId'] );
+		$this->assertEquals( $expected_cursor, $results['data']['categories']['edges'][0]['cursor'] );
+		$this->assertEquals( $expected_cursor, $results['data']['categories']['pageInfo']['startCursor'] );
+		$this->assertEquals( $expected_cursor, $results['data']['categories']['pageInfo']['endCursor'] );
+
+		$this->forwardPagination( $expected_cursor );
+
+	}
+
+	public function forwardPagination( $cursor ) {
+
+		$variables = [
+			'first' => 1,
+			'after' => $cursor,
+		];
+
+		$results = $this->categoriesQuery( $variables );
+
+		$offset = 1;
+		$query = new WP_Term_Query([
+			'taxonomy' => 'category',
+			'number' => 1,
+			'offset' => $offset,
+			'parent' => 0,
+			'orderby' => 'name',
+			'order' => 'ASC',
+			'hide_empty' => false,
+		]);
+		$terms = $query->get_terms();
+
+		$second_term_id   = $terms[ $offset ]->term_id;
+		$expected_cursor = \GraphQLRelay\Connection\ArrayConnection::offsetToCursor( $second_term_id );
+		$this->assertNotEmpty( $results );
+		$this->assertEquals( 1, count( $results['data']['categories']['edges'] ) );
+		$this->assertEquals( $second_term_id, $results['data']['categories']['edges'][0]['node']['categoryId'] );
+		$this->assertEquals( $expected_cursor, $results['data']['categories']['edges'][0]['cursor'] );
+		$this->assertEquals( $expected_cursor, $results['data']['categories']['pageInfo']['startCursor'] );
+		$this->assertEquals( $expected_cursor, $results['data']['categories']['pageInfo']['endCursor'] );
+	}
+
+	public function testLastPost() {
+		/**
+		 * Here we're trying to query the last post in our dataset
+		 */
+		$variables = [
+			'last' => 1,
+		];
+		$results   = $this->categoriesQuery( $variables );
+
+		/**
+		 * Let's query the last post in our data set so we can test against it
+		 */
+		$query = new WP_Term_Query([
+			'taxonomy' => 'category',
+			'number' => 1,
+			'parent' => 0,
+			'orderby' => 'name',
+			'order' => 'DESC',
+			'hide_empty' => false,
+		]);
+		$terms = $query->get_terms();
+
+		$last_term_id   = $terms[0]->term_id;
+		$expected_cursor = \GraphQLRelay\Connection\ArrayConnection::offsetToCursor( $last_term_id );
+		$this->assertNotEmpty( $results );
+		$this->assertEquals( 1, count( $results['data']['categories']['edges'] ) );
+		$this->assertEquals( $last_term_id, $results['data']['categories']['edges'][0]['node']['categoryId'] );
+		$this->assertEquals( $expected_cursor, $results['data']['categories']['edges'][0]['cursor'] );
+		$this->assertEquals( $expected_cursor, $results['data']['categories']['pageInfo']['startCursor'] );
+		$this->assertEquals( $expected_cursor, $results['data']['categories']['pageInfo']['endCursor'] );
+
+		$this->backwardPagination( $expected_cursor );
+
+	}
+
+	public function backwardPagination( $cursor ) {
+
+		$variables = [
+			'last'   => 1,
+			'before' => $cursor,
+		];
+
+		$results = $this->categoriesQuery( $variables );
+
+		$offset = 1;
+		$query = new WP_Term_Query([
+			'taxonomy' => 'category',
+			'number' => 1,
+			'parent' => 0,
+			'offset' => $offset,
+			'orderby' => 'name',
+			'order' => 'DESC',
+			'hide_empty' => false,
+		]);
+		$terms = $query->get_terms();
+
+		$second_last_term_id   = $terms[ $offset ]->term_id;
+		$expected_cursor = \GraphQLRelay\Connection\ArrayConnection::offsetToCursor( $second_last_term_id );
+		$this->assertNotEmpty( $results );
+		$this->assertEquals( 1, count( $results['data']['categories']['edges'] ) );
+		$this->assertEquals( $second_last_term_id, $results['data']['categories']['edges'][0]['node']['categoryId'] );
+		$this->assertEquals( $expected_cursor, $results['data']['categories']['edges'][0]['cursor'] );
+		$this->assertEquals( $expected_cursor, $results['data']['categories']['pageInfo']['startCursor'] );
+		$this->assertEquals( $expected_cursor, $results['data']['categories']['pageInfo']['endCursor'] );
+
+	}
+
+	public function _testMaxQueryAmount() {
+		$variables = [
+			'first' => 150,
+		];
+		$results   = $this->postsQuery( $variables );
+		$this->assertNotEmpty( $results );
+
+		/**
+		 * The max that can be queried by default is 100 items
+		 */
+		$this->assertCount( 100, $results['data']['posts']['edges'] );
+		$this->assertTrue( $results['data']['posts']['pageInfo']['hasNextPage'] );
+
+		/**
+		 * Test the filter to make sure it's capping the results properly
+		 */
+		add_filter( 'graphql_connection_max_query_amount', function() {
+			return 20;
+		} );
+
+		$variables = [
+			'first' => 150,
+		];
+		$results   = $this->postsQuery( $variables );
+
+		add_filter( 'graphql_connection_max_query_amount', function() {
+			return 100;
+		} );
+
+		$this->assertCount( 20, $results['data']['posts']['edges'] );
+		$this->assertTrue( $results['data']['posts']['pageInfo']['hasNextPage'] );
+	}
+
+}
diff --git a/tests/test-term-object-mutations.php b/tests/test-term-object-mutations.php
index 987496206..858821e68 100644
--- a/tests/test-term-object-mutations.php
+++ b/tests/test-term-object-mutations.php
@@ -150,6 +150,7 @@ public function deleteTagMutation( $id ) {
 		    }
 		  ) {
 		    clientMutationId
+		    deletedId
 		    tag {
 		        id
 		        name
@@ -291,6 +292,18 @@ public function testCategoryMutations() {
 		/**
 		 * Delete the tag
 		 */
+		wp_set_current_user( $this->subscriber );
+		$deleted_category = $this->deleteCategoryMutation( $updated_category['data']['updateCategory']['category']['id'] );
+
+		/**
+		 * A subscriber shouldn't be able to delete, so we should get an error
+		 */
+		$this->assertArrayHasKey( 'errors', $deleted_category );
+
+		/**
+		 * Set the user back to admin and delete again
+		 */
+		wp_set_current_user( $this->admin );
 		$deleted_category = $this->deleteCategoryMutation( $updated_category['data']['updateCategory']['category']['id'] );
 
 		/**
@@ -304,6 +317,134 @@ public function testCategoryMutations() {
 		$this->assertEquals( $deleted_category['data']['deleteCategory']['category']['name'], $this->category_name );
 	}
 
+	public function testCreateTagThatAlreadyExists() {
+
+		/**
+		 * Set the user as the admin
+		 */
+		wp_set_current_user( $this->admin );
+
+		/**
+		 * Run the mutation
+		 */
+		$actual1 = $this->createCategoryMutation();
+		$actual2 = $this->createCategoryMutation();
+
+		/**
+		 * Create the term
+		 */
+		$this->assertNotEmpty( $actual1 );
+
+		/**
+		 * Make sure there were no errors
+		 */
+		$this->assertArrayNotHasKey( 'errors', $actual1 );
+		$this->assertArrayHasKey( 'data', $actual1 );
+
+		/**
+		 * Try to create the exact same term
+		 */
+		$this->assertNotEmpty( $actual2 );
+
+		/**
+		 * Now we should expect an error
+		 */
+		$this->assertArrayHasKey( 'errors', $actual2 );
+		$this->assertArrayHasKey( 'data', $actual2 );
+
+	}
+
+	public function testTermIdNotReturningAfterCreate() {
+
+		/**
+		 * Filter the term response to simulate a failure with the response of a term creation mutation
+		 */
+		add_filter( 'term_id_filter', '__return_false' );
+
+		/**
+		 * Set the user as the admin
+		 */
+		wp_set_current_user( $this->admin );
+
+		/**
+		 * Run the mutation
+		 */
+		$actual = $this->createCategoryMutation();
+		$this->assertArrayHasKey( 'errors', $actual );
+		$actual = $this->deleteTagMutation( 'someInvalidId' );
+
+		/**
+		 * We should get an error because the ID is invalid
+		 */
+		$this->assertArrayHasKey( 'errors', $actual );
+
+		/**
+		 * Cleanup by removing the filter
+		 */
+		remove_filter( 'term_id_filter', '__return_false' );
+
+		/**
+		 * Now let's filter to mimick the response returning a WP_Error to make sure we also respond with an error
+		 */
+		add_filter( 'get_post_tag', function() {
+			return new \WP_Error( 'this is a test error' );
+		} );
+
+		/**
+		 * Create a term
+		 */
+		$term = $this->factory->term->create([
+			'taxonomy' => 'post_tag',
+			'name' => 'some random name',
+		]);
+
+		/**
+		 * Now try and delete it.
+		 */
+		$id = \GraphQLRelay\Relay::toGlobalId( 'post_tag', $term );
+		$actual = $this->deleteTagMutation( $id );
+
+		/**
+		 * Assert that we have an error because the response to the deletion responded with a WP_Error
+		 */
+		$this->assertArrayHasKey( 'errors', $actual );
+
+
+	}
+
+	public function testCreateTagWithNoName() {
+
+		$mutation = '
+		mutation createTag( $clientMutationId:String!, $name:String!, $description:String ) {
+		  createTag(
+		    input: {
+			  clientMutationId: $clientMutationId
+			    name: $name
+				description: $description
+			}
+		  ) {
+			clientMutationId
+			tag {
+			  id
+			  name
+			  description
+			}
+		  }
+		}
+		';
+
+		$variables = wp_json_encode([
+			'clientMutationId' => $this->client_mutation_id,
+			'description' => $this->description,
+		]);
+
+		$actual = do_graphql_request( $mutation, 'createTag', $variables );
+
+		$this->assertNotEmpty( $actual );
+		$this->assertArrayHasKey( 'errors', $actual );
+
+	}
+
 	/**
 	 * Test creating a tag
 	 */
@@ -337,12 +478,22 @@ public function testTagMutations() {
 		$try_to_update_category = $this->updateCategoryMutation( $actual['data']['createTag']['tag']['id'] );
 		$this->assertNotEmpty( $try_to_update_category['errors'] );
 
+		/**
+		 * Try to update the tag as a user with improper permissions
+		 */
+		wp_set_current_user( $this->subscriber );
+		$try_to_delete_category = $this->updateTagMutation( $actual['data']['createTag']['tag']['id'] );
+		$this->assertNotEmpty( $try_to_delete_category['errors'] );
+		wp_set_current_user( $this->admin );
+
 		/**
 		 * Try to update a Category using a Tag ID, which should return errors
 		 */
-		$try_to_delete_category = $this->deleteCategoryMutation( $actual['data']['createTag']['tag']['id'] );
+		$try_to_delete_category = $this->updateCategoryMutation( $actual['data']['createTag']['tag']['id'] );
 		$this->assertNotEmpty( $try_to_delete_category['errors'] );
 
+
+
 		/**
 		 * Run the update mutation with the ID of the created tag
 		 */
@@ -369,6 +520,7 @@ public function testTagMutations() {
 		 */
 		$this->assertNotEmpty( $deleted_tag );
 		$this->assertEquals( $deleted_tag['data']['deleteTag']['clientMutationId'], $this->client_mutation_id );
+		$this->assertNotEmpty( $deleted_tag['data']['deleteTag']['deletedId'] );
 		$id_parts = \GraphQLRelay\Relay::fromGlobalId( $deleted_tag['data']['deleteTag']['tag']['id'] );
 		$this->assertEquals( $id_parts['type'], 'post_tag' );
 		$this->assertNotEmpty( $id_parts['id'] );
@@ -426,4 +578,42 @@ public function testCreateCategoryWithoutProperCapabilities() {
 
 	}
 
+	public function testUpdateCategoryParent() {
+
+		wp_set_current_user( $this->admin );
+
+		$parent_term_id = $this->factory->term->create([
+			'taxonomy' => 'category',
+			'name' => 'Parent Category',
+		]);
+
+		$query = '
+		mutation createChildCategory($input: createCategoryInput!) {
+		  createCategory(input: $input) {
+		    category {
+		      parent{
+		        id
+		      }
+		    }
+		  }
+		}
+		';
+
+		$parent_id = \GraphQLRelay\Relay::toGlobalId( 'category', $parent_term_id );
+
+		$variables = [
+			'input' => [
+				'clientMutationId' => 'someId',
+				'name' => 'Child Category',
+				'parentId' => $parent_id,
+			],
+		];
+
+		$actual = do_graphql_request( $query, 'createChildCategory', $variables );
+
+		$this->assertArrayNotHasKey( 'errors', $actual );
+		$this->assertEquals( $parent_id, $actual['data']['createCategory']['category']['parent']['id'] );
+
+	}
+
 }
diff --git a/tests/test-term-object-queries.php b/tests/test-term-object-queries.php
index 019e8a9b4..dc98b1bf5 100644
--- a/tests/test-term-object-queries.php
+++ b/tests/test-term-object-queries.php
@@ -39,6 +39,121 @@ public function createTermObject( $args = [] ) {
 
 	}
 
+	public function testTermObjectConnectionQuery() {
+
+		$term_id1 = $this->createTermObject( [
+			'name'     => 'AAA1 Term',
+			'taxonomy' => 'category',
+			'description' => 'just a description',
+		] );
+
+		$term_id2 = $this->createTermObject( [
+			'name'     => 'AAA2 Term',
+			'taxonomy' => 'category',
+			'description' => 'just a description',
+		] );
+
+		$term_id3 = $this->createTermObject( [
+			'name'     => 'AAA3 Term',
+			'taxonomy' => 'category',
+			'description' => 'just a description',
+		] );
+
+		$query = '
+		{
+		  categories(where:{hideEmpty:false}) {
+		    edges {
+		      node {
+		        id
+		        categoryId
+		        name
+		      }
+		    }
+		  }
+		}
+		';
+
+		$actual = do_graphql_request( $query );
+
+		$this->assertNotEmpty( $actual['data']['categories']['edges'][0]['node'] );
+		$this->assertNotEmpty( $actual['data']['categories']['edges'][0]['node']['categoryId'], $term_id1 );
+
+		$query = '
+		query getCategoriesBefore($beforeCursor:String){
+			categories(last:1 before:$beforeCursor where:{hideEmpty:false}){
+			  edges{
+			    node{
+			      id
+			      categoryId
+			      name
+			    }
+			  }
+			}  
+		}
+		';
+
+		/**
+		 * Create a cursor for the first term ID
+		 */
+		$cursor = \GraphQLRelay\Connection\ArrayConnection::offsetToCursor( $term_id2 );
+
+		/**
+		 * Use the cursor in our variables
+		 */
+		$variables = wp_json_encode([
+			'beforeCursor' => $cursor,
+		]);
+
+		/**
+		 * Do the request
+		 */
+		$actual = do_graphql_request( $query, 'getCategoriesBefore', $variables );
+
+		/**
+		 * Assert that we should have received just 1 node, $term_id2
+		 */
+		$this->assertCount( 1, $actual['data']['categories']['edges'] );
+		$this->assertEquals( $actual['data']['categories']['edges'][0]['node']['categoryId'], $term_id1 );
+
+		$query = '
+		query getCategoriesAfter($afterCursor:String){
+			categories(first:1 after:$afterCursor where:{hideEmpty:false}){
+			  edges{
+			    node{
+			      id
+			      categoryId
+			      name
+			    }
+			  }
+			}  
+		}
+		';
+
+		/**
+		 * Create a cursor for the first term ID
+		 */
+		$cursor = \GraphQLRelay\Connection\ArrayConnection::offsetToCursor( $term_id2 );
+
+		/**
+		 * Use the cursor in our variables
+		 */
+		$variables = wp_json_encode([
+			'afterCursor' => $cursor,
+		]);
+
+		/**
+		 * Do the request
+		 */
+		$actual = do_graphql_request( $query, 'getCategoriesAfter', $variables );
+
+		/**
+		 * Assert that we should have received just 1 node, $term_id2
+		 */
+		$this->assertCount( 1, $actual['data']['categories']['edges'] );
+		$this->assertEquals( $actual['data']['categories']['edges'][0]['node']['categoryId'], $term_id3 );
+
+	}
+
 	/**
 	 * testTermQuery
 	 *
@@ -123,6 +238,7 @@ public function testTermQuery() {
 		];
 
 		$this->assertEquals( $expected, $actual );
+
 	}
 
 	/**
diff --git a/tests/test-theme-connection-queries.php b/tests/test-theme-connection-queries.php
new file mode 100644
index 000000000..c7d63c120
--- /dev/null
+++ b/tests/test-theme-connection-queries.php
@@ -0,0 +1,70 @@
+current_time = strtotime( 'now' );
+		$this->current_date = date( 'Y-m-d H:i:s', $this->current_time );
+		$this->current_date_gmt = gmdate( 'Y-m-d H:i:s', $this->current_time );
+		$this->admin = $this->factory->user->create( [
+			'role' => 'administrator',
+		] );
+
+	}
+
+	/**
+	 * Runs after each method.
+	 * @since 0.0.5
+	 */
+	public function tearDown() {
+		parent::tearDown();
+	}
+
+	/**
+	 * testThemesQuery
+	 * This tests querying for themes to ensure that we're getting back a proper connection
+	 */
+	public function testThemesQuery() {
+
+		$query = '
+		{
+		  themes(last: 1){
+		    edges{
+		      node{
+		        id
+		        name
+		      }
+		    }
+		  }
+		}
+		';
+
+		$actual = do_graphql_request( $query );
+
+		/**
+		 * We don't really care what the specifics are because the default theme could change at any time
+		 * and we don't care to maintain the exact match, we just want to make sure we are
+		 * properly getting a theme back in the query
+		 */
+		$this->assertNotEmpty( $actual['data']['themes']['edges'] );
+		$this->assertNotEmpty( $actual['data']['themes']['edges'][0]['node']['id'] );
+		$this->assertNotEmpty( $actual['data']['themes']['edges'][0]['node']['name'] );
+	}
+
+}
diff --git a/tests/test-theme-object-queries.php b/tests/test-theme-object-queries.php
index b2fd10248..2d5c21636 100644
--- a/tests/test-theme-object-queries.php
+++ b/tests/test-theme-object-queries.php
@@ -69,6 +69,9 @@ public function testThemeQuery() {
 		 */
 		$actual = do_graphql_request( $query );
 
+		$screenshot = $actual['data']['theme']['screenshot'];
+		$this->assertTrue( is_string( $screenshot ) || null === $screenshot );
+
 		/**
 		 * Establish the expectation for the output of the query
 		 */
@@ -80,7 +83,7 @@ public function testThemeQuery() {
 					'description' => null,
 					'id' => $global_id,
 					'name' => 'Twenty Seventeen',
-					'screenshot' => 'http://example.org/wp-content/themes/twentyseventeen/screenshot.png',
+					'screenshot' => $screenshot,
 					'slug' => 'twentyseventeen',
 					'tags' => null,
 					'themeUri' => 'https://wordpress.org/themes/twentyseventeen/',
diff --git a/tests/test-types.php b/tests/test-types.php
new file mode 100644
index 000000000..f46f20347
--- /dev/null
+++ b/tests/test-types.php
@@ -0,0 +1,77 @@
+assertEquals( [], $actual );
+
+		/**
+		 * Setup some args
+		 */
+		$map = [
+			'stringInput' => 'string_input',
+			'intInput' => 'int_input',
+			'boolInput' => 'bool_input',
+			'inputObject' => 'input_object',
+		];
+
+		$input_args = [
+			'stringInput' => 'value 2',
+			'intInput' => 2,
+			'boolInput' => false,
+		];
+
+		$args = [
+			'stringInput' => 'value',
+			'intInput' => 1,
+			'boolInput' => true,
+			'inputObject' => \WPGraphQL\Types::map_input( $input_args, $map ),
+		];
+
+		$expected = [
+			'string_input' => 'value',
+			'int_input' => 1,
+			'bool_input' => true,
+			'input_object' => [
+				'string_input' => 'value 2',
+				'int_input' => 2,
+				'bool_input' => false,
+			],
+		];
+
+		$actual = \WPGraphQL\Types::map_input( $args, $map );
+
+		$this->assertEquals( $expected, $actual );
+
+	}
+
+}
diff --git a/tests/test-user-object-queries.php b/tests/test-user-object-queries.php
index e66f4d5dd..f54e570cd 100644
--- a/tests/test-user-object-queries.php
+++ b/tests/test-user-object-queries.php
@@ -2,8 +2,9 @@
 /**
  * WPGraphQL Test User Object Queries
  * This tests user queries (singular and plural) checking to see if the available fields return the expected response
+ *
  * @package WPGraphQL
- * @since 0.0.5
+ * @since   0.0.5
  */
 
 /**
@@ -12,6 +13,7 @@
 class WP_GraphQL_Test_User_Object_Queries extends WP_UnitTestCase {
 	/**
 	 * This function is run before each method
+	 *
 	 * @since 0.0.5
 	 */
 	public function setUp() {
@@ -23,6 +25,7 @@ public function setUp() {
 
 	/**
 	 * Runs after each method.
+	 *
 	 * @since 0.0.5
 	 */
 	public function tearDown() {
@@ -73,7 +76,7 @@ public function testUserQuery() {
 				'user_email' => 'test@test.com',
 			]
 		);
-		$user = get_user_by( 'id', $user_id );
+		$user    = get_user_by( 'id', $user_id );
 
 		/**
 		 * Create the global ID based on the user_type and the created $id
@@ -148,38 +151,38 @@ public function testUserQuery() {
 		$expected = [
 			'data' => [
 				'user' => [
-					'avatar' => [
+					'avatar'            => [
 						'size' => 96,
 					],
-					'capKey' => 'wptests_capabilities',
-					'capabilities' => [ 'read', 'level_0', 'subscriber' ],
-					'comments' => [
+					'capKey'            => 'wptests_capabilities',
+					'capabilities'      => [ 'read', 'level_0', 'subscriber' ],
+					'comments'          => [
 						'edges' => [],
 					],
-					'description' => null,
-					'email' => 'test@test.com',
+					'description'       => null,
+					'email'             => 'test@test.com',
 					'extraCapabilities' => [ 'read', 'level_0', 'subscriber' ],
-					'firstName' => null,
-					'id' => $global_id,
-					'last_name' => null,
-					'locale' => 'en_US',
-					'mediaItems' => [
+					'firstName'         => null,
+					'id'                => $global_id,
+					'last_name'         => null,
+					'locale'            => 'en_US',
+					'mediaItems'        => [
 						'edges' => [],
 					],
-					'name' => $user->data->display_name,
-					'nickname' => $user->nickname,
-					'pages' => [
+					'name'              => $user->data->display_name,
+					'nickname'          => $user->nickname,
+					'pages'             => [
 						'edges' => [],
 					],
-					'posts' => [
+					'posts'             => [
 						'edges' => [],
 					],
-					'registeredDate' => date( 'c', strtotime( $user->user_registered ) ),
-					'roles' => [ 'subscriber' ],
-					'slug' => $user->data->user_nicename,
-					'url' => null,
-					'userId' => $user_id,
-					'username' => $user->data->user_login,
+					'registeredDate'    => date( 'c', strtotime( $user->user_registered ) ),
+					'roles'             => [ 'subscriber' ],
+					'slug'              => $user->data->user_nicename,
+					'url'               => null,
+					'userId'            => $user_id,
+					'username'          => $user->data->user_login,
 				],
 			],
 		];
@@ -475,19 +478,19 @@ public function testUserQueryWhereUserDoesNotExist() {
 		 * Establish the expectation for the output of the query
 		 */
 		$expected = [
-			'data' => [
+			'data'   => [
 				'user' => null,
 			],
 			'errors' => [
 				[
-					'message' => 'No user was found with ID doesNotExist',
+					'message'   => 'No user was found with ID doesNotExist',
 					'locations' => [
 						[
-							'line' => 3,
+							'line'   => 3,
 							'column' => 4,
 						],
 					],
-					'path' => [
+					'path'      => [
 						'user',
 					],
 				],
@@ -496,4 +499,126 @@ public function testUserQueryWhereUserDoesNotExist() {
 
 		$this->assertEquals( $expected, $actual );
 	}
+
+	public function testUsersQuery() {
+
+		/**
+		 * Create a user
+		 */
+		$user_id = $this->createUserObject(
+			[
+				'user_email' => 'test@test.com',
+			]
+		);
+
+		/**
+		 * Create the global ID based on the user_type and the created $id
+		 */
+		$global_id = \GraphQLRelay\Relay::toGlobalId( 'user', $user_id );
+
+		/**
+		 * Create the query string to pass to the $query
+		 */
+		$query = '
+		query {
+			users(first:1) {
+				edges{
+				  node{
+				    id
+				    userId
+				  }
+				}
+			}
+		}';
+
+		/**
+		 * Run the GraphQL query
+		 */
+		$actual = do_graphql_request( $query );
+
+		/**
+		 * Establish the expectation for the output of the query
+		 */
+		$expected = [
+			'data' => [
+				'users' => [
+					'edges' => [
+						[
+							'node' => [
+								'id' => $global_id,
+								'userId' => $user_id,
+							],
+						],
+					],
+				],
+			],
+		];
+
+		$this->assertEquals( $expected, $actual );
+
+	}
+
+	public function testPageInfoQuery() {
+
+		/**
+		 * Let's create 2 admins and 1 subscriber so we can test our "where" arg is working
+		 */
+		$this->factory->user->create([
+			'role' => 'administrator',
+		]);
+
+		$this->factory->user->create([
+			'role' => 'administrator',
+		]);
+
+		$this->factory->user->create([
+			'role' => 'subscriber',
+		]);
+
+		$query = '
+		query{
+		  users(first:2 where: {role:ADMINISTRATOR}){
+		    pageInfo{
+		      hasNextPage
+		    }
+		    edges{
+		      node{
+		        id
+		      }
+		    }
+		  }
+		}
+		';
+
+		$actual = do_graphql_request( $query );
+
+		$this->assertNotEmpty( $actual['data']['users']['pageInfo'] );
+		$this->assertNotEmpty( $actual['data']['users']['edges'] );
+		$this->assertCount( 2, $actual['data']['users']['edges'] );
+
+		$query = '
+		query{
+		  users(first:2 where: {role:SUBSCRIBER}){
+		    pageInfo{
+		      hasNextPage
+		    }
+		    edges{
+		      node{
+		        id
+		      }
+		    }
+		  }
+		}
+		';
+
+		$actual = do_graphql_request( $query );
+
+		/**
+		 * Now let's make sure the subscriber role query worked
+		 */
+		$this->assertNotEmpty( $actual['data']['users']['pageInfo'] );
+		$this->assertNotEmpty( $actual['data']['users']['edges'] );
+		$this->assertCount( 1, $actual['data']['users']['edges'] );
+
+	}
 }
diff --git a/tests/test-viewer-query.php b/tests/test-viewer-query.php
new file mode 100644
index 000000000..988b04a64
--- /dev/null
+++ b/tests/test-viewer-query.php
@@ -0,0 +1,66 @@
+factory->user->create( [
+			'role' => 'administrator',
+		] );
+
+		$query = '
+		{
+		  viewer{
+		    userId
+		    roles
+		  }
+		}
+		';
+
+		$actual = do_graphql_request( $query );
+
+		/**
+		 * We should get an error because no user is logged in right now
+		 */
+		$this->assertArrayHasKey( 'errors', $actual );
+
+		/**
+		 * Set the current user so we can properly test the viewer query
+		 */
+		wp_set_current_user( $user_id );
+		$actual = do_graphql_request( $query );
+
+		$this->assertNotEmpty( $actual );
+		$this->assertArrayNotHasKey( 'errors', $actual );
+		$this->assertEquals( $user_id, $actual['data']['viewer']['userId'] );
+		$this->assertContains( 'administrator', $actual['data']['viewer']['roles'] );
+
+	}
+
+}