Graphql: Question: How to read the requested fields in Resolve

Created on 21 Apr 2016  路  7Comments  路  Source: graphql-go/graphql

I hope this question makes sense, I'm new to graphql...
Anyway, is there a way in the Resolve func to access the list of fiends that the user requested?

Example:

fields := graphql.Fields{
    "companies": &graphql.Field{
        Type:        graphql.NewList(...),
        Resolve: func(p graphql.ResolveParams) (interface{}, error) {
                   // This func is going to access the database and run a select query
                   // I'd like to select only the fields that the user actually requested in the query so not to waste DB resources and bandwidth. Only those fields are going to be returned to the user anyway, so no need to ask for *, just the requested fields.
                   // Is there a way to get the list of requested fields from ResolveParams or somewhere else? 
        },
    },
}
documentation question

Most helpful comment

To answer my own question, here's how I solved this:

Assume you have the query:

query {
  companies {
    id,
    name
  }
}

And you want to know whether the companies fields contain id or name or other fields.
Use the following func

func getSelectedFields(selectionPath []string,
    resolveParams graphql.ResolveParams) []string {
    fields := resolveParams.Info.FieldASTs
    for _, propName := range selectionPath {
        found := false
        for _, field := range fields {
            if field.Name.Value == propName {
                selections := field.SelectionSet.Selections
                fields = make([]*ast.Field, 0)
                for _, selection := range selections {
                    fields = append(fields, selection.(*ast.Field))
                }
                found = true
                break
            }
        }
        if !found {
            return []string{}
        }
    }
    var collect []string
    for _, field := range fields {
        collect = append(collect, field.Name.Value)
    }
    return collect
}

And invoke it like this:

getSelectedFields([]string{"companies"}, resolveParams) 
// this will return []string{"id", "name"}

In case you have a "path" you want to select from, e.g.

query {
  a {
    b {
      x,
      y,
      z
    }
  }
}

Then you'd call it like this:

getSelectedFields([]string{"a", "b"}, resolveParams)
// Returns []string{"x", "y", "z"}

For context, this problem had also been discussed here: https://github.com/graphql/graphql-js/issues/19 as well as mentioned here http://pcarion.com/2015/09/26/graphql-resolve/

All 7 comments

look at https://godoc.org/github.com/graphql-go/graphql#ResolveParams

you can access graphql query variables through Args which is map[string]interface{}, keyed with parameter name.

Thanks @bsr203 but unfortunately this isn't what I'm looking for.
Yes, I can access the query variables, but what I want is to access the list of requested fields (it's the first time I use graphql so I hope my terminology is right...)

So if I had a query:

query {
  companies(arg1: 5.4) {
    id,
    name
  }
}

What you're suggesting is being able to access arg1, which indeed is required.
But what I'm asking, on top of that, is being able to access the list of requested fields, e.g. I want to know that the user asked for the fields id and name but did not ask for other fields.
My motivation is that I'm sending a query to a database and in this query I request a list of fields. I want to know what fields to include in the select statement.

To answer my own question, here's how I solved this:

Assume you have the query:

query {
  companies {
    id,
    name
  }
}

And you want to know whether the companies fields contain id or name or other fields.
Use the following func

func getSelectedFields(selectionPath []string,
    resolveParams graphql.ResolveParams) []string {
    fields := resolveParams.Info.FieldASTs
    for _, propName := range selectionPath {
        found := false
        for _, field := range fields {
            if field.Name.Value == propName {
                selections := field.SelectionSet.Selections
                fields = make([]*ast.Field, 0)
                for _, selection := range selections {
                    fields = append(fields, selection.(*ast.Field))
                }
                found = true
                break
            }
        }
        if !found {
            return []string{}
        }
    }
    var collect []string
    for _, field := range fields {
        collect = append(collect, field.Name.Value)
    }
    return collect
}

And invoke it like this:

getSelectedFields([]string{"companies"}, resolveParams) 
// this will return []string{"id", "name"}

In case you have a "path" you want to select from, e.g.

query {
  a {
    b {
      x,
      y,
      z
    }
  }
}

Then you'd call it like this:

getSelectedFields([]string{"a", "b"}, resolveParams)
// Returns []string{"x", "y", "z"}

For context, this problem had also been discussed here: https://github.com/graphql/graphql-js/issues/19 as well as mentioned here http://pcarion.com/2015/09/26/graphql-resolve/

thank you for posting the solution. I too may want to use it one day, but not there yet. cheers.

Thanks! I'm going to use this solution. But I still think the library should provide a straightforward way to access this.

Just an FYI for anyone reading this, I used the same solution, and it breaks if the client uses fragments:

[interface conversion: ast.Selection is *ast.FragmentSpread, not *ast.Field]

I'll post here when I find a solution to this.

I've posted my modified solution here, which works for queries that include fragments: https://github.com/graphql-go/graphql/issues/157#issuecomment-302868300

Was this page helpful?
0 / 5 - 0 ratings

Related issues

amedeiros picture amedeiros  路  3Comments

FrontMage picture FrontMage  路  5Comments

schaze picture schaze  路  4Comments

michelalbers picture michelalbers  路  3Comments

conord33 picture conord33  路  6Comments