Repository: amp-labs/connectors
Pull request #1367 (2025-02-15 00:34:01)

tidy(jsonquery): Explicit querying of required/optional JSON
# Background
The jsonquery package previously used a boolean flag to indicate whether a value in the JSON response should be optional or required. However, this approach has some issues:
* It is not very intuitive to read and understand.
* Required values should not be pointers, as this forces unnecessary pointer dereferencing despite knowing the value must be non-null.

# Review
The refactoring was performed method by method and split into separate commits for clarity.

Cobalt0s: * **#1367** <a href="https://app.graphite.dev/github/pr/amp-labs/connectors/1367?utm_source=stack-comment-icon" target="_blank"><img src="https://static.graphite.dev/graphite-32x32-black.png" alt="Graphite" width="10px" height="10px"/></a> 👈 <a href="https://app.graphite.dev/github/pr/amp-labs/connectors/1367?utm_source=stack-comment-view-in-graphite" target="_blank">(View in Graphite)</a>
* `main`


<h2></h2>

This stack of pull requests is managed by <a href="https://graphite.dev?utm-source=stack-comment"><b>Graphite</b></a>. Learn more about <a href="https://stacking.dev/?utm_source=stack-comment">stacking</a>.
<!-- Current dependencies on/for this PR: -->

Patch:
diff --git a/common/jsonquery/defaults.go b/common/jsonquery/defaults.go
index db1a8d92..430ba6c7 100644
--- a/common/jsonquery/defaults.go
+++ b/common/jsonquery/defaults.go
@@ -6,7 +6,7 @@ import (
 )
 
 func (q *Query) IntegerWithDefault(key string, defaultValue int64) (int64, error) {
-	result, err := q.Integer(key, true)
+	result, err := q.IntegerOptional(key)
 	if err != nil {
 		return 0, err
 	}
@@ -19,7 +19,7 @@ func (q *Query) IntegerWithDefault(key string, defaultValue int64) (int64, error
 }
 
 func (q *Query) StrWithDefault(key string, defaultValue string) (string, error) {
-	result, err := q.Str(key, true)
+	result, err := q.StringOptional(key)
 	if err != nil {
 		return "", err
 	}
@@ -44,7 +44,7 @@ func (q *Query) TextWithDefault(key string, defaultValue string) (string, error)
 		// Current data under `key` is not a string.
 		// Explore other data types.
 		// NOTE: as of now we check only if it is an integer.
-		number, err := q.Integer(key, true)
+		number, err := q.IntegerOptional(key)
 		if err != nil {
 			return "", err
 		}
@@ -60,7 +60,7 @@ func (q *Query) TextWithDefault(key string, defaultValue string) (string, error)
 }
 
 func (q *Query) BoolWithDefault(key string, defaultValue bool) (bool, error) {
-	result, err := q.Bool(key, true)
+	result, err := q.BoolOptional(key)
 	if err != nil {
 		return false, err
 	}
diff --git a/common/jsonquery/queries.go b/common/jsonquery/queries.go
index 611b7a12..71dbe283 100644
--- a/common/jsonquery/queries.go
+++ b/common/jsonquery/queries.go
@@ -27,10 +27,22 @@ func New(node *ajson.Node, zoom ...string) *Query {
 	}
 }
 
-// Object returns json object.
-// Optional argument set to false will create error in case of missing value.
+// ObjectOptional returns node object if present.
+// If the entity at the key path is not a node object, an error is returned.
 // Empty key is interpreter as "this", in other words current node.
-func (q *Query) Object(key string, optional bool) (*ajson.Node, error) {
+func (q *Query) ObjectOptional(key string) (*ajson.Node, error) {
+	return q.internalQueryObject(key, true)
+}
+
+// ObjectRequired returns node object.
+// If the entity at the key path is not a node object or is missing, an error is returned.
+// Empty key is interpreter as "this", in other words current node.
+// Missing key returns ErrKeyNotFound. Null value returns ErrNullJSON.
+func (q *Query) ObjectRequired(key string) (*ajson.Node, error) {
+	return q.internalQueryObject(key, false)
+}
+
+func (q *Query) internalQueryObject(key string, optional bool) (*ajson.Node, error) {
 	node, err := q.getInnerKey(key, optional)
 	if err != nil {
 		return nil, err
@@ -51,10 +63,27 @@ func (q *Query) Object(key string, optional bool) (*ajson.Node, error) {
 	return node, nil
 }
 
-// Integer returns integer.
-// Optional argument set to false will create error in case of missing value.
+// IntegerOptional returns integer if present.
+// If the entity at the key path is not an integer, an error is returned.
 // Empty key is interpreter as "this", in other words current node.
-func (q *Query) Integer(key string, optional bool) (*int64, error) {
+func (q *Query) IntegerOptional(key string) (*int64, error) {
+	return q.internalQueryInteger(key, true)
+}
+
+// IntegerRequired returns integer.
+// If the entity at the key path is not an integer or is missing, an error is returned.
+// Empty key is interpreter as "this", in other words current node.
+// Missing key returns ErrKeyNotFound. Null value returns ErrNullJSON.
+func (q *Query) IntegerRequired(key string) (int64, error) {
+	integer, err := q.internalQueryInteger(key, false)
+	if err != nil {
+		return 0, err
+	}
+
+	return *integer, nil
+}
+
+func (q *Query) internalQueryInteger(key string, optional bool) (*int64, error) {
 	node, err := q.getInnerKey(key, optional)
 	if err != nil {
 		return nil, err
@@ -82,10 +111,27 @@ func (q *Query) Integer(key string, optional bool) (*int64, error) {
 	return &result, nil
 }
 
-// Str returns string.
-// Optional argument set to false will create error in case of missing value.
-// Empty key is interpreter as "this", in other words current node.
-func (q *Query) Str(key string, optional bool) (*string, error) {
+// StringOptional returns string if present.
+// If the entity at the key path is not a string, an error is returned.
+// Empty key is interpreted as "this", in other words a current node.
+func (q *Query) StringOptional(key string) (*string, error) {
+	return q.internalQueryString(key, true)
+}
+
+// StringRequired returns string.
+// If the entity at the key path is not a string or is missing, an error is returned.
+// Empty key is interpreted as "this", in other words a current node.
+// Missing key returns ErrKeyNotFound. Null value returns ErrNullJSON.
+func (q *Query) StringRequired(key string) (string, error) {
+	text, err := q.internalQueryString(key, false)
+	if err != nil {
+		return "", err
+	}
+
+	return *text, nil
+}
+
+func (q *Query) internalQueryString(key string, optional bool) (*string, error) {
 	node, err := q.getInnerKey(key, optional)
 	if err != nil {
 		return nil, err
@@ -107,10 +153,27 @@ func (q *Query) Str(key string, optional bool) (*string, error) {
 	return &txt, nil
 }
 
-// Bool returns boolean.
-// Optional argument set to false will create error in case of missing value.
-// Empty key is interpreter as "this", in other words current node.
-func (q *Query) Bool(key string, optional bool) (*bool, error) {
+// BoolOptional returns boolean if present.
+// If the entity at the key path is not a boolean, an error is returned.
+// Empty key is interpreted as "this", in other words a current node.
+func (q *Query) BoolOptional(key string) (*bool, error) {
+	return q.internalQueryBool(key, true)
+}
+
+// BoolRequired returns boolean.
+// If the entity at the key path is not a boolean or is missing, an error is returned.
+// Empty key is interpreted as "this", in other words a current node.
+// Missing key returns ErrKeyNotFound. Null value returns ErrNullJSON.
+func (q *Query) BoolRequired(key string) (bool, error) {
+	flag, err := q.internalQueryBool(key, false)
+	if err != nil {
+		return false, err
+	}
+
+	return *flag, nil
+}
+
+func (q *Query) internalQueryBool(key string, optional bool) (*bool, error) {
 	node, err := q.getInnerKey(key, optional)
 	if err != nil {
 		return nil, err
@@ -132,10 +195,22 @@ func (q *Query) Bool(key string, optional bool) (*bool, error) {
 	return &flag, nil
 }
 
-// Array returns list of nodes.
-// Optional argument set to false will create error in case of missing value.
-// Empty key is interpreter as "this", in other words current node.
-func (q *Query) Array(key string, optional bool) ([]*ajson.Node, error) {
+// ArrayOptional returns array of nodes if present.
+// If the entity at the key path is not an array, an error is returned.
+// Empty key is interpreted as "this", in other words a current node.
+func (q *Query) ArrayOptional(key string) ([]*ajson.Node, error) {
+	return q.internalQueryArray(key, true)
+}
+
+// ArrayRequired returns array of nodes.
+// If the entity at the key path is not an array or is missing, an error is returned.
+// Empty key is interpreted as "this", in other words a current node.
+// Missing key returns ErrKeyNotFound. Null value returns ErrNullJSON.
+func (q *Query) ArrayRequired(key string) ([]*ajson.Node, error) {
+	return q.internalQueryArray(key, false)
+}
+
+func (q *Query) internalQueryArray(key string, optional bool) ([]*ajson.Node, error) {
 	node, err := q.getInnerKey(key, optional)
 	if err != nil {
 		return nil, err
@@ -156,15 +231,3 @@ func (q *Query) Array(key string, optional bool) ([]*ajson.Node, error) {
 
 	return arr, nil
 }
-
-// ArraySize returns the array size located under key.
-// It is assumed that array value must be not null and present.
-// Empty key is interpreter as "this", in other words current node.
-func (q *Query) ArraySize(key string) (int64, error) {
-	arr, err := q.Array(key, false)
-	if err != nil {
-		return 0, err
-	}
-
-	return int64(len(arr)), nil
-}
diff --git a/common/parse.go b/common/parse.go
index dee6353c..a1a3e11b 100644
--- a/common/parse.go
+++ b/common/parse.go
@@ -117,7 +117,17 @@ func GetOptionalRecordsUnderJSONPath(jsonPath string, nestedPath ...string) Reco
 
 func getRecords(optional bool, jsonPath string, nestedPath ...string) RecordsFunc {
 	return func(node *ajson.Node) ([]map[string]any, error) {
-		arr, err := jsonquery.New(node, nestedPath...).Array(jsonPath, optional)
+		var (
+			arr []*ajson.Node
+			err error
+		)
+
+		if optional {
+			arr, err = jsonquery.New(node, nestedPath...).ArrayOptional(jsonPath)
+		} else {
+			arr, err = jsonquery.New(node, nestedPath...).ArrayRequired(jsonPath)
+		}
+
 		if err != nil {
 			return nil, err
 		}
diff --git a/providers/apollo/metadata.go b/providers/apollo/metadata.go
index e665aa5d..c1e917b9 100644
--- a/providers/apollo/metadata.go
+++ b/providers/apollo/metadata.go
@@ -67,7 +67,7 @@ func (c *Connector) ListObjectMetadata(ctx context.Context,
 func parseMetadataFromResponse(body *ajson.Node, objectName string) (*common.ObjectMetadata, error) {
 	objectName = constructSupportedObjectName(objectName)
 
-	arr, err := jsonquery.New(body).Array(objectName, true)
+	arr, err := jsonquery.New(body).ArrayOptional(objectName)
 	if err != nil {
 		return nil, err
 	}
diff --git a/providers/apollo/parse.go b/providers/apollo/parse.go
index 3f35fbe3..a46adc30 100644
--- a/providers/apollo/parse.go
+++ b/providers/apollo/parse.go
@@ -12,7 +12,7 @@ import (
 func getNextRecords(node *ajson.Node) (string, error) {
 	var nextPage string
 
-	pagination, err := jsonquery.New(node).Object("pagination", true)
+	pagination, err := jsonquery.New(node).ObjectOptional("pagination")
 	if err != nil {
 		return "", err
 	}
@@ -23,7 +23,7 @@ func getNextRecords(node *ajson.Node) (string, error) {
 			return "", err
 		}
 
-		totalPages, err := jsonquery.New(pagination).Integer("total_pages", true)
+		totalPages, err := jsonquery.New(pagination).IntegerOptional("total_pages")
 		if err != nil {
 			return "", err
 		}
@@ -39,7 +39,7 @@ func getNextRecords(node *ajson.Node) (string, error) {
 // recordsWrapperFunc returns the records using the objectName dynamically.
 func recordsWrapperFunc(obj string) common.RecordsFunc {
 	return func(node *ajson.Node) ([]map[string]any, error) {
-		result, err := jsonquery.New(node).Array(obj, true)
+		result, err := jsonquery.New(node).ArrayOptional(obj)
 		if err != nil {
 			return nil, err
 		}
@@ -55,7 +55,7 @@ func searchRecords(fld string) common.RecordsFunc {
 	fld = constructSupportedObjectName(fld)
 
 	return func(node *ajson.Node) ([]map[string]any, error) {
-		result, err := jsonquery.New(node).Array(fld, true)
+		result, err := jsonquery.New(node).ArrayOptional(fld)
 		if err != nil {
 			return nil, err
 		}
diff --git a/providers/apollo/write.go b/providers/apollo/write.go
index e6192ae6..67bd0056 100644
--- a/providers/apollo/write.go
+++ b/providers/apollo/write.go
@@ -53,7 +53,7 @@ func constructWriteResult(body *ajson.Node, objName string) (*common.WriteResult
 	// created/updated details in it.
 	obj := naming.NewSingularString(objName)
 
-	respObject, err := jsonquery.New(body).Object(obj.String(), false)
+	respObject, err := jsonquery.New(body).ObjectRequired(obj.String())
 	if err != nil {
 		return nil, err
 	}
diff --git a/providers/asana/parse.go b/providers/asana/parse.go
index 99f99f7f..0db74023 100644
--- a/providers/asana/parse.go
+++ b/providers/asana/parse.go
@@ -8,7 +8,7 @@ import (
 
 func makeNextRecordsURL() common.NextPageFunc {
 	return func(node *ajson.Node) (string, error) {
-		nextPageURL, err := jsonquery.New(node, "next_page").Str("uri", true)
+		nextPageURL, err := jsonquery.New(node, "next_page").StringOptional("uri")
 		if err != nil {
 			return "", err
 		}
diff --git a/providers/asana/write.go b/providers/asana/write.go
index 0fda1894..d2d8afb5 100644
--- a/providers/asana/write.go
+++ b/providers/asana/write.go
@@ -51,7 +51,7 @@ func (c *Connector) Write(ctx context.Context, config common.WriteParams) (*comm
 }
 
 func constructWriteResult(body *ajson.Node) (*common.WriteResult, error) {
-	nested, err := jsonquery.New(body).Object("data", false)
+	nested, err := jsonquery.New(body).ObjectRequired("data")
 	if err != nil {
 		return nil, err
 	}
diff --git a/providers/atlassian/authmetadata.go b/providers/atlassian/authmetadata.go
index 775976b7..d4e68263 100644
--- a/providers/atlassian/authmetadata.go
+++ b/providers/atlassian/authmetadata.go
@@ -75,19 +75,15 @@ func (c *Connector) retrieveCloudId(ctx context.Context) (string, error) {
 	}
 
 	for _, item := range arr {
-		workspaceName, err := jsonquery.New(item).Str("name", false)
+		workspaceName, err := jsonquery.New(item).StringRequired("name")
 		if err != nil {
 			return "", err
 		}
 
-		if *workspaceName == c.workspace {
-			// names match, select this container.
-			cloudId, err := jsonquery.New(item).Str("id", false)
-			if err != nil {
-				return "", err
-			}
-
-			return *cloudId, nil
+		if workspaceName == c.workspace {
+			// Names match, select this container.
+			// Returns cloudID.
+			return jsonquery.New(item).StringRequired("id")
 		}
 	}
 
diff --git a/providers/atlassian/metadataFields.go b/providers/atlassian/metadataFields.go
index e955ecad..34faadd0 100644
--- a/providers/atlassian/metadataFields.go
+++ b/providers/atlassian/metadataFields.go
@@ -26,17 +26,17 @@ func (c *Connector) parseFieldsJiraIssue(node *ajson.Node) (map[string]string, e
 	fieldsMap := make(map[string]string)
 
 	for _, item := range arr {
-		name, err := jsonquery.New(item).Str("id", false)
+		name, err := jsonquery.New(item).StringRequired("id")
 		if err != nil {
 			return nil, err
 		}
 
-		displayName, err := jsonquery.New(item).Str("name", false)
+		displayName, err := jsonquery.New(item).StringRequired("name")
 		if err != nil {
 			return nil, err
 		}
 
-		fieldsMap[*name] = *displayName
+		fieldsMap[name] = displayName
 	}
 
 	return fieldsMap, nil
diff --git a/providers/atlassian/parse.go b/providers/atlassian/parse.go
index c9117d39..0581aeac 100644
--- a/providers/atlassian/parse.go
+++ b/providers/atlassian/parse.go
@@ -31,7 +31,7 @@ Visual example of what will happen to each property:
 	}
 */
 func getRecords(node *ajson.Node) ([]map[string]any, error) {
-	arr, err := jsonquery.New(node).Array("issues", false)
+	arr, err := jsonquery.New(node).ArrayRequired("issues")
 	if err != nil {
 		return nil, err
 	}
@@ -39,7 +39,7 @@ func getRecords(node *ajson.Node) ([]map[string]any, error) {
 	list := make([]map[string]any, len(arr))
 
 	for index, item := range arr {
-		fieldsObject, err := jsonquery.New(item).Object("fields", false)
+		fieldsObject, err := jsonquery.New(item).ObjectRequired("fields")
 		if err != nil {
 			return nil, errors.Join(common.ErrParseError, err)
 		}
@@ -49,13 +49,13 @@ func getRecords(node *ajson.Node) ([]map[string]any, error) {
 			return nil, errors.Join(common.ErrParseError, err)
 		}
 
-		id, err := jsonquery.New(item).Str("id", false)
+		id, err := jsonquery.New(item).StringRequired("id")
 		if err != nil {
 			return nil, errors.Join(common.ErrParseError, err)
 		}
 
 		// Enhance response with id property.
-		fields["id"] = *id
+		fields["id"] = id
 		list[index] = fields
 	}
 
@@ -77,7 +77,7 @@ func getNextRecords(node *ajson.Node) (string, error) {
 		return "", nil
 	}
 
-	startAt, err := jsonquery.New(node).Integer("startAt", true)
+	startAt, err := jsonquery.New(node).IntegerOptional("startAt")
 	if err != nil {
 		return "", err
 	}
diff --git a/providers/atlassian/write.go b/providers/atlassian/write.go
index 5250f872..d584f8ec 100644
--- a/providers/atlassian/write.go
+++ b/providers/atlassian/write.go
@@ -52,14 +52,14 @@ func (c *Connector) Write(ctx context.Context, config common.WriteParams) (*comm
 }
 
 func constructWriteResult(body *ajson.Node) (*common.WriteResult, error) {
-	recordID, err := jsonquery.New(body).Str("id", false)
+	recordID, err := jsonquery.New(body).StringRequired("id")
 	if err != nil {
 		return nil, err
 	}
 
 	return &common.WriteResult{
 		Success:  true,
-		RecordId: *recordID,
+		RecordId: recordID,
 		Errors:   nil,
 		Data:     nil,
 	}, nil
diff --git a/providers/attio/parse.go b/providers/attio/parse.go
index 46de76aa..ed7ae9e3 100644
--- a/providers/attio/parse.go
+++ b/providers/attio/parse.go
@@ -14,7 +14,7 @@ func makeNextRecordsURL(reqLink *urlbuilder.URL) common.NextPageFunc {
 		previousStart := 0
 
 		// Extract the data key value from the response.
-		value, err := jsonquery.New(node).Array("data", false)
+		value, err := jsonquery.New(node).ArrayRequired("data")
 		if err != nil {
 			return "", err
 		}
diff --git a/providers/attio/write.go b/providers/attio/write.go
index 06dba6f8..2c05888f 100644
--- a/providers/attio/write.go
+++ b/providers/attio/write.go
@@ -58,12 +58,12 @@ func (c *Connector) Write(ctx context.Context, config common.WriteParams) (*comm
 func constructWriteResult(objName string, body *ajson.Node) (*common.WriteResult, error) {
 	obj := naming.NewSingularString(objName)
 
-	objectResponse, err := jsonquery.New(body).Object("data", false)
+	objectResponse, err := jsonquery.New(body).ObjectRequired("data")
 	if err != nil {
 		return nil, err
 	}
 
-	recordID, err := jsonquery.New(objectResponse, "id").Str(obj.String()+"_id", false)
+	recordID, err := jsonquery.New(objectResponse, "id").StringRequired(obj.String() + "_id")
 	if err != nil {
 		return nil, err
 	}
@@ -75,7 +75,7 @@ func constructWriteResult(objName string, body *ajson.Node) (*common.WriteResult
 
 	return &common.WriteResult{
 		Success:  true,
-		RecordId: *recordID,
+		RecordId: recordID,
 		Errors:   nil,
 		Data:     response,
 	}, nil
diff --git a/providers/chilipiper/parse.go b/providers/chilipiper/parse.go
index f530b656..3a472d10 100644
--- a/providers/chilipiper/parse.go
+++ b/providers/chilipiper/parse.go
@@ -24,12 +24,12 @@ func nextRecordsURL(url string) common.NextPageFunc {
 	return func(node *ajson.Node) (string, error) {
 		jsonQuery := jsonquery.New(node)
 
-		page, err := jsonQuery.Integer(pageKey, false)
+		page, err := jsonQuery.IntegerRequired(pageKey)
 		if err != nil {
 			return "", err
 		}
 
-		totalRecords, err := jsonQuery.Integer(totalKey, false)
+		totalRecords, err := jsonQuery.IntegerRequired(totalKey)
 		if err != nil {
 			return "", err
 		}
@@ -39,8 +39,8 @@ func nextRecordsURL(url string) common.NextPageFunc {
 			return "", err
 		}
 
-		if hasMorePages(pagesize, int(*page), int(*totalRecords)) {
-			pg := strconv.Itoa(int(*page + 1))
+		if hasMorePages(pagesize, int(page), int(totalRecords)) {
+			pg := strconv.Itoa(int(page + 1))
 
 			nextURL, err := urlbuilder.New(url)
 			if err != nil {
diff --git a/providers/closecrm/parse.go b/providers/closecrm/parse.go
index 60db595e..fbd492d9 100644
--- a/providers/closecrm/parse.go
+++ b/providers/closecrm/parse.go
@@ -34,7 +34,7 @@ var ErrSkipFailure = errors.New("error: failed to create next page url")
 func nextRecordsURL(url *urlbuilder.URL) common.NextPageFunc {
 	return func(node *ajson.Node) (string, error) {
 		// check if there is more items in the collection.
-		hasMore, err := jsonquery.New(node).Bool(hasMoreQuery, true)
+		hasMore, err := jsonquery.New(node).BoolOptional(hasMoreQuery)
 		if err != nil {
 			return "", err
 		}
@@ -66,7 +66,7 @@ Search Response schema:
 	}
 */
 func getNextRecordCursor(node *ajson.Node) (string, error) {
-	crs, err := jsonquery.New(node).Str("cursor", true)
+	crs, err := jsonquery.New(node).StringOptional("cursor")
 	if err != nil {
 		return "", err
 	}
diff --git a/providers/closecrm/write.go b/providers/closecrm/write.go
index 51fbf963..dbfc5366 100644
--- a/providers/closecrm/write.go
+++ b/providers/closecrm/write.go
@@ -48,7 +48,7 @@ func (c *Connector) Write(ctx context.Context, config common.WriteParams) (*comm
 }
 
 func constructWriteResult(node *ajson.Node) (*common.WriteResult, error) {
-	recordID, err := jsonquery.New(node).Str("id", false)
+	recordID, err := jsonquery.New(node).StringRequired("id")
 	if err != nil {
 		return nil, err
 	}
@@ -60,7 +60,7 @@ func constructWriteResult(node *ajson.Node) (*common.WriteResult, error) {
 
 	return &common.WriteResult{
 		Success:  true,
-		RecordId: *recordID,
+		RecordId: recordID,
 		Errors:   nil,
 		Data:     data,
 	}, nil
diff --git a/providers/constantcontact/write.go b/providers/constantcontact/write.go
index 539e5bee..789dff51 100644
--- a/providers/constantcontact/write.go
+++ b/providers/constantcontact/write.go
@@ -59,7 +59,7 @@ func (c *Connector) Write(ctx context.Context, config common.WriteParams) (*comm
 func constructWriteResult(config common.WriteParams, body *ajson.Node) (*common.WriteResult, error) {
 	idFieldName := objectNameToWriteResponseIdentifier.Get(config.ObjectName)
 
-	recordID, err := jsonquery.New(body).Str(idFieldName, false)
+	recordID, err := jsonquery.New(body).StringRequired(idFieldName)
 	if err != nil {
 		return nil, err
 	}
@@ -71,7 +71,7 @@ func constructWriteResult(config common.WriteParams, body *ajson.Node) (*common.
 
 	return &common.WriteResult{
 		Success:  true,
-		RecordId: *recordID,
+		RecordId: recordID,
 		Errors:   nil,
 		Data:     data,
 	}, nil
diff --git a/providers/customerapp/write.go b/providers/customerapp/write.go
index 60bbb45e..a25dd271 100644
--- a/providers/customerapp/write.go
+++ b/providers/customerapp/write.go
@@ -62,7 +62,7 @@ func (c *Connector) Write(ctx context.Context, config common.WriteParams) (*comm
 func constructWriteResult(config common.WriteParams, body *ajson.Node) (*common.WriteResult, error) {
 	fieldName := ObjectNameToWriteResponseField.Get(config.ObjectName)
 
-	nested, err := jsonquery.New(body).Object(fieldName, false)
+	nested, err := jsonquery.New(body).ObjectRequired(fieldName)
 	if err != nil {
 		return nil, err
 	}
diff --git a/providers/dynamicscrm/parse.go b/providers/dynamicscrm/parse.go
index a3e9f848..992ae6eb 100644
--- a/providers/dynamicscrm/parse.go
+++ b/providers/dynamicscrm/parse.go
@@ -6,7 +6,7 @@ import (
 )
 
 func getRecords(node *ajson.Node) ([]map[string]any, error) {
-	arr, err := jsonquery.New(node).Array("value", false)
+	arr, err := jsonquery.New(node).ArrayRequired("value")
 	if err != nil {
 		return nil, err
 	}
diff --git a/providers/gong/write.go b/providers/gong/write.go
index 334f56fd..7c6ee265 100644
--- a/providers/gong/write.go
+++ b/providers/gong/write.go
@@ -41,14 +41,14 @@ func (c *Connector) Write(ctx context.Context, config common.WriteParams) (*comm
 }
 
 func constructWriteResult(body *ajson.Node) (*common.WriteResult, error) {
-	recordID, err := jsonquery.New(body).Str("callId", false)
+	recordID, err := jsonquery.New(body).StringRequired("callId")
 	if err != nil {
 		return nil, err
 	}
 
 	return &common.WriteResult{
 		Success:  true,
-		RecordId: *recordID,
+		RecordId: recordID,
 		Errors:   nil,
 		Data:     nil,
 	}, nil
diff --git a/providers/instantly/write.go b/providers/instantly/write.go
index 6aa618a2..3c705ce1 100644
--- a/providers/instantly/write.go
+++ b/providers/instantly/write.go
@@ -110,14 +110,14 @@ func constructWriteResult(body *ajson.Node, recordIdLocation *string) (*common.W
 	}
 
 	// ID is integer that is always stored under different field name.
-	recordID, err := jsonquery.New(body).Str(*recordIdLocation, false)
+	recordID, err := jsonquery.New(body).StringRequired(*recordIdLocation)
 	if err != nil {
 		return nil, err
 	}
 
 	return &common.WriteResult{
 		Success:  true,
-		RecordId: *recordID,
+		RecordId: recordID,
 		Errors:   nil,
 		Data:     nil,
 	}, nil
diff --git a/providers/intercom/parse.go b/providers/intercom/parse.go
index c902b709..00555423 100644
--- a/providers/intercom/parse.go
+++ b/providers/intercom/parse.go
@@ -38,7 +38,7 @@ func getRecords(node *ajson.Node) ([]map[string]any, error) {
 		return nil, err
 	}
 
-	arr, err := jsonquery.New(node).Array(arrKey, false)
+	arr, err := jsonquery.New(node).ArrayRequired(arrKey)
 	if err != nil {
 		return nil, err
 	}
@@ -59,7 +59,7 @@ func makeNextRecordsURL(reqLink *urlbuilder.URL) common.NextPageFunc {
 		}
 
 		// Probably, we are dealing with an object under `pages.next`
-		startingAfter, err := jsonquery.New(node, "pages", "next").Str("starting_after", true)
+		startingAfter, err := jsonquery.New(node, "pages", "next").StringOptional("starting_after")
 		if err != nil {
 			return "", err
 		}
@@ -95,7 +95,7 @@ func extractListFieldName(node *ajson.Node) (string, error) {
 	// default field at which list is stored
 	defaultFieldName := "data"
 
-	fieldName, err := jsonquery.New(node).Str("type", true)
+	fieldName, err := jsonquery.New(node).StringOptional("type")
 	if err != nil {
 		return "", err
 	}
diff --git a/providers/iterable/write.go b/providers/iterable/write.go
index 67e27c63..6735d2da 100644
--- a/providers/iterable/write.go
+++ b/providers/iterable/write.go
@@ -138,16 +138,16 @@ func locateWriteRecordID(body *ajson.Node, objectName string) (string, error) {
 	}
 
 	// ID is integer that is always stored under different field name.
-	intIdentifier, err := jsonquery.New(body, recordIDLocation.zoom...).Integer(recordIDLocation.id, false)
+	intIdentifier, err := jsonquery.New(body, recordIDLocation.zoom...).IntegerRequired(recordIDLocation.id)
 	if err != nil {
 		return "", err
 	}
 
-	return strconv.FormatInt(*intIdentifier, 10), nil
+	return strconv.FormatInt(intIdentifier, 10), nil
 }
 
 func extractTemplateWriteRecordID(body *ajson.Node) (string, error) {
-	message, err := jsonquery.New(body).Str("msg", true)
+	message, err := jsonquery.New(body).StringOptional("msg")
 	if err != nil {
 		return "", err
 	}
diff --git a/providers/keap/parse.go b/providers/keap/parse.go
index f25c8f0e..2ab8fc9b 100644
--- a/providers/keap/parse.go
+++ b/providers/keap/parse.go
@@ -25,7 +25,7 @@ func (c *Connector) parseReadRecords(
 	ctx context.Context, config common.ReadParams, jsonPath string,
 ) common.RecordsFunc {
 	return func(node *ajson.Node) ([]map[string]any, error) {
-		arr, err := jsonquery.New(node).Array(jsonPath, true)
+		arr, err := jsonquery.New(node).ArrayOptional(jsonPath)
 		if err != nil {
 			return nil, err
 		}
diff --git a/providers/keap/write.go b/providers/keap/write.go
index 20fa86ce..cd1287fb 100644
--- a/providers/keap/write.go
+++ b/providers/keap/write.go
@@ -68,7 +68,7 @@ func constructWriteResult(
 	if config.ObjectName == objectNameFiles {
 		var err error
 		// Identifier is nested under "file_descriptor" object.
-		identifierHolder, err = jsonquery.New(body).Object("file_descriptor", false)
+		identifierHolder, err = jsonquery.New(body).ObjectRequired("file_descriptor")
 		if err != nil {
 			return nil, err
 		}
diff --git a/providers/kit/parse.go b/providers/kit/parse.go
index 0dc9baa6..675ad245 100644
--- a/providers/kit/parse.go
+++ b/providers/kit/parse.go
@@ -9,13 +9,13 @@ import (
 
 func makeNextRecordsURL(reqLink *urlbuilder.URL) common.NextPageFunc {
 	return func(node *ajson.Node) (string, error) {
-		pagination, err := jsonquery.New(node).Object("pagination", true)
+		pagination, err := jsonquery.New(node).ObjectOptional("pagination")
 		if err != nil {
 			return "", err
 		}
 
 		if pagination != nil {
-			hasNextPage, err := jsonquery.New(pagination).Bool("has_next_page", true)
+			hasNextPage, err := jsonquery.New(pagination).BoolOptional("has_next_page")
 			if err != nil {
 				return "", err
 			}
@@ -24,7 +24,7 @@ func makeNextRecordsURL(reqLink *urlbuilder.URL) common.NextPageFunc {
 				return "", nil
 			}
 
-			endCursorToken, err := jsonquery.New(pagination).Str("end_cursor", true)
+			endCursorToken, err := jsonquery.New(pagination).StringOptional("end_cursor")
 			if err != nil {
 				return "", err
 			}
diff --git a/providers/kit/write.go b/providers/kit/write.go
index 8837e2fe..d4f4b56e 100644
--- a/providers/kit/write.go
+++ b/providers/kit/write.go
@@ -56,12 +56,12 @@ func (c *Connector) Write(ctx context.Context, config common.WriteParams) (*comm
 func constructWriteResult(objName string, body *ajson.Node) (*common.WriteResult, error) {
 	obj := naming.NewSingularString(objName).String()
 
-	objectResponse, err := jsonquery.New(body).Object(obj, true)
+	objectResponse, err := jsonquery.New(body).ObjectOptional(obj)
 	if err != nil {
 		return nil, err
 	}
 
-	recordID, err := jsonquery.New(objectResponse).Integer("id", true)
+	recordID, err := jsonquery.New(objectResponse).IntegerOptional("id")
 	if err != nil {
 		return nil, err
 	}
diff --git a/providers/klaviyo/parse.go b/providers/klaviyo/parse.go
index badda859..1eab2e91 100644
--- a/providers/klaviyo/parse.go
+++ b/providers/klaviyo/parse.go
@@ -25,7 +25,7 @@ import (
 //
 // The resulting fields for the above will be: type, id, test_account, contact_information, locale, links.
 func getRecords(node *ajson.Node) ([]map[string]any, error) {
-	arr, err := jsonquery.New(node).Array("data", true)
+	arr, err := jsonquery.New(node).ArrayOptional("data")
 	if err != nil {
 		return nil, err
 	}
@@ -39,7 +39,7 @@ func flattenRecords(arr []*ajson.Node) ([]map[string]any, error) {
 	for index, element := range arr {
 		const keyAttributes = "attributes"
 
-		attributes, err := jsonquery.New(element).Object(keyAttributes, true)
+		attributes, err := jsonquery.New(element).ObjectOptional(keyAttributes)
 		if err != nil {
 			return nil, err
 		}
diff --git a/providers/klaviyo/write.go b/providers/klaviyo/write.go
index 0a4171bd..27980ab7 100644
--- a/providers/klaviyo/write.go
+++ b/providers/klaviyo/write.go
@@ -125,7 +125,7 @@ func prepareWritePayload(config common.WriteParams) (any, error) {
 }
 
 func constructWriteResult(body *ajson.Node) (*common.WriteResult, error) {
-	nested, err := jsonquery.New(body).Object("data", false)
+	nested, err := jsonquery.New(body).ObjectRequired("data")
 	if err != nil {
 		return nil, err
 	}
diff --git a/providers/marketo/parse.go b/providers/marketo/parse.go
index e208c6b8..52e6605c 100644
--- a/providers/marketo/parse.go
+++ b/providers/marketo/parse.go
@@ -27,7 +27,7 @@ func constructNextRecordsURL(object string) common.NextPageFunc {
 func constructNextPageFilteredURL(node *ajson.Node) (string, error) {
 	jsonParser := jsonquery.New(node)
 
-	data, err := jsonParser.Array("result", false)
+	data, err := jsonParser.ArrayRequired("result")
 	if err != nil {
 		return "", err
 	}
@@ -36,12 +36,12 @@ func constructNextPageFilteredURL(node *ajson.Node) (string, error) {
 	// We'd have to check for the next page records, also due deletes the is also a probability of having more records
 	// even if the size do not reach 300.
 	if len(data) > 0 {
-		lastRecordID, err := jsonquery.New(data[len(data)-1]).Integer("id", false)
+		lastRecordID, err := jsonquery.New(data[len(data)-1]).IntegerRequired("id")
 		if err != nil {
 			return "", err
 		}
 
-		return strconv.Itoa(int(*lastRecordID) + 1), nil
+		return strconv.Itoa(int(lastRecordID) + 1), nil
 	}
 
 	return "", nil
@@ -49,7 +49,7 @@ func constructNextPageFilteredURL(node *ajson.Node) (string, error) {
 
 // getRecords returns the records from the response.
 func getRecords(node *ajson.Node) ([]map[string]any, error) {
-	result, err := jsonquery.New(node).Array("result", true)
+	result, err := jsonquery.New(node).ArrayOptional("result")
 	if err != nil {
 		return nil, err
 	}
diff --git a/providers/outreach/parse.go b/providers/outreach/parse.go
index 0a15d0be..b2dba264 100644
--- a/providers/outreach/parse.go
+++ b/providers/outreach/parse.go
@@ -10,7 +10,7 @@ import (
 // getNextRecords returns the "next" url for the next page of results,
 // If available, else returns an empty string.
 func getNextRecordsURL(node *ajson.Node) (string, error) {
-	nextPageURL, err := jsonquery.New(node, "links").Str("next", true)
+	nextPageURL, err := jsonquery.New(node, "links").StringOptional("next")
 	if err != nil {
 		return "", err
 	}
diff --git a/providers/pipedrive/parse.go b/providers/pipedrive/parse.go
index f35e2e5e..c472749b 100644
--- a/providers/pipedrive/parse.go
+++ b/providers/pipedrive/parse.go
@@ -30,12 +30,12 @@ import (
 func nextRecordsURL(url *urlbuilder.URL) common.NextPageFunc {
 	return func(node *ajson.Node) (string, error) {
 		// check if there is more items in the collection.
-		more, err := jsonquery.New(node, "additional_data", "pagination").Bool("more_items_in_collection", true)
+		more, err := jsonquery.New(node, "additional_data", "pagination").BoolOptional("more_items_in_collection")
 		if err != nil {
 			return "", err
 		}
 
-		startValue, err := jsonquery.New(node, "additional_data", "pagination").Integer("next_start", true)
+		startValue, err := jsonquery.New(node, "additional_data", "pagination").IntegerOptional("next_start")
 		if err != nil {
 			return "", err
 		}
diff --git a/providers/pipeliner/parse.go b/providers/pipeliner/parse.go
index c87ff96c..31023092 100644
--- a/providers/pipeliner/parse.go
+++ b/providers/pipeliner/parse.go
@@ -6,7 +6,7 @@ import (
 )
 
 func getRecords(node *ajson.Node) ([]map[string]any, error) {
-	arr, err := jsonquery.New(node).Array("data", false)
+	arr, err := jsonquery.New(node).ArrayRequired("data")
 	if err != nil {
 		return nil, err
 	}
diff --git a/providers/pipeliner/write.go b/providers/pipeliner/write.go
index 4c5126f9..c6b1412b 100644
--- a/providers/pipeliner/write.go
+++ b/providers/pipeliner/write.go
@@ -47,12 +47,12 @@ func (c *Connector) Write(ctx context.Context, config common.WriteParams) (*comm
 }
 
 func constructWriteResult(body *ajson.Node) (*common.WriteResult, error) {
-	success, err := jsonquery.New(body).Bool("success", false)
+	success, err := jsonquery.New(body).BoolRequired("success")
 	if err != nil {
 		return nil, err
 	}
 
-	nested, err := jsonquery.New(body).Object("data", false)
+	nested, err := jsonquery.New(body).ObjectRequired("data")
 	if err != nil {
 		return nil, err
 	}
@@ -68,7 +68,7 @@ func constructWriteResult(body *ajson.Node) (*common.WriteResult, error) {
 	}
 
 	return &common.WriteResult{
-		Success:  *success,
+		Success:  success,
 		RecordId: recordID,
 		Errors:   nil,
 		Data:     data,
diff --git a/providers/salesforce/parse.go b/providers/salesforce/parse.go
index 1be9d9fd..173174be 100644
--- a/providers/salesforce/parse.go
+++ b/providers/salesforce/parse.go
@@ -7,7 +7,7 @@ import (
 
 // getRecords returns the records from the response.
 func getRecords(node *ajson.Node) ([]map[string]any, error) {
-	records, err := jsonquery.New(node).Array("records", false)
+	records, err := jsonquery.New(node).ArrayRequired("records")
 	if err != nil {
 		return nil, err
 	}
diff --git a/providers/salesforce/write.go b/providers/salesforce/write.go
index 891b9d88..100a83d1 100644
--- a/providers/salesforce/write.go
+++ b/providers/salesforce/write.go
@@ -42,7 +42,7 @@ func parseWriteResult(rsp *common.JSONHTTPResponse) (*common.WriteResult, error)
 		}, nil
 	}
 
-	recordID, err := jsonquery.New(body).Str("id", false)
+	recordID, err := jsonquery.New(body).StringRequired("id")
 	if err != nil {
 		return nil, err
 	}
@@ -52,7 +52,7 @@ func parseWriteResult(rsp *common.JSONHTTPResponse) (*common.WriteResult, error)
 		return nil, err
 	}
 
-	success, err := jsonquery.New(body).Bool("success", false)
+	success, err := jsonquery.New(body).BoolRequired("success")
 	if err != nil {
 		return nil, err
 	}
@@ -60,15 +60,15 @@ func parseWriteResult(rsp *common.JSONHTTPResponse) (*common.WriteResult, error)
 	// Salesforce does not return record data upon successful write so we do not populate
 	// the corresponding result field
 	return &common.WriteResult{
-		RecordId: *recordID,
+		RecordId: recordID,
 		Errors:   errors,
-		Success:  *success,
+		Success:  success,
 	}, nil
 }
 
 // getErrors returns the errors from the response.
 func getErrors(node *ajson.Node) ([]any, error) {
-	arr, err := jsonquery.New(node).Array("errors", true)
+	arr, err := jsonquery.New(node).ArrayOptional("errors")
 	if err != nil {
 		return nil, err
 	}
diff --git a/providers/salesloft/parse.go b/providers/salesloft/parse.go
index f7a49347..b0a75b28 100644
--- a/providers/salesloft/parse.go
+++ b/providers/salesloft/parse.go
@@ -31,7 +31,7 @@ Response example:
 	}
 */
 func getRecords(node *ajson.Node) ([]map[string]any, error) {
-	arr, err := jsonquery.New(node).Array("data", false)
+	arr, err := jsonquery.New(node).ArrayRequired("data")
 	if err != nil {
 		return nil, err
 	}
@@ -41,7 +41,7 @@ func getRecords(node *ajson.Node) ([]map[string]any, error) {
 
 func makeNextRecordsURL(reqLink *urlbuilder.URL) common.NextPageFunc {
 	return func(node *ajson.Node) (string, error) {
-		nextPageNum, err := jsonquery.New(node, "metadata", "paging").Integer("next_page", true)
+		nextPageNum, err := jsonquery.New(node, "metadata", "paging").IntegerOptional("next_page")
 		if err != nil {
 			if errors.Is(err, jsonquery.ErrKeyNotFound) {
 				// list resource doesn't support pagination, hence no next page
diff --git a/providers/salesloft/write.go b/providers/salesloft/write.go
index e4e3fd97..dbaef359 100644
--- a/providers/salesloft/write.go
+++ b/providers/salesloft/write.go
@@ -49,12 +49,12 @@ func (c *Connector) Write(ctx context.Context, config common.WriteParams) (*comm
 }
 
 func constructWriteResult(body *ajson.Node) (*common.WriteResult, error) {
-	nested, err := jsonquery.New(body).Object("data", false)
+	nested, err := jsonquery.New(body).ObjectRequired("data")
 	if err != nil {
 		return nil, err
 	}
 
-	rawID, err := jsonquery.New(nested).Integer("id", true)
+	rawID, err := jsonquery.New(nested).IntegerOptional("id")
 	if err != nil {
 		return nil, err
 	}
diff --git a/providers/smartlead/parse.go b/providers/smartlead/parse.go
index 527484fe..2b0101f6 100644
--- a/providers/smartlead/parse.go
+++ b/providers/smartlead/parse.go
@@ -6,7 +6,7 @@ import (
 )
 
 func getRecords(node *ajson.Node) ([]map[string]any, error) {
-	arr, err := jsonquery.New(node).Array("", false)
+	arr, err := jsonquery.New(node).ArrayRequired("")
 	if err != nil {
 		return nil, err
 	}
diff --git a/providers/smartlead/write.go b/providers/smartlead/write.go
index affa2983..caeff1d3 100644
--- a/providers/smartlead/write.go
+++ b/providers/smartlead/write.go
@@ -89,7 +89,7 @@ func constructURLPathUpdate(config common.WriteParams, url *urlbuilder.URL) {
 
 func constructWriteResult(body *ajson.Node, recordIdLocation string) (*common.WriteResult, error) {
 	// ID is integer that is always stored under different field name.
-	rawID, err := jsonquery.New(body).Integer(recordIdLocation, true)
+	rawID, err := jsonquery.New(body).IntegerOptional(recordIdLocation)
 	if err != nil {
 		return nil, err
 	}
diff --git a/providers/smartleadv2/handlers.go b/providers/smartleadv2/handlers.go
index 6f962df2..71101792 100644
--- a/providers/smartleadv2/handlers.go
+++ b/providers/smartleadv2/handlers.go
@@ -99,7 +99,7 @@ func (c *Connector) parseWriteResponse(
 	}
 
 	// ID is integer that is always stored under different field name.
-	rawID, err := jsonquery.New(node).Integer(idPath, true)
+	rawID, err := jsonquery.New(node).IntegerOptional(idPath)
 	if err != nil {
 		return nil, err
 	}
diff --git a/providers/stripe/parse.go b/providers/stripe/parse.go
index ada7bffd..f818aeef 100644
--- a/providers/stripe/parse.go
+++ b/providers/stripe/parse.go
@@ -25,7 +25,7 @@ func makeNextRecordsURL(url *urlbuilder.URL) common.NextPageFunc {
 			return "", nil
 		}
 
-		data, err := jsonquery.New(node).Array("data", true)
+		data, err := jsonquery.New(node).ArrayOptional("data")
 		if err != nil {
 			return "", err
 		}
@@ -36,7 +36,7 @@ func makeNextRecordsURL(url *urlbuilder.URL) common.NextPageFunc {
 
 		lastElement := data[len(data)-1]
 
-		lastItemID, err := jsonquery.New(lastElement).Str("id", true)
+		lastItemID, err := jsonquery.New(lastElement).StringOptional("id")
 		if err != nil {
 			return "", err
 		}
diff --git a/providers/stripe/write.go b/providers/stripe/write.go
index 8affa53f..406efcc1 100644
--- a/providers/stripe/write.go
+++ b/providers/stripe/write.go
@@ -54,7 +54,7 @@ func (c *Connector) Write(ctx context.Context, config common.WriteParams) (*comm
 }
 
 func constructWriteResult(node *ajson.Node) (*common.WriteResult, error) {
-	recordID, err := jsonquery.New(node).Str("id", false)
+	recordID, err := jsonquery.New(node).StringRequired("id")
 	if err != nil {
 		return nil, err
 	}
@@ -66,7 +66,7 @@ func constructWriteResult(node *ajson.Node) (*common.WriteResult, error) {
 
 	return &common.WriteResult{
 		Success:  true,
-		RecordId: *recordID,
+		RecordId: recordID,
 		Errors:   nil,
 		Data:     data,
 	}, nil
diff --git a/providers/zendesksupport/write.go b/providers/zendesksupport/write.go
index 0aceec78..65d90001 100644
--- a/providers/zendesksupport/write.go
+++ b/providers/zendesksupport/write.go
@@ -50,7 +50,7 @@ func (c *Connector) Write(ctx context.Context, config common.WriteParams) (*comm
 }
 
 func constructWriteResult(config common.WriteParams, body *ajson.Node) (*common.WriteResult, error) {
-	nested, err := jsonquery.New(body).Object(config.ObjectName, true)
+	nested, err := jsonquery.New(body).ObjectOptional(config.ObjectName)
 	if err != nil {
 		return nil, err
 	}
@@ -58,9 +58,8 @@ func constructWriteResult(config common.WriteParams, body *ajson.Node) (*common.
 	if nested == nil {
 		// Field should be in singular form. Either one will be matched.
 		// This one is NOT optional.
-		nested, err = jsonquery.New(body).Object(
+		nested, err = jsonquery.New(body).ObjectOptional(
 			naming.NewSingularString(config.ObjectName).String(),
-			false,
 		)
 		if err != nil {
 			return nil, err
@@ -68,7 +67,7 @@ func constructWriteResult(config common.WriteParams, body *ajson.Node) (*common.
 	}
 	// nested node now must be not null, carry on
 
-	rawID, err := jsonquery.New(nested).Integer("id", true)
+	rawID, err := jsonquery.New(nested).IntegerOptional("id")
 	if err != nil {
 		return nil, err
 	}
diff --git a/providers/zohocrm/parse.go b/providers/zohocrm/parse.go
index 5d9e7afc..725b2db0 100644
--- a/providers/zohocrm/parse.go
+++ b/providers/zohocrm/parse.go
@@ -29,13 +29,13 @@ import (
 
 func getNextRecordsURL(url *urlbuilder.URL) common.NextPageFunc {
 	return func(node *ajson.Node) (string, error) {
-		hasMoreRecords, err := jsonquery.New(node, "info").Bool("more_records", false)
+		hasMoreRecords, err := jsonquery.New(node, "info").BoolRequired("more_records")
 		if err != nil {
 			return "", err
 		}
 
-		if *hasMoreRecords {
-			pageToken, err := jsonquery.New(node, "info").Str("next_page_token", true)
+		if hasMoreRecords {
+			pageToken, err := jsonquery.New(node, "info").StringOptional("next_page_token")
 			if err != nil {
 				return "", err
 			}
diff --git a/providers/zoom/write.go b/providers/zoom/write.go
index c7cf2a66..c80c5edb 100644
--- a/providers/zoom/write.go
+++ b/providers/zoom/write.go
@@ -53,7 +53,7 @@ func (c *Connector) Write(ctx context.Context, config common.WriteParams) (*comm
 }
 
 func constructWriteResult(body *ajson.Node, recordIdLocation string) (*common.WriteResult, error) {
-	recordID, err := jsonquery.New(body).Str(recordIdLocation, false)
+	recordID, err := jsonquery.New(body).StringRequired(recordIdLocation)
 	if err != nil {
 		return nil, err
 	}
@@ -65,7 +65,7 @@ func constructWriteResult(body *ajson.Node, recordIdLocation string) (*common.Wr
 
 	return &common.WriteResult{
 		Success:  true,
-		RecordId: *recordID,
+		RecordId: recordID,
 		Errors:   nil,
 		Data:     data,
 	}, nil


Test patch:
diff --git a/common/jsonquery/queries_test.go b/common/jsonquery/queries_test.go
index 92d0c19b..b55558d5 100644
--- a/common/jsonquery/queries_test.go
+++ b/common/jsonquery/queries_test.go
@@ -1,3 +1,4 @@
+// nolint:dupl
 package jsonquery
 
 import (
@@ -27,15 +28,14 @@ var testJSONData = `{
 			"display_time": "yesterday"
 		}}`
 
-func TestQueryInteger(t *testing.T) { // nolint:funlen
+func TestQueryIntegerOptional(t *testing.T) { // nolint:funlen
 	t.Parallel()
 
 	j := helperCreateJSON(t, testJSONData) // nolint:varnamelen
 
 	type inType struct {
-		key      string
-		optional bool
-		zoom     []string
+		key  string
+		zoom []string
 	}
 
 	tests := []struct {
@@ -44,21 +44,13 @@ func TestQueryInteger(t *testing.T) { // nolint:funlen
 		expected    *int64
 		expectedErr error
 	}{
-		{
-			name: "Missing required integer",
-			input: inType{
-				key:      "age",
-				optional: false,
-			},
-			expectedErr: ErrKeyNotFound,
-		},
 		{
 			name: "Missing optional integer",
 			input: inType{
-				key:      "age",
-				optional: true,
+				key: "age",
 			},
-			expected: nil,
+			expected:    nil,
+			expectedErr: nil,
 		},
 		{
 			name: "Incorrect data type",
@@ -79,7 +71,8 @@ func TestQueryInteger(t *testing.T) { // nolint:funlen
 			input: inType{
 				key: "metadata",
 			},
-			expectedErr: ErrNullJSON,
+			expected:    nil,
+			expectedErr: nil,
 		},
 		{
 			name: "Valid number",
@@ -91,27 +84,24 @@ func TestQueryInteger(t *testing.T) { // nolint:funlen
 		{
 			name: "Reaching for nested integer",
 			input: inType{
-				key:      "amount",
-				optional: false,
-				zoom:     []string{"payload", "notes", "body"},
+				key:  "amount",
+				zoom: []string{"payload", "notes", "body"},
 			},
 			expected: goutils.Pointer[int64](359),
 		},
 		{
 			name: "Reaching for nested integer using self reference",
 			input: inType{
-				key:      "", // empty string acts as 'self'
-				optional: false,
-				zoom:     []string{"payload", "notes", "body", "amount"},
+				key:  "", // empty string acts as 'self'
+				zoom: []string{"payload", "notes", "body", "amount"},
 			},
 			expected: goutils.Pointer[int64](359),
 		},
 		{
 			name: "Non existent zoom path is ok for optional integer",
 			input: inType{
-				key:      "street",
-				optional: true,
-				zoom:     []string{"payload", "location", "address"},
+				key:  "street",
+				zoom: []string{"payload", "location", "address"},
 			},
 			expected: nil,
 		},
@@ -122,44 +112,123 @@ func TestQueryInteger(t *testing.T) { // nolint:funlen
 		t.Run(tt.name, func(t *testing.T) {
 			t.Parallel()
 
-			output, err := New(j, tt.input.zoom...).Integer(tt.input.key, tt.input.optional)
+			output, err := New(j, tt.input.zoom...).IntegerOptional(tt.input.key)
 			testutils.CheckOutputWithError(t, tt.name, tt.expected, tt.expectedErr, output, err)
 		})
 	}
 }
 
-func TestQueryString(t *testing.T) { // nolint:funlen
+func TestQueryIntegerRequired(t *testing.T) { // nolint:funlen
 	t.Parallel()
 
 	j := helperCreateJSON(t, testJSONData) // nolint:varnamelen
 
 	type inType struct {
-		key      string
-		optional bool
-		zoom     []string
+		key  string
+		zoom []string
 	}
 
 	tests := []struct {
 		name        string
 		input       inType
-		expected    *string
+		expected    int64
 		expectedErr error
 	}{
 		{
-			name: "Missing required string",
+			name: "Missing required integer",
 			input: inType{
-				key:      "surname",
-				optional: false,
+				key: "age",
 			},
 			expectedErr: ErrKeyNotFound,
 		},
+		{
+			name: "Incorrect data type",
+			input: inType{
+				key: "text",
+			},
+			expectedErr: ErrNotNumeric,
+		},
+		{
+			name: "Float is not integer",
+			input: inType{
+				key: "pi",
+			},
+			expectedErr: ErrNotInteger,
+		},
+		{
+			name: "Key is found but Null",
+			input: inType{
+				key: "metadata",
+			},
+			expectedErr: ErrNullJSON,
+		},
+		{
+			name: "Valid number",
+			input: inType{
+				key: "count",
+			},
+			expected: 38,
+		},
+		{
+			name: "Reaching for nested integer",
+			input: inType{
+				key:  "amount",
+				zoom: []string{"payload", "notes", "body"},
+			},
+			expected: 359,
+		},
+		{
+			name: "Reaching for nested integer using self reference",
+			input: inType{
+				key:  "", // empty string acts as 'self'
+				zoom: []string{"payload", "notes", "body", "amount"},
+			},
+			expected: 359,
+		},
+		{
+			name: "Non existent zoom path throws key not found",
+			input: inType{
+				key:  "street",
+				zoom: []string{"payload", "location", "address"},
+			},
+			expectedErr: ErrKeyNotFound,
+		},
+	}
+
+	for _, tt := range tests {
+		// nolint:varnamelen
+		t.Run(tt.name, func(t *testing.T) {
+			t.Parallel()
+
+			output, err := New(j, tt.input.zoom...).IntegerRequired(tt.input.key)
+			testutils.CheckOutputWithError(t, tt.name, tt.expected, tt.expectedErr, output, err)
+		})
+	}
+}
+
+func TestQueryStringOptional(t *testing.T) { // nolint:funlen
+	t.Parallel()
+
+	j := helperCreateJSON(t, testJSONData) // nolint:varnamelen
+
+	type inType struct {
+		key  string
+		zoom []string
+	}
+
+	tests := []struct {
+		name        string
+		input       inType
+		expected    *string
+		expectedErr error
+	}{
 		{
 			name: "Missing optional string",
 			input: inType{
-				key:      "surname",
-				optional: true,
+				key: "surname",
 			},
-			expected: nil,
+			expected:    nil,
+			expectedErr: nil,
 		},
 		{
 			name: "Incorrect data type",
@@ -173,7 +242,8 @@ func TestQueryString(t *testing.T) { // nolint:funlen
 			input: inType{
 				key: "metadata",
 			},
-			expectedErr: ErrNullJSON,
+			expected:    nil,
+			expectedErr: nil,
 		},
 		{
 			name: "Valid string",
@@ -185,27 +255,24 @@ func TestQueryString(t *testing.T) { // nolint:funlen
 		{
 			name: "Reaching for nested string",
 			input: inType{
-				key:      "text",
-				optional: false,
-				zoom:     []string{"payload", "notes", "body"},
+				key:  "text",
+				zoom: []string{"payload", "notes", "body"},
 			},
 			expected: goutils.Pointer("Some notes"),
 		},
 		{
 			name: "Reaching for nested string using self reference",
 			input: inType{
-				key:      "", // empty string acts as 'self'
-				optional: false,
-				zoom:     []string{"payload", "notes", "body", "text"},
+				key:  "", // empty string acts as 'self'
+				zoom: []string{"payload", "notes", "body", "text"},
 			},
 			expected: goutils.Pointer("Some notes"),
 		},
 		{
 			name: "Non existent zoom path is ok for optional string",
 			input: inType{
-				key:      "street",
-				optional: true,
-				zoom:     []string{"payload", "location", "address"},
+				key:  "street",
+				zoom: []string{"payload", "location", "address"},
 			},
 			expected: nil,
 		},
@@ -216,44 +283,116 @@ func TestQueryString(t *testing.T) { // nolint:funlen
 		t.Run(tt.name, func(t *testing.T) {
 			t.Parallel()
 
-			output, err := New(j, tt.input.zoom...).Str(tt.input.key, tt.input.optional)
+			output, err := New(j, tt.input.zoom...).StringOptional(tt.input.key)
 			testutils.CheckOutputWithError(t, tt.name, tt.expected, tt.expectedErr, output, err)
 		})
 	}
 }
 
-func TestQueryBool(t *testing.T) { // nolint:funlen
+func TestQueryStringRequired(t *testing.T) { // nolint:funlen
 	t.Parallel()
 
 	j := helperCreateJSON(t, testJSONData) // nolint:varnamelen
 
 	type inType struct {
-		key      string
-		optional bool
-		zoom     []string
+		key  string
+		zoom []string
 	}
 
 	tests := []struct {
 		name        string
 		input       inType
-		expected    *bool
+		expected    string
 		expectedErr error
 	}{
 		{
-			name: "Missing required bool",
+			name: "Missing required string",
 			input: inType{
-				key:      "completed",
-				optional: false,
+				key: "surname",
 			},
 			expectedErr: ErrKeyNotFound,
 		},
+		{
+			name: "Incorrect data type",
+			input: inType{
+				key: "count",
+			},
+			expectedErr: ErrNotString,
+		},
+		{
+			name: "Key is found but Null",
+			input: inType{
+				key: "metadata",
+			},
+			expectedErr: ErrNullJSON,
+		},
+		{
+			name: "Valid string",
+			input: inType{
+				key: "text",
+			},
+			expected: "Hello World",
+		},
+		{
+			name: "Reaching for nested string",
+			input: inType{
+				key:  "text",
+				zoom: []string{"payload", "notes", "body"},
+			},
+			expected: "Some notes",
+		},
+		{
+			name: "Reaching for nested string using self reference",
+			input: inType{
+				key:  "", // empty string acts as 'self'
+				zoom: []string{"payload", "notes", "body", "text"},
+			},
+			expected: "Some notes",
+		},
+		{
+			name: "Non existent zoom path throws key not found",
+			input: inType{
+				key:  "street",
+				zoom: []string{"payload", "location", "address"},
+			},
+			expectedErr: ErrKeyNotFound,
+		},
+	}
+
+	for _, tt := range tests {
+		// nolint:varnamelen
+		t.Run(tt.name, func(t *testing.T) {
+			t.Parallel()
+
+			output, err := New(j, tt.input.zoom...).StringRequired(tt.input.key)
+			testutils.CheckOutputWithError(t, tt.name, tt.expected, tt.expectedErr, output, err)
+		})
+	}
+}
+
+func TestQueryBoolOptional(t *testing.T) { // nolint:funlen
+	t.Parallel()
+
+	j := helperCreateJSON(t, testJSONData) // nolint:varnamelen
+
+	type inType struct {
+		key  string
+		zoom []string
+	}
+
+	tests := []struct {
+		name        string
+		input       inType
+		expected    *bool
+		expectedErr error
+	}{
 		{
 			name: "Missing optional bool",
 			input: inType{
-				key:      "completed",
-				optional: true,
+				key: "completed",
 			},
-			expected: nil,
+			expected:    nil,
+			expectedErr: nil,
 		},
 		{
 			name: "Incorrect data type",
@@ -267,7 +406,8 @@ func TestQueryBool(t *testing.T) { // nolint:funlen
 			input: inType{
 				key: "metadata",
 			},
-			expectedErr: ErrNullJSON,
+			expected:    nil,
+			expectedErr: nil,
 		},
 		{
 			name: "Valid Bool",
@@ -279,27 +419,24 @@ func TestQueryBool(t *testing.T) { // nolint:funlen
 		{
 			name: "Reaching for nested bool",
 			input: inType{
-				key:      "purchased",
-				optional: false,
-				zoom:     []string{"payload", "notes", "body"},
+				key:  "purchased",
+				zoom: []string{"payload", "notes", "body"},
 			},
 			expected: goutils.Pointer(true),
 		},
 		{
 			name: "Reaching for nested bool using self reference",
 			input: inType{
-				key:      "", // empty string acts as 'self'
-				optional: false,
-				zoom:     []string{"payload", "notes", "body", "purchased"},
+				key:  "", // empty string acts as 'self'
+				zoom: []string{"payload", "notes", "body", "purchased"},
 			},
 			expected: goutils.Pointer(true),
 		},
 		{
 			name: "Non existent zoom path is ok for optional bool",
 			input: inType{
-				key:      "applicable",
-				optional: true,
-				zoom:     []string{"payload", "location", "address"},
+				key:  "applicable",
+				zoom: []string{"payload", "location", "address"},
 			},
 			expected: nil,
 		},
@@ -310,13 +447,193 @@ func TestQueryBool(t *testing.T) { // nolint:funlen
 		t.Run(tt.name, func(t *testing.T) {
 			t.Parallel()
 
-			output, err := New(j, tt.input.zoom...).Bool(tt.input.key, tt.input.optional)
+			output, err := New(j, tt.input.zoom...).BoolOptional(tt.input.key)
+			testutils.CheckOutputWithError(t, tt.name, tt.expected, tt.expectedErr, output, err)
+		})
+	}
+}
+
+func TestQueryBoolRequired(t *testing.T) { // nolint:funlen
+	t.Parallel()
+
+	j := helperCreateJSON(t, testJSONData) // nolint:varnamelen
+
+	type inType struct {
+		key  string
+		zoom []string
+	}
+
+	tests := []struct {
+		name        string
+		input       inType
+		expected    bool
+		expectedErr error
+	}{
+		{
+			name: "Missing required bool",
+			input: inType{
+				key: "completed",
+			},
+			expectedErr: ErrKeyNotFound,
+		},
+		{
+			name: "Incorrect data type",
+			input: inType{
+				key: "count",
+			},
+			expectedErr: ErrNotBool,
+		},
+		{
+			name: "Key is found but Null",
+			input: inType{
+				key: "metadata",
+			},
+			expectedErr: ErrNullJSON,
+		},
+		{
+			name: "Valid Bool",
+			input: inType{
+				key: "inProgress",
+			},
+			expected: false,
+		},
+		{
+			name: "Reaching for nested bool",
+			input: inType{
+				key:  "purchased",
+				zoom: []string{"payload", "notes", "body"},
+			},
+			expected: true,
+		},
+		{
+			name: "Reaching for nested bool using self reference",
+			input: inType{
+				key:  "", // empty string acts as 'self'
+				zoom: []string{"payload", "notes", "body", "purchased"},
+			},
+			expected: true,
+		},
+		{
+			name: "Non existent zoom path throws key not found",
+			input: inType{
+				key:  "applicable",
+				zoom: []string{"payload", "location", "address"},
+			},
+			expectedErr: ErrKeyNotFound,
+		},
+	}
+
+	for _, tt := range tests {
+		// nolint:varnamelen
+		t.Run(tt.name, func(t *testing.T) {
+			t.Parallel()
+
+			output, err := New(j, tt.input.zoom...).BoolRequired(tt.input.key)
 			testutils.CheckOutputWithError(t, tt.name, tt.expected, tt.expectedErr, output, err)
 		})
 	}
 }
 
-func TestQueryArray(t *testing.T) { // nolint:funlen
+func TestQueryArrayOptional(t *testing.T) { // nolint:funlen
+	t.Parallel()
+
+	j := helperCreateJSON(t, testJSONData) // nolint:varnamelen
+
+	type inType struct {
+		key  string
+		zoom []string
+	}
+
+	tests := []struct {
+		name         string
+		input        inType
+		expectedSize int
+		expectedErr  error
+	}{
+		{
+			name: "Missing optional array",
+			input: inType{
+				key: "queue",
+			},
+			expectedSize: 0, // empty array, there is nothing
+			expectedErr:  nil,
+		},
+		{
+			name: "Key is found but Null",
+			input: inType{
+				key: "metadata",
+			},
+			expectedSize: 0, // empty array, there is nothing
+		},
+		{
+			name: "Value with wrong type",
+			input: inType{
+				key: "text",
+			},
+			expectedErr: ErrNotArray,
+		},
+		{
+			name: "Valid array",
+			input: inType{
+				key: "list",
+			},
+			expectedSize: 3,
+		},
+		{
+			name: "Empty array",
+			input: inType{
+				key: "arr",
+			},
+			expectedSize: 0,
+			expectedErr:  nil,
+		},
+		{
+			name: "Reaching for nested array",
+			input: inType{
+				key:  "nested_arr",
+				zoom: []string{"payload", "notes"},
+			},
+			expectedSize: 5,
+		},
+		{
+			name: "Reaching for nested array using self reference",
+			input: inType{
+				key:  "", // empty string acts as 'self'
+				zoom: []string{"payload", "notes", "nested_arr"},
+			},
+			expectedSize: 5,
+		},
+		{
+			name: "Non existent zoom path is ok for optional arr",
+			input: inType{
+				key:  "street",
+				zoom: []string{"payload", "location", "address"},
+			},
+			expectedSize: 0,
+			expectedErr:  nil,
+		},
+	}
+
+	for _, tt := range tests { // nolint:varnamelen
+		// nolint:varnamelen
+		t.Run(tt.name, func(t *testing.T) {
+			t.Parallel()
+
+			output, err := New(j, tt.input.zoom...).ArrayOptional(tt.input.key)
+
+			if !errors.Is(err, tt.expectedErr) {
+				t.Fatalf("%s: expected: (%v), got: (%v)", tt.name, tt.expectedErr, err)
+			}
+
+			outputSize := len(output)
+			if !reflect.DeepEqual(outputSize, tt.expectedSize) {
+				t.Fatalf("%s: expected: (%v), got: (%v)", tt.name, tt.expectedSize, outputSize)
+			}
+		})
+	}
+}
+
+func TestQueryArrayRequired(t *testing.T) { // nolint:funlen
 	t.Parallel()
 
 	j := helperCreateJSON(t, testJSONData) // nolint:varnamelen
@@ -394,14 +711,13 @@ func TestQueryArray(t *testing.T) { // nolint:funlen
 			expectedSize: 5,
 		},
 		{
-			name: "Non existent zoom path is ok for optional arr",
+			name: "Non existent zoom throws key not found",
 			input: inType{
-				key:      "street",
-				optional: true,
-				zoom:     []string{"payload", "location", "address"},
+				key:  "street",
+				zoom: []string{"payload", "location", "address"},
 			},
 			expectedSize: 0,
-			expectedErr:  nil,
+			expectedErr:  ErrKeyNotFound,
 		},
 	}
 
@@ -410,7 +726,7 @@ func TestQueryArray(t *testing.T) { // nolint:funlen
 		t.Run(tt.name, func(t *testing.T) {
 			t.Parallel()
 
-			output, err := New(j, tt.input.zoom...).Array(tt.input.key, tt.input.optional)
+			output, err := New(j, tt.input.zoom...).ArrayRequired(tt.input.key)
 
 			if !errors.Is(err, tt.expectedErr) {
 				t.Fatalf("%s: expected: (%v), got: (%v)", tt.name, tt.expectedErr, err)
@@ -424,15 +740,93 @@ func TestQueryArray(t *testing.T) { // nolint:funlen
 	}
 }
 
-func TestQueryObject(t *testing.T) { // nolint:funlen
+func TestQueryObjectOptional(t *testing.T) { // nolint:funlen
 	t.Parallel()
 
 	j := helperCreateJSON(t, testJSONData) // nolint:varnamelen
 
 	type inType struct {
-		key      string
-		optional bool
-		zoom     []string
+		key  string
+		zoom []string
+	}
+
+	tests := []struct {
+		name        string
+		input       inType
+		expectedErr error
+	}{
+		{
+			name: "Missing optional nested object",
+			input: inType{
+				key:  "random",
+				zoom: []string{"payload"},
+			},
+			expectedErr: nil, // success
+		},
+		{
+			name: "Nested key is found but Null",
+			input: inType{
+				key:  "links",
+				zoom: []string{"payload", "notes"},
+			},
+			expectedErr: nil, // success
+		},
+		{
+			name: "Invalid data type of existing key",
+			input: inType{
+				key:  "text",
+				zoom: []string{"payload", "notes", "body"},
+			},
+			expectedErr: ErrNotObject,
+		},
+		{
+			name: "Valid nested object",
+			input: inType{
+				key:  "body",
+				zoom: []string{"payload", "notes"},
+			},
+			expectedErr: nil, // success
+		},
+		{
+			name: "Valid nested object using self reference",
+			input: inType{
+				key:  "", // empty string acts as 'self'
+				zoom: []string{"payload", "notes", "body"},
+			},
+			expectedErr: nil, // success
+		},
+		{
+			name: "Non existent zoom path is ok for optional object",
+			input: inType{
+				key:  "street",
+				zoom: []string{"payload", "location", "address"},
+			},
+			expectedErr: nil, // success
+		},
+	}
+
+	for _, tt := range tests {
+		// nolint:varnamelen
+		t.Run(tt.name, func(t *testing.T) {
+			t.Parallel()
+
+			_, err := New(j, tt.input.zoom...).ObjectOptional(tt.input.key)
+
+			if !errors.Is(err, tt.expectedErr) {
+				t.Fatalf("%s: expected: (%v), got: (%v)", tt.name, tt.expectedErr, err)
+			}
+		})
+	}
+}
+
+func TestQueryObjectRequired(t *testing.T) { // nolint:funlen
+	t.Parallel()
+
+	j := helperCreateJSON(t, testJSONData) // nolint:varnamelen
+
+	type inType struct {
+		key  string
+		zoom []string
 	}
 
 	tests := []struct {
@@ -481,13 +875,12 @@ func TestQueryObject(t *testing.T) { // nolint:funlen
 			expectedErr: nil, // success
 		},
 		{
-			name: "Non existent zoom path is ok for optional object",
+			name: "Non existent zoom path throws key not found",
 			input: inType{
-				key:      "street",
-				optional: true,
-				zoom:     []string{"payload", "location", "address"},
+				key:  "street",
+				zoom: []string{"payload", "location", "address"},
 			},
-			expectedErr: nil, // success
+			expectedErr: ErrKeyNotFound,
 		},
 	}
 
@@ -496,7 +889,7 @@ func TestQueryObject(t *testing.T) { // nolint:funlen
 		t.Run(tt.name, func(t *testing.T) {
 			t.Parallel()
 
-			_, err := New(j, tt.input.zoom...).Object(tt.input.key, tt.input.optional)
+			_, err := New(j, tt.input.zoom...).ObjectRequired(tt.input.key)
 
 			if !errors.Is(err, tt.expectedErr) {
 				t.Fatalf("%s: expected: (%v), got: (%v)", tt.name, tt.expectedErr, err)
diff --git a/test/utils/searcher/search.go b/test/utils/searcher/search.go
index 68a433b7..29bf0eae 100644
--- a/test/utils/searcher/search.go
+++ b/test/utils/searcher/search.go
@@ -47,31 +47,31 @@ func Find[T any](res *common.ReadResult, keys []Key, value T) map[string]any {
 		for _, key := range keys {
 			switch key.Type {
 			case String:
-				actual, err := jsonquery.New(node).Str(key.At, false)
+				actual, err := jsonquery.New(node).StringRequired(key.At)
 				if err != nil {
 					slog.Warn("string", "error", err)
 
 					continue
 				}
 
-				if fmt.Sprintf("%v", *actual) == fmt.Sprintf("%v", value) {
+				if fmt.Sprintf("%v", actual) == fmt.Sprintf("%v", value) {
 					return data.Fields
 				}
 			case Integer:
-				actual, err := jsonquery.New(node).Integer(key.At, false)
+				actual, err := jsonquery.New(node).IntegerRequired(key.At)
 				if err != nil {
 					slog.Warn("integer", "error", err)
 
 					continue
 				}
 
-				if fmt.Sprintf("%v", *actual) == fmt.Sprintf("%v", value) {
+				if fmt.Sprintf("%v", actual) == fmt.Sprintf("%v", value) {
 					return data.Fields
 				}
 			case Array:
 				var nodes []*ajson.Node
 
-				nodes, err = jsonquery.New(node).Array(key.At, false)
+				nodes, err = jsonquery.New(node).ArrayRequired(key.At)
 				if err != nil {
 					slog.Warn("array", "error", err)
 
@@ -82,7 +82,7 @@ func Find[T any](res *common.ReadResult, keys []Key, value T) map[string]any {
 			case Object:
 				fallthrough
 			default:
-				node, err = jsonquery.New(node).Object(key.At, false)
+				node, err = jsonquery.New(node).ObjectRequired(key.At)
 				if err != nil {
 					slog.Warn("object", "error", err)