Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Can't get all request fields from query #954

Closed
RichardLindhout opened this issue Dec 13, 2019 · 5 comments
Closed

Can't get all request fields from query #954

RichardLindhout opened this issue Dec 13, 2019 · 5 comments

Comments

@RichardLindhout
Copy link
Contributor

RichardLindhout commented Dec 13, 2019

What happened?

I'm using https://relay.dev/ as client library where you can put data inside fragments to structure your query more logically.
This is query Relay executes.

query FlowTreeQuery(
  $flowId: ID!
) {
  flowBlocks(flowId: $flowId) {
    id
    flowBlock {
      id
    }
    block {
      title
      id
    }
    blockChoice {
      id
    }
    ...FlowBlock_flowBlock
  }
}

fragment BlockAmount_blockAmount on Block {
  title
}

fragment BlockChoices_blockChoices on Block {
  blockChoices {
    id
    title
    slug
  }
}

fragment BlockInput_blockInput on Block {
  title
}

fragment BlockTypeAssigner_blockTypeAssigner on Block {
  id
}

fragment FlowBlock_flowBlock on FlowBlock {
  block {
    id
    blockType
    ...BlockChoices_blockChoices
    ...BlockAmount_blockAmount
    ...BlockInput_blockInput
    ...BlockTypeAssigner_blockTypeAssigner
  }
}

I called

fields := graphql.CollectAllFields(ctx)
	for _, f := range fields {
		fmt.Println(f)
	}

It returned:

id
flowBlock
block
blockChoice

What did you expect?

I expected at least

flowBlock
block
block.blockChoices
blockChoice

And even better something like this:

flowBlock
flowBlock.id
block
block.blockType
block.blockChoices
block.blockChoices.id
block.blockChoices.title
block.blockChoices.slug
block.id
block.title
blockChoice
blockChoice.id

Minimal graphql.schema and models to reproduce

type Block implements Node {
  id: ID!
  title: String!
  description: String
  slug: String
  blockType: String!
  blockChoices: [BlockChoice!]
}

type BlockChoice implements Node {
  id: ID!
  block: Block
  title: String!
  description: String
  slug: String
  imgUrl: String
}

type Flow implements Node {
  id: ID!
  title: String!
  description: String
  expression: String
  flowBlocks: [FlowBlock!]
}

type FlowBlock implements Node {
  id: ID!
  flowBlock: FlowBlock
  block: Block
  blockChoice: BlockChoice
  flow: Flow!
  flowBlocks: [FlowBlock]!
}

interface Node {
  id: ID!
}

type RootQueryType {
  node(id: ID!): Node
}

type Query {
  flowBlocks(flowId: ID!, ids: [ID!]): [FlowBlock!]!
}

versions

  • gqlgen version
    v0.10.2-dev
  • go version
    go version go1.13 darwin/amd64
  • go module version
    github.com/99designs/gqlgen v0.10.2
@RichardLindhout
Copy link
Contributor Author

RichardLindhout commented Dec 17, 2019

Ok, I've resolved this
Say we have the following GraphQL query

Updated examples 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"]

@RichardLindhout
Copy link
Contributor Author

@vektah I think a lot of people want something like this, maybe we could describe this in the documentation?

@RichardLindhout
Copy link
Contributor Author

Related: #746

@stale
Copy link

stale bot commented Feb 15, 2020

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.

@RichardLindhout
Copy link
Contributor Author

@vektah I've made a pull request for this :)

cgxxv pushed a commit to cgxxv/gqlgen that referenced this issue Mar 25, 2022
Based on this 99designs#954 was tagged as 'need documentation'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants