gqlgen: CollectAllFields doesn't include nested fields

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

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Reactions: 4
  • Comments: 15 (9 by maintainers)

Most upvoted comments

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.

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

@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)
		// }

	}