Skip to content

starJammer/spring-graphql-unions-pagination

Repository files navigation

SPRING-GRAPHQL-UNION-WITH-PAGINATION microservice

Owners

jerry.saravia

Purpose

The purpose of this project is to highlight how unions and relay pagination types are not fully compatible in Spring Boot GraphQL. Additionally, I present a workaround to make them work together but hope that the Spring Boot GraphQL team will provide a better approach or guide this to a more final state.

Getting Started

  • Spring 3.4.1
  • Java 21
  • Kotlin 1.9.25

Quick Start Commands

# Install dependencies
./scripts/bootstrap.sh

# Compile/Build with tests, tests will fail by default
# Navigate to GraphQLUnionConfiguration and uncomment line 23 and include 
# the @Bean annotation import to fix the unit test.
./gradlew build

# Run the service on the command line, or start it from Intellij if you want
./scripts/server.sh

Problem Description

Relay Pagination is supported in Spring Boot GraphQL so that in the following schema the integers field will work correctly, but the integersUnion field will not.

See the schema file in this project for the schema.

You can start the service and see the empty response for yourself or you can run the unit tests with ./gradlew clean build to see the failure. (sample query below)

type Query {
    """ returns a list of integers from 1 to 1000 """
    integers(first: Int, after: String): IntegerConnection!
    """ Same as above except the connection object is wrapped in a union """
    integersUnion(first: Int, after: String): IntegerResult!
}

union IntegerResult = IntegerConnection | NoIntegersFound

type IntegerConnection {
    edges: [IntegerEdge]
    pageInfo: PageInfo
}

type NoIntegersFound {
    message: String!   
}

type IntegerEdge {
    node: Int
    cursor: String   
}

type PageInfo {
    startCursor: String
    endCursor: String
    hasNextPage: Boolean
    hasPreviousPage: Boolean
}
query Integers {
	integersUnion {
		__typename
		... on IntegerConnection {
			pageInfo {
				startCursor
				endCursor
				hasNextPage
				hasPreviousPage
			}
			edges {
				node
			}
		}
		... on NoIntegersFound {
			message
		}
	}
}

The resulting response is empty:

{
  "data": {
    "integersUnion": {
      "__typename": "IntegerConnection",
      "pageInfo": null,
      "edges": null
    }
  }
}

This is the expected result

{
	"data": {
		"integersUnion": {
			"__typename": "IntegerConnection",
			"pageInfo": {
				"startCursor": "S19bImphdmEudXRpbC5Db2xsZWN0aW9ucyRVbm1vZGlmaWFibGVNYXAiLHsic3RhcnQiOjF9XQ==",
				"endCursor": "S19bImphdmEudXRpbC5Db2xsZWN0aW9ucyRVbm1vZGlmaWFibGVNYXAiLHsic3RhcnQiOjEwfV0=",
				"hasNextPage": true,
				"hasPreviousPage": false
			},
			"edges": [
				{
					"node": 1
				},
				{
					"node": 2
				},
				{
					"node": 3
				},
				{
					"node": 4
				},
				{
					"node": 5
				},
				{
					"node": 6
				},
				{
					"node": 7
				},
				{
					"node": 8
				},
				{
					"node": 9
				},
				{
					"node": 10
				}
			]
		}
	}
}

Solution Description

Open this file and go to line 23 and add uncomment it so the bean gets created. (make sure to add the related import too at the top of the file)

Run the unit tests again and they'll pass.

Or you can run the project and attempt the query from above and notice that it works. If you pass in a negative first parameter value you will see that the NoIntegersFound object is returned instead of an IntegerConnection.

GraphQLUnionConfiguration will register the ConnectionUnderUnionTypeVisitor. Read comments on the class for more details but in short it will:

  1. Look for union types being returned and in a decorator data fetcher.
  2. The decorator data fecher will execute the original data fetcher and if the return value is a SliceWrapper or WindowWrapper it will adapt the return value to a Connection object the same way that ConnectionFieldTypeVisitor does. It also adds a type resolver for the union so it resolves the connection object to the Connection type.

Limitations:

  1. Does not support multiple connection objects under a union.
  2. Not sure if this is a good approach or if there is a better one.

About

Showing a potential solution for unions + relay pagination object underneath it.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors