Repository: jensneuse/graphql-go-tools (go)

Issue:
add functionality to data sources to set root to a path on the response object
Consider the following data source response:

```json
{
  "apis":
    [
      {
        "slug":"/foo"
      },
      {
        "slug":"/bar"
      },
    ] 
}
```

...and the corresponding GraphQL schema:

```graphql
type Query {
  apis(page: Int! = 1): [Api]
        @HttpJsonDataSource(
            host: "https://foo.bar.com"
            url: "/api/apis?p={{ .arguments.page }}"
            path: "apis"
        )
}
```

The DataSourceDirective should contain a "path" variable to be able to set the entrypoint on the response object.

Pull request:
add path directive for query planner
This PR will add a path directive which allows the query planner to individually rewrite the path of a response object.
Closes #118 

Patch:
diff --git a/pkg/execution/a_execution-packr.go b/pkg/execution/a_execution-packr.go
index 4e1c6660..bf7e54f3 100644
--- a/pkg/execution/a_execution-packr.go
+++ b/pkg/execution/a_execution-packr.go
@@ -7,9 +7,11 @@ import "github.com/gobuffalo/packr"
 // You can use the "packr clean" command to clean up this,
 // and any other packr generated files.
 func init() {
-	_ = packr.PackJSONBytes("./graphql_definitions", "directives/graphql_datasource.graphql", "\"ZGlyZWN0aXZlIEBHcmFwaFFMRGF0YVNvdXJjZSAoCiAgICAiIiIKICAgIHRoZSBob3N0IG9mIHRoZSBvcmlnaW4KICAgICIiIgogICAgaG9zdDogU3RyaW5nIQogICAgIiIiCiAgICB0aGUgdXJsIG9mIHRoZSBvcmlnaW4KICAgICIiIgogICAgdXJsOiBTdHJpbmchCiAgICAiIiIKICAgIHRoZSBIVFRQIG1ldGhvZCB0aGUgY2xpZW50IHNob3VsZCB1c2UgZm9yIHRoZSByZXF1ZXN0LCBkZWZhdWx0cyB0byBHRVQKICAgICIiIgogICAgbWV0aG9kOiBIVFRQX01FVEhPRCA9IFBPU1QKICAgICIiIgogICAgdGhlIGZpZWxkL2VudHJ5IHBvaW50IG9uIHRoZSByZW1vdGUgc2NoZW1hCiAgICAiIiIKICAgIGZpZWxkOiBTdHJpbmcKICAgICIiIgogICAgcGFyYW1ldGVycwogICAgIiIiCiAgICBwYXJhbXM6IFtQYXJhbWV0ZXJdCikgb24gRklFTERfREVGSU5JVElPTg==\"")
+	_ = packr.PackJSONBytes("./graphql_definitions", "directives/graphql_datasource.graphql", "\"ZGlyZWN0aXZlIEBHcmFwaFFMRGF0YVNvdXJjZSAoCiAgICAiIiIKICAgIHRoZSBob3N0IG9mIHRoZSBvcmlnaW4KICAgICIiIgogICAgaG9zdDogU3RyaW5nIQogICAgIiIiCiAgICB0aGUgdXJsIG9mIHRoZSBvcmlnaW4KICAgICIiIgogICAgdXJsOiBTdHJpbmchCiAgICAiIiIKICAgIHRoZSBIVFRQIG1ldGhvZCB0aGUgY2xpZW50IHNob3VsZCB1c2UgZm9yIHRoZSByZXF1ZXN0LCBkZWZhdWx0cyB0byBHRVQKICAgICIiIgogICAgbWV0aG9kOiBIVFRQX01FVEhPRCA9IFBPU1QKICAgICIiIgogICAgcGFyYW1ldGVycwogICAgIiIiCiAgICBwYXJhbXM6IFtQYXJhbWV0ZXJdCikgb24gRklFTERfREVGSU5JVElPTg==\"")
 	_ = packr.PackJSONBytes("./graphql_definitions", "directives/http_json_datasource.graphql", "\"ZGlyZWN0aXZlIEBIdHRwSnNvbkRhdGFTb3VyY2UgKAogICAgIiIiCiAgICB0aGUgaG9zdCBvZiB0aGUgb3JpZ2luCiAgICAiIiIKICAgIGhvc3Q6IFN0cmluZyEKICAgICIiIgogICAgdGhlIHVybCBvZiB0aGUgb3JpZ2luCiAgICAiIiIKICAgIHVybDogU3RyaW5nIQogICAgIiIiCiAgICB0aGUgSFRUUCBtZXRob2QgdGhlIGNsaWVudCBzaG91bGQgdXNlIGZvciB0aGUgcmVxdWVzdCwgZGVmYXVsdHMgdG8gR0VUCiAgICAiIiIKICAgIG1ldGhvZDogSFRUUF9NRVRIT0QgPSBHRVQKICAgICIiIgogICAgcGFyYW1ldGVycwogICAgIiIiCiAgICBwYXJhbXM6IFtQYXJhbWV0ZXJdCiAgICAiIiIKICAgIGJvZHkgaXMgdGhlIG9wdGlvbmFsIGRlZmluaXRpb24gb2YgdGhlIGh0dHAgYm9keSB0byBiZSBzZW50IHRvIHRoZSB1cHN0cmVhbQogICAgZ29sYW5nIHRlbXBsYXRpbmcgc3ludGF4IG1pZ2h0IGJlIHVzZWQgdG8gY3JlYXRlIHRoZSBib2R5CiAgICAiIiIKICAgIGJvZHk6IFN0cmluZwogICAgIiIiCiAgICBoZWFkZXJzIGFyZSB0aGUga2V5IHZhbHVlIHBhaXJzIHRvIGJlIHNldCBvbiB0aGUgdXBzdHJlYW0gcmVxdWVzdAogICAgIiIiCiAgICBoZWFkZXJzOiBbSGVhZGVyXQopIG9uIEZJRUxEX0RFRklOSVRJT04=\"")
 	_ = packr.PackJSONBytes("./graphql_definitions", "directives/http_polling_stream_datasource.graphql", "\"IiIiCkh0dHBQb2xsaW5nU3RyZWFtRGF0YVNvdXJjZQoiIiIKZGlyZWN0aXZlIEBIdHRwUG9sbGluZ1N0cmVhbURhdGFTb3VyY2UgKAogICAgIiIiCiAgICBob3N0IGlzIHRoZSBob3N0IG5hbWUgb2YgdGhlIGRhdGEgc291cmNlLCBlLmcuIGV4YW1wbGUuY29tCiAgICAiIiIKICAgIGhvc3Q6IFN0cmluZyEKICAgICIiIgogICAgdXJsIGlzIHRoZSBVUkwgZnJvbSB3aGljaCB0aGUgZGF0YSBzb3VyY2Ugc2hvdWxkIHBvbGwgZGF0YSBmcm9tCiAgICB5b3UgY291bGQgdXNlIGdvbGFuZyB0ZW1wbGF0aW5nIHN5bnRheCwgZS5nLjoKICAgICcvdXNlcnMve3sgLklkIH19JyArIHBhcmFtZXRlciBuYW1lZCBpZCB3aXRoIHZhbHVlIDEgKHNlZSBiZWxvdykgd2lsbCByZW5kZXIgdG8gVVJMICcvdXNlcnMvMScKICAgICIiIgogICAgdXJsOiBTdHJpbmchCiAgICAibWV0aG9kIGlzIHRoZSBIVFRQIG1ldGhvZCB0aGUgZGF0YSBzb3VyY2Ugc2hvdWxkIHVzZSB0byBmZXRjaCBkYXRhIGZyb20gdGhlIHVwc3RyZWFtIgogICAgbWV0aG9kOiBIVFRQX01FVEhPRCA9IEdFVAogICAgInRoZSBkZWxheSBpbiBzZWNvbmRzIGJldHdlZW4gZWFjaCBwb2xsaW5nIgogICAgZGVsYXlTZWNvbmRzOiBJbnQgPSA1CiAgICAiIiIKICAgIHBhcmFtcyBhcmUgdGhlIHBhcmFtZXRlcnMgdGhhdCBzaG91bGQgZ2V0IHBhc3NlZCB0byB0aGUgZGF0YSBzb3VyY2UKICAgIHlvdSBjb3VsZCB1c2UgdGhpcyBmdW5jdGlvbiB0byBwYXNzIHZhcmlhYmxlcyBmcm9tIGZpZWxkIHZhcmlhYmxlcywgdGhlIGNvbnRleHQgb3IgcGFyZW50IG9iamVjdCB0byB0aGUgZGF0YSBzb3VyY2UKICAgIHRoc2UgY291bGQgYmUgdXNlZCB0byAncmVuZGVyJyB0aGUgdXJsIGR5bmFtaWNhbGx5CiAgICAiIiIKICAgIHBhcmFtczogW1BhcmFtZXRlcl0KKSBvbiBGSUVMRF9ERUZJTklUSU9O\"")
+	_ = packr.PackJSONBytes("./graphql_definitions", "directives/map_to.graphql", "\"ZGlyZWN0aXZlIEBtYXBUbygKICAgIG9iamVjdEZpZWxkOiBTdHJpbmchCikgb24gRklFTERfREVGSU5JVElPTg==\"")
+	_ = packr.PackJSONBytes("./graphql_definitions", "directives/path.graphql", "\"ZGlyZWN0aXZlIEBwYXRoKAogICAgIiIiCiAgICBhcHBlbmQgd2lsbCBhcHBlbmQgdGhlIGxpc3Qgb2YgcGF0aCBlbGVtZW50cyB0byB0aGUgcGF0aCBnZW5lcmF0ZWQgYnkgdGhlIHF1ZXJ5IHBsYW5uZXIKICAgICIiIgogICAgYXBwZW5kOiBbU3RyaW5nIV0KICAgICIiIgogICAgcHJlcGVuZCB3aWxsIHByZXBlbmQgdGhlIGxpc3Qgb2YgcGF0aCBlbGVtZW50cyB0byB0aGUgcGF0aCBnZW5lcmF0ZWQgYnkgdGhlIHF1ZXJ5IHBsYW5uZXIKICAgICIiIgogICAgcHJlcGVuZDogW1N0cmluZyFdCikgb24gRklFTERfREVGSU5JVElPTg==\"")
 	_ = packr.PackJSONBytes("./graphql_definitions", "directives/static_datasource.graphql", "\"ZGlyZWN0aXZlIEBTdGF0aWNEYXRhU291cmNlICgKICAgIGRhdGE6IFN0cmluZwopIG9uIEZJRUxEX0RFRklOSVRJT04=\"")
 	_ = packr.PackJSONBytes("./graphql_definitions", "enums/http_method.graphql", "\"ZW51bSBIVFRQX01FVEhPRCB7CiAgICBHRVQKICAgIFBPU1QKICAgIFVQREFURQogICAgREVMRVRFCn0=\"")
 	_ = packr.PackJSONBytes("./graphql_definitions", "enums/parameter_source.graphql", "\"IlBBUkFNRVRFUl9TT1VSQ0UgZGVmaW5lcyB0aGUgc291cmNlIG9mIGEgZHlhbm1pYyBwYXJhbWV0ZXIiCmVudW0gUEFSQU1FVEVSX1NPVVJDRSB7CiAgICAicGFyYW1ldGVyIGZyb20gdGhlIGNvbnRleHQgb2JqZWN0IHdoaWNoIGNvdWxkIGJlIHB1cHVsYXRlZCBieSB0aGUgaHR0cCB3cmFwcGVyIgogICAgQ09OVEVYVF9WQVJJQUJMRQogICAgIiIiCiAgICBwYXJhbWV0ZXIgZnJvbSB0aGUgcGFyZW50IG9iamVjdAogICAgZXhhbXBsZToKICAgIGNvbnNpZGVyIHRoZSBmb2xsb3dpbmcgcXVlcnk6CiAgICB7CiAgICAgICAgdXNlcihpZDogMSkgewogICAgICAgICAgICB1c2VySUQKICAgICAgICAgICAgZnJpZW5kcwogICAgICAgIH0KICAgIH0KICAgIFVzaW5nIHRoZSBQQVJBTUVURVJfU09VUkNFICdPQkpFQ1RfVkFSSUFCTEVfQVJHVU1FTlQnIHdpdGggc291cmNlTmFtZSAndXNlcklEJyByZXR1cm5zIHRoZSB1c2VySUQgZm9yIHVzZXIgd2l0aCBpZCAxIGF0IHJ1bnRpbWUuCiAgICBUaGlzIHdheSB5b3UgY2FuIGR5bmFtaWNhbGx5IGJ1aWxkIGEgcXVlcnkgdG8gdGhlIGZyaWVuZHMgcmVzc291cmNlIHVzaW5nIHRoZSB1c2VySUQgYXQgcnVudGltZSBhbmQgaW5qZWN0IGl0IGludG8gdGhlIHNlY29uZCBxdWVyeS4KICAgICIiIgogICAgT0JKRUNUX1ZBUklBQkxFX0FSR1VNRU5UCiAgICAiIiIKICAgIHBhcmFtZXRlciBmcm9tIHRoZSBmaWVsZCBhcmd1bWVudHMKICAgIGV4YW1wbGU6CiAgICBjb25zaWRlciB0aGUgZm9sbG93aW5nIHF1ZXJ5OgogICAgewogICAgICAgIHVzZXIoaWQ6IDEpIHsKICAgICAgICAgICAgZnJpZW5kcwogICAgICAgIH0KICAgIH0KICAgIFVzaW5nIHRoZSBQQVJBTUVURVJfU09VUkNFICdGSUVMRF9BUkdVTUVOVFMnIHdpdGggc291cmNlTmFtZSAnaWQnIHJldHVybnMgMSBhdCBydW50aW1lLgogICAgVGhpcyB3YXkgeW91IGNhbiByZW5kZXIgdGhlIHJlc3NvdXJjZSB1cmwgZm9yIC91c2VyLzppZCB0byAvdXNlci8xIGF0IHJ1bnRpbWUgZHluYW1pY2FsbHkuCiAgICAiIiIKICAgIEZJRUxEX0FSR1VNRU5UUwp9\"")
diff --git a/pkg/execution/datasource_graphql.go b/pkg/execution/datasource_graphql.go
index e1645c5b..2cb0163e 100644
--- a/pkg/execution/datasource_graphql.go
+++ b/pkg/execution/datasource_graphql.go
@@ -159,17 +159,16 @@ func (g *GraphQLDataSourcePlanner) EnterField(ref int) {
 	if g.rootFieldRef == -1 {
 		g.rootFieldRef = ref
 
-		fieldNameValue, ok := g.walker.FieldDefinitionDirectiveArgumentValueByName(ref, g.DirectiveName(), []byte("field"))
-		if !ok {
-			return
-		}
-
-		if fieldNameValue.Kind != ast.ValueKindString {
-			return
+		var fieldName []byte
+		objectName, ok := g.walker.FieldDefinitionDirectiveArgumentValueByName(ref, []byte("mapTo"), []byte("objectField"))
+		if ok && objectName.Kind == ast.ValueKindString {
+			fieldName = g.definition.StringValueContentBytes(objectName.Ref)
+		} else {
+			fieldName = g.operation.FieldNameBytes(ref)
 		}
 
 		field := ast.Field{
-			Name: g.resolveDocument.Input.AppendInputBytes(g.definition.StringValueContentBytes(fieldNameValue.Ref)),
+			Name: g.resolveDocument.Input.AppendInputBytes(fieldName),
 			Arguments: ast.ArgumentList{
 				Refs: g.rootFieldArgumentRefs,
 			},
diff --git a/pkg/execution/datasource_http_json.go b/pkg/execution/datasource_http_json.go
index fedd9814..da64b89a 100644
--- a/pkg/execution/datasource_http_json.go
+++ b/pkg/execution/datasource_http_json.go
@@ -32,7 +32,10 @@ func (h *HttpJsonDataSourcePlanner) DirectiveDefinition() []byte {
 }
 
 func (h *HttpJsonDataSourcePlanner) OverrideRootFieldPath(path []string) []string {
-	return nil
+	if len(path) == 1 {
+		return nil
+	}
+	return path
 }
 
 func (h *HttpJsonDataSourcePlanner) DirectiveName() []byte {
diff --git a/pkg/execution/fixtures/render_graphql_definitions.golden b/pkg/execution/fixtures/render_graphql_definitions.golden
index 892ca419..7c9f85aa 100644
--- a/pkg/execution/fixtures/render_graphql_definitions.golden
+++ b/pkg/execution/fixtures/render_graphql_definitions.golden
@@ -12,10 +12,6 @@ directive @GraphQLDataSource
     """
     method: HTTP_METHOD = POST 
     """
-    the field/entry point on the remote schema
-    """
-    field: String 
-    """
     parameters
     """
     params: [Parameter]
@@ -75,6 +71,21 @@ directive @HttpPollingStreamDataSource
     params: [Parameter]
  on FIELD_DEFINITION
 
+directive @mapTo
+    objectField: String!
+ on FIELD_DEFINITION
+
+directive @path
+    """
+    append will append the list of path elements to the path generated by the query planner
+    """
+    append: [String!] 
+    """
+    prepend will prepend the list of path elements to the path generated by the query planner
+    """
+    prepend: [String!]
+ on FIELD_DEFINITION
+
 directive @StaticDataSource
     data: String
  on FIELD_DEFINITION
diff --git a/pkg/execution/graphql_definitions/directives/graphql_datasource.graphql b/pkg/execution/graphql_definitions/directives/graphql_datasource.graphql
index a1c8f161..3f4cdff4 100644
--- a/pkg/execution/graphql_definitions/directives/graphql_datasource.graphql
+++ b/pkg/execution/graphql_definitions/directives/graphql_datasource.graphql
@@ -12,10 +12,6 @@ directive @GraphQLDataSource (
     """
     method: HTTP_METHOD = POST
     """
-    the field/entry point on the remote schema
-    """
-    field: String
-    """
     parameters
     """
     params: [Parameter]
diff --git a/pkg/execution/graphql_definitions/directives/map_to.graphql b/pkg/execution/graphql_definitions/directives/map_to.graphql
new file mode 100644
index 00000000..0d62603d
--- /dev/null
+++ b/pkg/execution/graphql_definitions/directives/map_to.graphql
@@ -0,0 +1,3 @@
+directive @mapTo(
+    objectField: String!
+) on FIELD_DEFINITION
\ No newline at end of file
diff --git a/pkg/execution/graphql_definitions/directives/path.graphql b/pkg/execution/graphql_definitions/directives/path.graphql
new file mode 100644
index 00000000..88dfb728
--- /dev/null
+++ b/pkg/execution/graphql_definitions/directives/path.graphql
@@ -0,0 +1,10 @@
+directive @path(
+    """
+    append will append the list of path elements to the path generated by the query planner
+    """
+    append: [String!]
+    """
+    prepend will prepend the list of path elements to the path generated by the query planner
+    """
+    prepend: [String!]
+) on FIELD_DEFINITION
\ No newline at end of file
diff --git a/pkg/execution/planning.go b/pkg/execution/planning.go
index 4ff96608..c94373eb 100644
--- a/pkg/execution/planning.go
+++ b/pkg/execution/planning.go
@@ -175,13 +175,6 @@ func (p *planningVisitor) EnterField(ref int) {
 		}
 
 		path := p.fieldPath(ref)
-		if hasResolverDefinition {
-			value, ok := p.FieldDefinitionDirectiveArgumentValueByName(ref, p.planners[len(p.planners)-1].planner.DirectiveName(), literal.FIELD)
-			if ok && value.Kind == ast.ValueKindString {
-				path[0] = p.definition.StringValueContentString(value.Ref)
-			}
-		}
-
 		if planner != nil {
 			path = planner.OverrideRootFieldPath(path)
 		}
@@ -403,13 +396,57 @@ func (p *planningVisitor) fieldPath(ref int) []string {
 		return path
 	}
 	directive, ok := p.definition.FieldDefinitionDirectiveByName(definition, []byte("mapTo"))
-	if !ok {
-		return path
+	if ok {
+		value, ok := p.definition.DirectiveArgumentValueByName(directive, []byte("objectField"))
+		if ok && value.Kind == ast.ValueKindString {
+			path[0] = p.definition.StringValueContentString(value.Ref)
+		}
 	}
-	value, ok := p.definition.DirectiveArgumentValueByName(directive, []byte("objectField"))
-	if !ok || value.Kind != ast.ValueKindString {
-		return path
+
+	var fields []int
+	for i := 0;i<len(p.Ancestors);i++{
+		if p.Ancestors[i].Kind == ast.NodeKindField {
+			fields = append(fields,p.Ancestors[i].Ref)
+		}
 	}
-	path[0] = p.definition.StringValueContentString(value.Ref)
+
+	fields = append(fields,ref)
+
+	var basePath []string
+	for _,i := range fields {
+		def, ok := p.FieldDefinition(i)
+		if !ok {
+			continue
+		}
+		pathDirective, ok := p.definition.FieldDefinitionDirectiveByName(def, []byte("path"))
+		if !ok {
+			continue
+		}
+		appendValue, ok := p.definition.DirectiveArgumentValueByName(pathDirective, []byte("append"))
+		if ok && appendValue.Kind == ast.ValueKindList {
+			for _, j := range p.definition.ListValues[appendValue.Ref].Refs {
+				listValue := p.definition.Values[j]
+				if listValue.Kind != ast.ValueKindString {
+					continue
+				}
+				basePath = append(basePath, p.definition.StringValueContentString(listValue.Ref))
+			}
+		}
+		prependValue, ok := p.definition.DirectiveArgumentValueByName(pathDirective, []byte("prepend"))
+		if ok {
+			for _, j := range p.definition.ListValues[prependValue.Ref].Refs {
+				listValue := p.definition.Values[j]
+				if listValue.Kind != ast.ValueKindString {
+					continue
+				}
+				basePath = append([]string{p.definition.StringValueContentString(listValue.Ref)}, basePath...)
+			}
+		}
+	}
+
+	if len(basePath) != 0 {
+		path = append(basePath, path...)
+	}
+
 	return path
 }


Test patch:
diff --git a/pkg/execution/execution_test.go b/pkg/execution/execution_test.go
index 7d1fecbd..c5eab653 100644
--- a/pkg/execution/execution_test.go
+++ b/pkg/execution/execution_test.go
@@ -75,7 +75,7 @@ func TestExecution(t *testing.T) {
 		case "/friends/2/pets":
 			data = yaarasPets
 		default:
-			panic(fmt.Errorf("unexpected URI: %s",r.RequestURI))
+			panic(fmt.Errorf("unexpected URI: %s", r.RequestURI))
 		}
 
 		_, err := w.Write(data)
@@ -230,7 +230,7 @@ func TestExecution(t *testing.T) {
 														Value: []byte("/graphql"),
 													},
 													&StaticVariableArgument{
-														Name: literal.QUERY,
+														Name:  literal.QUERY,
 														Value: []byte(`query q1($id: String!){userPets(id: $id){	__typename name nickname... on Dog {woof} ... on Cat {meow}}}`),
 													},
 													&ObjectVariableArgument{
@@ -293,7 +293,7 @@ func TestExecution(t *testing.T) {
 															},
 														},
 														DataSource: &HttpJsonDataSource{
-															log:zap.NewNop(),
+															log: zap.NewNop(),
 														},
 													},
 													BufferName: "pets",
@@ -663,7 +663,7 @@ func genField() Field {
 												Value: []byte("/graphql"),
 											},
 											&StaticVariableArgument{
-												Name: literal.QUERY,
+												Name:  literal.QUERY,
 												Value: []byte(`query q1($id: String!){userPets(id: $id){	__typename name nickname... on Dog {woof} ... on Cat {meow}}}`),
 											},
 											&ObjectVariableArgument{
@@ -1052,6 +1052,122 @@ func TestExecutor_ListFilterFirstN(t *testing.T) {
 	}
 }
 
+func TestExecutor_ListWithPath(t *testing.T) {
+
+	plan := &Object{
+		operationType: ast.OperationTypeQuery,
+		Fields: []Field{
+			{
+				Name: []byte("data"),
+				Value: &Object{
+					Fetch: &SingleFetch{
+						Source: &DataSourceInvocation{
+							Args: []Argument{
+								&StaticVariableArgument{
+									Value: []byte(`{"apis": [{"id": 1},{"id":2}]}`),
+								},
+							},
+							DataSource: &StaticDataSource{},
+						},
+						BufferName: "apis",
+					},
+					Fields: []Field{
+						{
+							Name:        []byte("apis"),
+							HasResolver: true,
+							Value: &List{
+								Path: []string{"apis"},
+								Value: &Object{
+									Fields: []Field{
+										{
+											Name: []byte("id"),
+											Value: &Value{
+												Path:       []string{"id"},
+												QuoteValue: false,
+											},
+										},
+									},
+								},
+							},
+						},
+					},
+				},
+			},
+		},
+	}
+
+	out := &bytes.Buffer{}
+	ex := NewExecutor()
+	ctx := Context{
+		Context: context.Background(),
+	}
+
+	_, err := ex.Execute(ctx, plan, out)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	want := `{"data":{"apis":[{"id":1},{"id":2}]}}`
+	got := out.String()
+
+	if got != want {
+		t.Fatalf("want: %s\ngot: %s\n", want, got)
+	}
+}
+
+func TestExecutor_ObjectWithPath(t *testing.T) {
+
+	plan := &Object{
+		operationType: ast.OperationTypeQuery,
+		Fields: []Field{
+			{
+				Name: []byte("data"),
+				Value: &Object{
+					Fetch: &SingleFetch{
+						Source: &DataSourceInvocation{
+							Args: []Argument{
+								&StaticVariableArgument{
+									Value: []byte(`{"api": {"id": 1}`),
+								},
+							},
+							DataSource: &StaticDataSource{},
+						},
+						BufferName: "id",
+					},
+					Fields: []Field{
+						{
+							Name:        []byte("id"),
+							HasResolver: true,
+							Value: &Value{
+								Path:       []string{"api","id"},
+								QuoteValue: false,
+							},
+						},
+					},
+				},
+			},
+		},
+	}
+
+	out := &bytes.Buffer{}
+	ex := NewExecutor()
+	ctx := Context{
+		Context: context.Background(),
+	}
+
+	_, err := ex.Execute(ctx, plan, out)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	want := `{"data":{"id":1}}`
+	got := out.String()
+
+	if got != want {
+		t.Fatalf("want: %s\ngot: %s\n", want, got)
+	}
+}
+
 func TestExecutor_ResolveArgs(t *testing.T) {
 	e := NewExecutor()
 	e.context = Context{
@@ -1063,23 +1179,23 @@ func TestExecutor_ResolveArgs(t *testing.T) {
 
 	args := []Argument{
 		&StaticVariableArgument{
-			Name: []byte("body"),
+			Name:  []byte("body"),
 			Value: []byte("{\\\"key\\\":\\\"{{ .arguments.input.foo }}\\\"}"),
 		},
 		&ContextVariableArgument{
-			Name: []byte(".arguments.input"),
+			Name:         []byte(".arguments.input"),
 			VariableName: []byte("input"),
 		},
 	}
 
-	resolved := e.ResolveArgs(args,nil)
+	resolved := e.ResolveArgs(args, nil)
 	if len(resolved) != 1 {
-		t.Fatalf("want 1, got: %d\n",len(resolved))
+		t.Fatalf("want 1, got: %d\n", len(resolved))
 		return
 	}
 	want := []byte("{\\\"key\\\":\\\"fooValue\\\"}")
-	if !bytes.Equal(resolved.ByKey([]byte("body")),want){
-		t.Fatalf("want key 'body' with value: '%s'\ndump: %s",string(want),resolved.Dump())
+	if !bytes.Equal(resolved.ByKey([]byte("body")), want) {
+		t.Fatalf("want key 'body' with value: '%s'\ndump: %s", string(want), resolved.Dump())
 	}
 }
 
@@ -1094,24 +1210,24 @@ func TestExecutor_ResolveArgsComplexPayload(t *testing.T) {
 
 	args := []Argument{
 		&StaticVariableArgument{
-			Name: []byte("body"),
+			Name:  []byte("body"),
 			Value: []byte("{{ .arguments.input }}"),
 		},
 		&ContextVariableArgument{
-			Name: []byte(".arguments.input"),
+			Name:         []byte(".arguments.input"),
 			VariableName: []byte("input"),
 		},
 	}
 
-	resolved := e.ResolveArgs(args,nil)
+	resolved := e.ResolveArgs(args, nil)
 	if len(resolved) != 1 {
-		t.Fatalf("want 1, got: %d\n",len(resolved))
+		t.Fatalf("want 1, got: %d\n", len(resolved))
 		return
 	}
 	want := `{"foo": "fooValue", "bar": {"bal": "baz"}}`
 	got := resolved.ByKey([]byte("body"))
-	if !bytes.Equal(got,[]byte(want)){
-		t.Fatalf("want key 'body' with value:\n%s\ngot:\n%s\n",want,string(got))
+	if !bytes.Equal(got, []byte(want)) {
+		t.Fatalf("want key 'body' with value:\n%s\ngot:\n%s\n", want, string(got))
 	}
 }
 
@@ -1126,23 +1242,23 @@ func TestExecutor_ResolveArgsComplexPayloadWithSelector(t *testing.T) {
 
 	args := []Argument{
 		&StaticVariableArgument{
-			Name: []byte("body"),
+			Name:  []byte("body"),
 			Value: []byte("{{ .arguments.input.bar }}"),
 		},
 		&ContextVariableArgument{
-			Name: []byte(".arguments.input"),
+			Name:         []byte(".arguments.input"),
 			VariableName: []byte("input"),
 		},
 	}
 
-	resolved := e.ResolveArgs(args,nil)
+	resolved := e.ResolveArgs(args, nil)
 	if len(resolved) != 1 {
-		t.Fatalf("want 1, got: %d\n",len(resolved))
+		t.Fatalf("want 1, got: %d\n", len(resolved))
 		return
 	}
 	want := `{"bal": "baz"}`
-	if !bytes.Equal(resolved.ByKey([]byte("body")),[]byte(want)){
-		t.Fatalf("want key 'body' with value: '%s'",want)
+	if !bytes.Equal(resolved.ByKey([]byte("body")), []byte(want)) {
+		t.Fatalf("want key 'body' with value: '%s'", want)
 	}
 }
 
@@ -1157,35 +1273,35 @@ func TestExecutor_ResolveArgsWithListArguments(t *testing.T) {
 			Name: []byte("headers"),
 			Arguments: []Argument{
 				&StaticVariableArgument{
-					Name: []byte("foo"),
+					Name:  []byte("foo"),
 					Value: []byte("fooVal"),
 				},
 				&StaticVariableArgument{
-					Name: []byte("bar"),
+					Name:  []byte("bar"),
 					Value: []byte("barVal"),
 				},
 			},
 		},
 	}
 
-	resolved := e.ResolveArgs(args,nil)
+	resolved := e.ResolveArgs(args, nil)
 	if len(resolved) != 1 {
-		t.Fatalf("want 1, got: %d\n",len(resolved))
+		t.Fatalf("want 1, got: %d\n", len(resolved))
 		return
 	}
 	want := "{\"bar\":\"barVal\",\"foo\":\"fooVal\"}"
 	got := string(resolved.ByKey([]byte("headers")))
 	if want != got {
-		t.Fatalf("want key 'headers' with value:\n%s, got:\n%s\ndump:\n%s\n",want,got,resolved.Dump())
+		t.Fatalf("want key 'headers' with value:\n%s, got:\n%s\ndump:\n%s\n", want, got, resolved.Dump())
 	}
 }
 
-func TestExecutor_HTTPJSONDataSourceWithBody(t *testing.T){
+func TestExecutor_HTTPJSONDataSourceWithBody(t *testing.T) {
 
 	wantUpstream := map[string]interface{}{
 		"key": "fooValue",
 	}
-	wantBytes,err := json.MarshalIndent(wantUpstream,"","  ")
+	wantBytes, err := json.MarshalIndent(wantUpstream, "", "  ")
 	if err != nil {
 		t.Fatal(err)
 		return
@@ -1196,11 +1312,11 @@ func TestExecutor_HTTPJSONDataSourceWithBody(t *testing.T){
 	REST1 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 
 		if r.Method != http.MethodPost {
-			t.Fatalf("wantUpstream: %s, got: %s\n",http.MethodPost,r.Method)
+			t.Fatalf("wantUpstream: %s, got: %s\n", http.MethodPost, r.Method)
 			return
 		}
 
-		data,err := ioutil.ReadAll(r.Body)
+		data, err := ioutil.ReadAll(r.Body)
 		if err != nil {
 			t.Fatal(err)
 			return
@@ -1213,16 +1329,16 @@ func TestExecutor_HTTPJSONDataSourceWithBody(t *testing.T){
 		gotString := prettyJSON(bytes.NewReader(data))
 
 		if wantString != gotString {
-			t.Fatalf("wantUpstream: %s\ngot: %s\n",wantString,gotString)
+			t.Fatalf("wantUpstream: %s\ngot: %s\n", wantString, gotString)
 			return
 		}
 
 		w.WriteHeader(http.StatusOK)
-		_,_ = w.Write([]byte("bar"))
+		_, _ = w.Write([]byte("bar"))
 	}))
 
 	plan := &Object{
-		operationType:ast.OperationTypeQuery,
+		operationType: ast.OperationTypeQuery,
 		Fields: []Field{
 			{
 				Name: []byte("data"),
@@ -1230,8 +1346,8 @@ func TestExecutor_HTTPJSONDataSourceWithBody(t *testing.T){
 					Fetch: &SingleFetch{
 						BufferName: "withBody",
 						Source: &DataSourceInvocation{
-							DataSource:&HttpJsonDataSource{
-								log:zap.NewNop(),
+							DataSource: &HttpJsonDataSource{
+								log: zap.NewNop(),
 							},
 							Args: []Argument{
 								&StaticVariableArgument{
@@ -1247,11 +1363,11 @@ func TestExecutor_HTTPJSONDataSourceWithBody(t *testing.T){
 									Value: []byte("POST"),
 								},
 								&StaticVariableArgument{
-									Name: []byte("body"),
+									Name:  []byte("body"),
 									Value: []byte("{\\\"key\\\":\\\"{{ .arguments.input.foo }}\\\"}"),
 								},
 								&ContextVariableArgument{
-									Name: []byte(".arguments.input"),
+									Name:         []byte(".arguments.input"),
 									VariableName: []byte("input"),
 								},
 							},
@@ -1259,10 +1375,10 @@ func TestExecutor_HTTPJSONDataSourceWithBody(t *testing.T){
 					},
 					Fields: []Field{
 						{
-							Name: []byte("withBody"),
-							HasResolver:true,
+							Name:        []byte("withBody"),
+							HasResolver: true,
 							Value: &Value{
-								QuoteValue:true,
+								QuoteValue: true,
 							},
 						},
 					},
@@ -1291,7 +1407,7 @@ func TestExecutor_HTTPJSONDataSourceWithBody(t *testing.T){
 		},
 	}
 
-	wantResult,err := json.MarshalIndent(expected,"","  ")
+	wantResult, err := json.MarshalIndent(expected, "", "  ")
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -1300,12 +1416,12 @@ func TestExecutor_HTTPJSONDataSourceWithBody(t *testing.T){
 	got := prettyJSON(out)
 
 	if want != got {
-		t.Fatalf("want: %s\ngot: %s\n",want,got)
+		t.Fatalf("want: %s\ngot: %s\n", want, got)
 		return
 	}
 }
 
-func TestExecutor_HTTPJSONDataSourceWithBodyComplexPlayload(t *testing.T){
+func TestExecutor_HTTPJSONDataSourceWithBodyComplexPlayload(t *testing.T) {
 
 	wantUpstream := map[string]interface{}{
 		"foo": "fooValue",
@@ -1314,7 +1430,7 @@ func TestExecutor_HTTPJSONDataSourceWithBodyComplexPlayload(t *testing.T){
 		},
 	}
 
-	wantBytes,err := json.MarshalIndent(wantUpstream,"","  ")
+	wantBytes, err := json.MarshalIndent(wantUpstream, "", "  ")
 	if err != nil {
 		t.Fatal(err)
 		return
@@ -1325,11 +1441,11 @@ func TestExecutor_HTTPJSONDataSourceWithBodyComplexPlayload(t *testing.T){
 	REST1 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 
 		if r.Method != http.MethodPost {
-			t.Fatalf("wantUpstream: %s, got: %s\n",http.MethodPost,r.Method)
+			t.Fatalf("wantUpstream: %s, got: %s\n", http.MethodPost, r.Method)
 			return
 		}
 
-		data,err := ioutil.ReadAll(r.Body)
+		data, err := ioutil.ReadAll(r.Body)
 		if err != nil {
 			t.Fatal(err)
 			return
@@ -1339,16 +1455,16 @@ func TestExecutor_HTTPJSONDataSourceWithBodyComplexPlayload(t *testing.T){
 		gotString := prettyJSON(bytes.NewReader(data))
 
 		if wantString != gotString {
-			t.Fatalf("wantUpstream: %s\ngot: %s\n",wantString,gotString)
+			t.Fatalf("wantUpstream: %s\ngot: %s\n", wantString, gotString)
 			return
 		}
 
 		w.WriteHeader(http.StatusOK)
-		_,_ = w.Write([]byte("bar"))
+		_, _ = w.Write([]byte("bar"))
 	}))
 
 	plan := &Object{
-		operationType:ast.OperationTypeQuery,
+		operationType: ast.OperationTypeQuery,
 		Fields: []Field{
 			{
 				Name: []byte("data"),
@@ -1356,8 +1472,8 @@ func TestExecutor_HTTPJSONDataSourceWithBodyComplexPlayload(t *testing.T){
 					Fetch: &SingleFetch{
 						BufferName: "withBody",
 						Source: &DataSourceInvocation{
-							DataSource:&HttpJsonDataSource{
-								log:zap.NewNop(),
+							DataSource: &HttpJsonDataSource{
+								log: zap.NewNop(),
 							},
 							Args: []Argument{
 								&StaticVariableArgument{
@@ -1373,11 +1489,11 @@ func TestExecutor_HTTPJSONDataSourceWithBodyComplexPlayload(t *testing.T){
 									Value: []byte("POST"),
 								},
 								&StaticVariableArgument{
-									Name: []byte("body"),
+									Name:  []byte("body"),
 									Value: []byte("{{ .arguments.input }}"),
 								},
 								&ContextVariableArgument{
-									Name: []byte(".arguments.input"),
+									Name:         []byte(".arguments.input"),
 									VariableName: []byte("input"),
 								},
 							},
@@ -1385,10 +1501,10 @@ func TestExecutor_HTTPJSONDataSourceWithBodyComplexPlayload(t *testing.T){
 					},
 					Fields: []Field{
 						{
-							Name: []byte("withBody"),
-							HasResolver:true,
+							Name:        []byte("withBody"),
+							HasResolver: true,
 							Value: &Value{
-								QuoteValue:true,
+								QuoteValue: true,
 							},
 						},
 					},
@@ -1417,7 +1533,7 @@ func TestExecutor_HTTPJSONDataSourceWithBodyComplexPlayload(t *testing.T){
 		},
 	}
 
-	wantResult,err := json.MarshalIndent(expected,"","  ")
+	wantResult, err := json.MarshalIndent(expected, "", "  ")
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -1426,31 +1542,31 @@ func TestExecutor_HTTPJSONDataSourceWithBodyComplexPlayload(t *testing.T){
 	got := prettyJSON(out)
 
 	if want != got {
-		t.Fatalf("want: %s\ngot: %s\n",want,got)
+		t.Fatalf("want: %s\ngot: %s\n", want, got)
 		return
 	}
 }
 
-func TestExecutor_HTTPJSONDataSourceWithHeaders(t *testing.T){
+func TestExecutor_HTTPJSONDataSourceWithHeaders(t *testing.T) {
 
 	REST1 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 
-		for k,v := range map[string]string{
+		for k, v := range map[string]string{
 			"foo": "fooVal",
 			"bar": "barVal",
-		}{
+		} {
 			got := r.Header.Get(k)
 			if got != v {
-				t.Fatalf("want header with key '%s' and value '%s', got: '%s'",k,v,got)
+				t.Fatalf("want header with key '%s' and value '%s', got: '%s'", k, v, got)
 			}
 		}
 
 		w.WriteHeader(http.StatusOK)
-		_,_ = w.Write([]byte("bar"))
+		_, _ = w.Write([]byte("bar"))
 	}))
 
 	plan := &Object{
-		operationType:ast.OperationTypeQuery,
+		operationType: ast.OperationTypeQuery,
 		Fields: []Field{
 			{
 				Name: []byte("data"),
@@ -1458,8 +1574,8 @@ func TestExecutor_HTTPJSONDataSourceWithHeaders(t *testing.T){
 					Fetch: &SingleFetch{
 						BufferName: "withHeaders",
 						Source: &DataSourceInvocation{
-							DataSource:&HttpJsonDataSource{
-								log:zap.NewNop(),
+							DataSource: &HttpJsonDataSource{
+								log: zap.NewNop(),
 							},
 							Args: []Argument{
 								&StaticVariableArgument{
@@ -1475,7 +1591,7 @@ func TestExecutor_HTTPJSONDataSourceWithHeaders(t *testing.T){
 									Value: []byte("GET"),
 								},
 								&ListArgument{
-									Name:      []byte("headers"),
+									Name: []byte("headers"),
 									Arguments: []Argument{
 										&StaticVariableArgument{
 											Name:  []byte("foo"),
@@ -1492,10 +1608,10 @@ func TestExecutor_HTTPJSONDataSourceWithHeaders(t *testing.T){
 					},
 					Fields: []Field{
 						{
-							Name: []byte("withHeaders"),
-							HasResolver:true,
+							Name:        []byte("withHeaders"),
+							HasResolver: true,
 							Value: &Value{
-								QuoteValue:true,
+								QuoteValue: true,
 							},
 						},
 					},
@@ -1521,7 +1637,7 @@ func TestExecutor_HTTPJSONDataSourceWithHeaders(t *testing.T){
 		},
 	}
 
-	wantResult,err := json.MarshalIndent(expected,"","  ")
+	wantResult, err := json.MarshalIndent(expected, "", "  ")
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -1530,7 +1646,7 @@ func TestExecutor_HTTPJSONDataSourceWithHeaders(t *testing.T){
 	got := prettyJSON(out)
 
 	if want != got {
-		t.Fatalf("want: %s\ngot: %s\n",want,got)
+		t.Fatalf("want: %s\ngot: %s\n", want, got)
 		return
 	}
 }
@@ -1541,9 +1657,9 @@ func prettyJSON(r io.Reader) string {
 	if err != nil {
 		panic(err)
 	}
-	out,err := json.MarshalIndent(data,"","  ")
+	out, err := json.MarshalIndent(data, "", "  ")
 	if err != nil {
 		panic(err)
 	}
 	return string(out)
-}
\ No newline at end of file
+}
diff --git a/pkg/execution/planning_test.go b/pkg/execution/planning_test.go
index 53028ce4..065fdf98 100644
--- a/pkg/execution/planning_test.go
+++ b/pkg/execution/planning_test.go
@@ -411,15 +411,15 @@ func TestPlanner_Plan(t *testing.T) {
 					`,
 		ResolverDefinitions{
 			{
-				TypeName:literal.QUERY,
-				FieldName:[]byte("withBody"),
+				TypeName:  literal.QUERY,
+				FieldName: []byte("withBody"),
 				DataSourcePlannerFactory: func() DataSourcePlanner {
 					return &HttpJsonDataSourcePlanner{}
 				},
 			},
 		},
 		&Object{
-			operationType:ast.OperationTypeQuery,
+			operationType: ast.OperationTypeQuery,
 			Fields: []Field{
 				{
 					Name: []byte("data"),
@@ -427,7 +427,7 @@ func TestPlanner_Plan(t *testing.T) {
 						Fetch: &SingleFetch{
 							BufferName: "withBody",
 							Source: &DataSourceInvocation{
-								DataSource:&HttpJsonDataSource{},
+								DataSource: &HttpJsonDataSource{},
 								Args: []Argument{
 									&StaticVariableArgument{
 										Name:  []byte("host"),
@@ -442,11 +442,11 @@ func TestPlanner_Plan(t *testing.T) {
 										Value: []byte("POST"),
 									},
 									&StaticVariableArgument{
-										Name: []byte("body"),
+										Name:  []byte("body"),
 										Value: []byte("{\\\"key\\\":\\\"{{ .arguments.input.foo }}\\\"}"),
 									},
 									&ContextVariableArgument{
-										Name: []byte(".arguments.input"),
+										Name:         []byte(".arguments.input"),
 										VariableName: []byte("input"),
 									},
 								},
@@ -454,10 +454,64 @@ func TestPlanner_Plan(t *testing.T) {
 						},
 						Fields: []Field{
 							{
-								Name: []byte("withBody"),
-								HasResolver:true,
+								Name:        []byte("withBody"),
+								HasResolver: true,
 								Value: &Value{
-									QuoteValue:true,
+									QuoteValue: true,
+								},
+							},
+						},
+					},
+				},
+			},
+		}))
+	t.Run("HTTPJSONDataSource withPath", run(withBaseSchema(HTTPJSONDataSourceSchema), `
+					query WithPath {
+						withPath
+					}
+					`,
+		ResolverDefinitions{
+			{
+				TypeName:  literal.QUERY,
+				FieldName: []byte("withPath"),
+				DataSourcePlannerFactory: func() DataSourcePlanner {
+					return &HttpJsonDataSourcePlanner{}
+				},
+			},
+		},
+		&Object{
+			operationType: ast.OperationTypeQuery,
+			Fields: []Field{
+				{
+					Name: []byte("data"),
+					Value: &Object{
+						Fetch: &SingleFetch{
+							BufferName: "withPath",
+							Source: &DataSourceInvocation{
+								DataSource: &HttpJsonDataSource{},
+								Args: []Argument{
+									&StaticVariableArgument{
+										Name:  []byte("host"),
+										Value: []byte("httpbin.org"),
+									},
+									&StaticVariableArgument{
+										Name:  []byte("url"),
+										Value: []byte("/anything"),
+									},
+									&StaticVariableArgument{
+										Name:  []byte("method"),
+										Value: []byte("GET"),
+									},
+								},
+							},
+						},
+						Fields: []Field{
+							{
+								Name: []byte("withPath"),
+								HasResolver: true,
+								Value: &Value{
+									Path:       []string{"subObject","withPath"},
+									QuoteValue: true,
 								},
 							},
 						},
@@ -472,15 +526,15 @@ func TestPlanner_Plan(t *testing.T) {
 					`,
 		ResolverDefinitions{
 			{
-				TypeName:literal.QUERY,
-				FieldName:[]byte("withHeaders"),
+				TypeName:  literal.QUERY,
+				FieldName: []byte("withHeaders"),
 				DataSourcePlannerFactory: func() DataSourcePlanner {
 					return &HttpJsonDataSourcePlanner{}
 				},
 			},
 		},
 		&Object{
-			operationType:ast.OperationTypeQuery,
+			operationType: ast.OperationTypeQuery,
 			Fields: []Field{
 				{
 					Name: []byte("data"),
@@ -488,7 +542,7 @@ func TestPlanner_Plan(t *testing.T) {
 						Fetch: &SingleFetch{
 							BufferName: "withHeaders",
 							Source: &DataSourceInvocation{
-								DataSource:&HttpJsonDataSource{},
+								DataSource: &HttpJsonDataSource{},
 								Args: []Argument{
 									&StaticVariableArgument{
 										Name:  []byte("host"),
@@ -520,10 +574,10 @@ func TestPlanner_Plan(t *testing.T) {
 						},
 						Fields: []Field{
 							{
-								Name: []byte("withHeaders"),
-								HasResolver:true,
+								Name:        []byte("withHeaders"),
+								HasResolver: true,
 								Value: &Value{
-									QuoteValue:true,
+									QuoteValue: true,
 								},
 							},
 						},
@@ -2278,8 +2332,7 @@ const GraphQLDataSourceSchema = `
 directive @GraphQLDataSource (
     host: String!
     url: String!
-	field: String
-    method: HTTP_METHOD = POST
+	method: HTTP_METHOD = POST
     params: [Parameter]
 ) on FIELD_DEFINITION
 
@@ -2342,7 +2395,6 @@ type Query {
 		@GraphQLDataSource(
 			host: "countries.trevorblades.com"
 			url: "/"
-			field: "country"
 			params: [
 				{
 					name: "code"
@@ -2359,7 +2411,6 @@ type Mutation {
 		@GraphQLDataSource(
 			host: "fakebook.com"
 			url: "/"
-			field: "likePost"
 			params: [
 				{
 					name: "id"
@@ -2390,6 +2441,8 @@ directive @mapTo(
 	objectField: String!
 ) on FIELD_DEFINITION
 
+directive @path(append: [String!], prepend: [String!]) on FIELD_DEFINITION
+
 enum HTTP_METHOD {
     GET
     POST
@@ -2490,6 +2543,12 @@ type Query {
 				}
 			]
         )
+	withPath: String!
+		@path(prepend: ["subObject"])
+		@HttpJsonDataSource(
+            host: "httpbin.org"
+            url: "/anything"
+        )
     __schema: __Schema!
     __type(name: String!): __Type
 }
@@ -2535,8 +2594,7 @@ directive @HttpJsonDataSource (
 directive @GraphQLDataSource (
     host: String!
     url: String!
-	field: String
-    method: HTTP_METHOD = POST
+	method: HTTP_METHOD = POST
     params: [Parameter]
 ) on FIELD_DEFINITION
 
@@ -2613,7 +2671,6 @@ type Query {
 		@GraphQLDataSource(
 			host: "localhost:8001"
 			url: "/graphql"
-			field: "user"
 			params: [
 				{
 					name: "id"
@@ -2647,10 +2704,12 @@ type User {
 			]
 		)
 	pets: [Pet]
+		@mapTo(
+			objectField: "userPets"
+		)
 		@GraphQLDataSource(
 			host: "localhost:8002"
 			url: "/graphql"
-			field: "userPets"
 			params: [
 				{
 					name: "userId"