Skip to content

Single-entity @EntityMapping should resolve an empty Mono to a null _Entity (per Federation spec) instead of raising a per-entity error #1516

Description

@hameno

What

For a single-entity federated reference resolver:

@EntityMapping
public Mono<Book> book(@Argument String id) {
    return repository.findById(id);   // Mono.empty() when not found
}

an empty Mono (the idiomatic reactive "not found") is turned into a hard per-entity GraphQL error — "Entity fetcher returned null or completed empty" — rather than resolving that entity to null.

Source: EntitiesDataFetcher.invokeEntityMethod:

return handlerMethod.getEntity(environment, representation)
    .switchIfEmpty(Mono.error(new RepresentationNotResolvedException(representation, handlerMethod)))
    .onErrorResume((ex) -> resolveException(ex, environment, handlerMethod, index));

Why this is arguably wrong

  1. The Apollo Federation subgraph spec defines _entities(representations: [_Any!]!): [_Entity]! — the list is non-null but its elements are nullable, and the spec states: "Entries in the list can be null if no entity exists for a provided representation." So resolving an unresolvable representation to null is spec-compliant; erroring is stricter than the spec.

  2. Mono.empty() is the natural reactive signal for "absent"; converting it to an error is surprising.

  3. Inconsistency within the framework: the batch form (List<T> / Mono<List<T>>) already lets a null list entry become a null entity with no error — EntitiesDataFetcher.applyResults does entities.set(index, null) for a non-ErrorContainer (null) result. So the framework can already emit spec-legal nulls; only the ergonomic single-Mono form cannot.

Impact

A subgraph that references another subgraph's entity by a key that may legitimately not resolve (e.g. content deleted after the reference was recorded) cannot return a spec-compliant null from the single-entity form. It must either pre-validate every key or switch to the batch form purely to obtain null tolerance. At the gateway the per-entity error is surfaced to clients (e.g. graphql-request throws on any GraphQL error, failing the whole operation).

Proposed change

Let the single-entity @EntityMapping (Mono<T>) resolve an empty Mono to a null entity — matching the nullable _Entity element and the batch form — or provide an opt-in for that behaviour.

Repro

An @EntityMapping Mono<T> returning Mono.empty() yields the error "Entity fetcher returned null or completed empty" instead of a null entity.

Environment

  • Spring for GraphQL (Spring Boot 4.0.x)
  • Apollo Federation v2 (FederationSchemaFactory)

References

  • EntitiesDataFetcher.invokeEntityMethod (single) vs applyResults (batch) — spring-graphql/src/main/java/org/springframework/graphql/data/federation/EntitiesDataFetcher.java
  • Apollo Federation subgraph spec, _entities field

Disclaimer

This summary was created with the help of AI during a debugging session on why optional entities throw errors

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions