99designs / 99designs/gqlgen

Incorrect results when querying 1 type and 2 interfaces using different selection sets

Open
#3,636 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Go
Stars
10.8k
Forks
1.3k
Avg merge
2d 36m
Merged PRs (30d)
26

Description

## Describe the bug
In a system with: 1 type (CompactPost) and 2 interfaces (PostInterface1, PostInterface2), when requesting different selections sets from the types/interfaces, the output differs depending on which selection set requests all fields or fewer fields.

## To Reproduce
A repository with a reproduction is here:
https://github.com/montykamath/gqlgen-bug-repro-1

1. Clone the repository.
2. Start the gql server by `cd b; make run`
3. Open the router sandbox url that shows in the terminal like http://127.0.0.1:8071/
4. Run the BadQuery.graphql or GoodQuery.graphql to see the differences in output
5. Place a breakpoint in 99designs/gqlgen@v0.17.70/graphql/handler/transport/util.go in writeJson to see the output before it is written. It has duplicate keys for awards before the response is written. After the response is written it takes the last of those duplicate keys.

## Expected behavior
Expect the output to be a deep merge of the selection sets into a single response list, rather than allowing the JSON writer to select the last of the duplicate keys.

## The working query
```
# This query fetches posts and their associated awards.
# It includes inline fragments to handle different post types/interfaces
# It only works because it requests the same fields in all fragments for awards
query GoodQuery {
posts {
__typename
awards {
__typename
id
name
}
... on CompactPost {
__typename
id
awards {
__typename
id
name
}
}
... on PostInterface2 {
__typename
id
awards {
__typename
name
id
}
}
}
}
```

#### Working query produces this output
```
{
"data": {
"posts": [
{
"__typename": "CompactPost",
"awards": [
{
"__typename": "Award",
"id": "1",
"name": "One"
},
{
"__typename": "Award",
"id": "2",
"name": "Two"
},
{
"__typename": "Award",
"id": "3",
"name": "Three"
}
],
"id": "1"
}
]
}
}
```

## The failing query
```
# This query is identical to the good query except that it comments out the name and id from the PostInterface2 fragment for Award
# This query fetches posts and their associated awards.
# It includes inline fragments to handle different post types/interfaces
# It does not work because it requests different fields in some of the fragments for awards
query BadQuery {
posts {
__typename
awards {
__typename
id
name
}
... on CompactPost {
__typename
id
awards {
__typename
id
name
}
}
... on PostInterface2 {
__typename
id
awards {
__typename
# name
# id
}
}
}
}
```

### Failing query produces this output
```
{
"data": {
"posts": [
{
"__typename": "CompactPost",
"awards": [
{
"__typename": "Award"
},
{
"__typename": "Award"
},
{
"__typename": "Award"
}
],
"id": "1"
}
]
}
}
```

Contributor guide

Open the contributing guide

Research direction

The bug is in the GraphQL response merging logic when different selection sets are used across fragments. Start by examining the reproduction repository at https://github.com/montykamath/gqlgen-bug-repro-1. Look at the handler/transport/util.go file mentioned, specifically the writeJson function, to see duplicate keys. Understand how gqlgen merges selection sets for interfaces and types. The fix likely involves the execution layer's field collection or response formatting to perform a deep merge instead of allowing JSON duplicates.

Written by the indexing model from the issue text.

Assessment

Tech stack
go, graphql
Domain
api, backend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Clearly specified
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.