Gqlgen: CollectAllFields doesn't include nested fields

Created on 14 Jun 2019  路  14Comments  路  Source: 99designs/gqlgen

What happened?

This is my code ...

    requested := graphql.CollectAllFields(ctx)
    fmt.Printf("\nRequested Fields:   %v\n", requested)

This is the graphql request ...

{user(id:"1") {name, nickname}, cars{registration}}

Just the top level fields were returned ...

Requested Fields:   [name nickname]

What did you expect?

That even the nested fields would be returned

Requested Fields: name, nickname, cars, registration

Minimal graphql.schema and models to reproduce

type User {
  id: ID
  name: String!
  nickname: String
  cars: [Car!]
}

type Car {
  id: ID
  registration: String!
}

versions

  • gqlgen version? dev
  • go version? g01.11.10
  • dep or go modules? dep
stale

Most helpful comment

Thank you, @RichardLindhout. That's helpful.

All 14 comments

This is intentional, as it is a convenience wrapper around CollectFieldsCtx to account for the most common case, where you just want to limit querying your data source by the actual fields requested. If you have a more complex use-case, use that instead.

See also our field collection docs.

@mathewbyrne,

Thank you for your reply. Using my graphql request (above) and CollectFieldsCtx, I still can't see cars.registration getting returned. (I still only see user.name and user.nickname.) It still seems like those Collect functions don't look inside the nested part of the graphql request.

Maybe I'm missing something.

E.g. If I have ...

    requested := []string{}
    satisfiesFields := graphql.CollectFieldsCtx(ctx, []string{"registration"})
    fmt.Printf("\nSatisfies Fields:   \n")
    for _, f := range satisfiesFields {
        requested = append(requested, f.Name)
        fmt.Printf("   %v\n", f.Name)
    }

I get ...

Satisfies Fields:   
   name
   nickname

Also, if I replace "registration", with "car", "Car, "cars" or "Cars", I still get that result.

Can you give me a concrete example of what I would need to do to get the car.registration field included in the results?

Thank you for your help.

I have the same use-case.

My query:

 flowBlocks {
            id
            block {
              id
              title
              type
              choices {
                id
                title
                description
                slug
              }
            }
          }
        }

Code

    requested := graphql.CollectFieldsCtx(ctx, []string{"block", "choices"})
    for _, column := range requested {
        r.l.Debug(column.Name)
    }

Response

2019-07-18T20:49:09.829+0200    DEBUG   backend/resolver.go:71  id
2019-07-18T20:49:09.830+0200    DEBUG   backend/resolver.go:71  block

Regardless of what I put in the satisfies argument I only got id and block

CollectAllFields the same problem. Looks like nested fields are not available in the SelectionSet of the param

@Baggerone After spitting through source got something working now

fields := graphql.CollectFieldsCtx(ctx, []string{"Block"})
    for _, column := range fields {
        r.l.Debug(column.Alias)
        fields2 := graphql.CollectFields(reqCtx, column.SelectionSet, []string{"BlockChoices"})
        for _, c := range fields2 {
            r.l.Debug(c.Alias)
        }
        // graphql.CollectFields(ctx, []string{"Block"})
        // for _, c := range column.SelectionSet {
        //  r.l.Debug(c.GetPosition().Src.Name)
        // }

    }

I've created some nice helpers which get's al the request fields

_Updated example on 11 May 2020: New PR for:
Also look at for most recent example: https://gqlgen.com/reference/field-collection/_

query {
  flowBlocks {
    id
    block {
      id
      title
      type
      choices {
        id
        title
        description
        slug
      }
    }
  }
}

We don't want to overfetch our database so we want to know which field are requested.
Here is an example which get's all requested field as convenient string slice, which can be easily checked.

func GetPreloads(ctx context.Context) []string {
    return GetNestedPreloads(
        graphql.GetOperationContext(ctx),
        graphql.CollectFieldsCtx(ctx, nil),
        "",
    )
}

func GetNestedPreloads(ctx *graphql.OperationContext, fields []graphql.CollectedField, prefix string) (preloads []string) {
    for _, column := range fields {
        prefixColumn := GetPreloadString(prefix, column.Name)
        preloads = append(preloads, prefixColumn)
        preloads = append(preloads, GetNestedPreloads(ctx, graphql.CollectFields(ctx, column.Selections, nil), prefixColumn)...)
    }
    return
}

func GetPreloadString(prefix, name string) string {
    if len(prefix) > 0 {
        return prefix + "." + name
    }
    return name
}

So if we call these helpers in our resolver:

func (r *queryResolver) FlowBlocks(ctx context.Context) ([]*FlowBlock, error) {
    preloads := getPreloads(ctx)

it will result in the following string slice:

["id", "block", "block.id", "block.title", "block.type", "block.choices", "block.choices.id", "block.choices.title", "block.choices.description", "block.choices.slug"]

Thank you, @RichardLindhout. That's helpful.

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

My code does not include all selected fields somehow I forgot

preloads = append(preloads, GetNestedPreloads(ctx, graphql.CollectFields(ctx, column.Selections, nil), prefixColumn)...)

I've updated the code

I've updated the examples above since there were cases of duplicated preloads. And it's updated to the new OperationContext

Hi guys, is a way to get this fields in a middleware instead of a resolver ?

@mtatarau90 Sure, you have a ctx variable in your resolver right? Or else try a directive

https://gqlgen.com/reference/field-collection/
https://gqlgen.com/reference/directives/

@RichardLindhout i will use directives, thanks. Btw in middleware seems do not work, and gqlgen return a context error.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

bieber picture bieber  路  4Comments

lynntobing picture lynntobing  路  3Comments

JulienBreux picture JulienBreux  路  3Comments

sumanthakannantha picture sumanthakannantha  路  3Comments

huanghantao picture huanghantao  路  3Comments