Skip to content

Register reflection hints for Relay pagination types#1480

Open
seonwooj0810 wants to merge 1 commit into
spring-projects:mainfrom
seonwooj0810:fix/issue-1479-relay-native-hints
Open

Register reflection hints for Relay pagination types#1480
seonwooj0810 wants to merge 1 commit into
spring-projects:mainfrom
seonwooj0810:fix/issue-1479-relay-native-hints

Conversation

@seonwooj0810

Copy link
Copy Markdown

Fixes #1479

Root cause

graphql-java's PropertyFetchingImpl resolves Relay connection fields (edges, node, cursor, pageInfo, hasNextPage, endCursor, …) reflectively against whatever Java object the data fetcher returns. Spring for GraphQL wraps Window, Slice, and other backing containers into the concrete graphql-java types DefaultConnection, DefaultConnectionCursor, DefaultEdge, and DefaultPageInfo inside ConnectionFieldTypeVisitor.ConnectionDataFetcher.

The GraalVM reachability metadata that used to list those four classes for graphql-java has been dropped from recent versions of the published metadata. On the JVM the reflective lookup still works, but in a native image the getters are inaccessible and PropertyFetchingImpl falls back to null, so relay responses come back with empty node/pageInfo even though the data fetcher produced the correct objects. The reporter on #1479 confirmed that adding hints for these four types restores the expected behavior.

Change

Adds RelayPaginationRuntimeHints, a RuntimeHintsRegistrar that registers INVOKE_PUBLIC_METHODS for the four graphql.relay.Default* types, and wires it through META-INF/spring/aot.factories. Since Spring for GraphQL is the component that surfaces these types to graphql-java, registering the hints here keeps the fix scoped to the situations where Spring actually creates them, rather than relying on upstream metadata to stay in sync.

The hint only covers public getters (INVOKE_PUBLIC_METHODS), matching what PropertyFetchingImpl actually uses; no constructor or field hints are needed because Spring instantiates these types directly.

Test evidence

  • New RelayPaginationRuntimeHintsTests covers two things:
    • the registrar is picked up via META-INF/spring/aot.factories
    • each of the four Relay types has the INVOKE_PUBLIC_METHODS hint registered
  • ./gradlew :spring-graphql:test --tests "org.springframework.graphql.data.pagination.*" passes (existing ConnectionFieldTypeVisitorTests, Base64CursorEncoderTests, plus the new tests, all green).
  • ./gradlew :spring-graphql:checkstyleMain :spring-graphql:checkstyleTest is clean.

Verification done

  1. No in-flight PR. gh pr list --repo spring-projects/spring-graphql --search "relay native hints" and a scan of the recent PR list show no open PR referencing this issue.
  2. No active claim. Issue thread has 0 comments and no "working on it" notes.
  3. Code-focused. Touches only Java source, a test class, and one line of META-INF/spring/aot.factories.
  4. Bug still present on main. None of DefaultConnection/DefaultConnectionCursor/DefaultEdge/DefaultPageInfo appear in spring-graphql/src/main/resources/META-INF/native-image/.../reflect-config.json, and no existing RuntimeHintsRegistrar registers them — verified by grep -r "DefaultConnection\|DefaultEdge\|DefaultPageInfo" spring-graphql/src/main/.
  5. Not closed by an umbrella. Issue is open with no linked PR.
  6. Impact check. Issue contains a clear reproducer (Relay-style controller returning Window running in native image) and a precise root-cause trace through graphql.schema.PropertyFetchingImpl; the reporter verified the same fix in their own application.

graphql-java's PropertyFetchingImpl resolves Connection edges, nodes,
cursors and page info via reflection. The Relay implementation classes
that ConnectionFieldTypeVisitor constructs (DefaultConnection,
DefaultConnectionCursor, DefaultEdge, DefaultPageInfo) are no longer
present in the GraalVM reachability metadata for recent graphql-java
releases, so in a native image those getter calls fail and edges are
serialized with null nodes and page info.

Add a RuntimeHintsRegistrar that registers INVOKE_PUBLIC_METHODS for
the four Relay types so they remain reflectively accessible in a native
image, and wire it through META-INF/spring/aot.factories.

Closes spring-projectsgh-1479

Signed-off-by: seonwoo_jung <laborlawseon@kap.kr>
@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Jun 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

status: waiting-for-triage An issue we've not yet triaged

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Native Image: Relay Window edges return null nodes due to missing runtime hints for graphql-java relay types

2 participants