Skip to content

Add a GraphQL extension on graphql-java (org.restlet.ext.graphql) #1581

Open

Description

@jlouvel

Add a new extension, org.restlet.ext.graphql, that hosts graphql-java behind a Restlet and provides a GraphQL client over a borrowed org.restlet.Client.

graphql-java is transport-free: GraphQL.execute(ExecutionInput) returns an ExecutionResult and has no HTTP layer. Today the only Java HTTP hosts for it are Spring modules. A Restlet host puts GraphQL on the same port as REST resources, behind the same filters and authenticators, and reuses the Netty connectors for HTTP/1.1, HTTP/2, SSE and WebSocket.

Principles

  • graphql-java types are the native model end to end: Document, GraphQLSchema, ExecutionInput, ExecutionResult, GraphQLError, Publisher<ExecutionResult>. No Restlet-specific GraphQL model.
  • The extension implements only the wire bindings: GraphQL over HTTP (application/graphql-response+json and legacy application/json), graphql-sse (distinct connections) and graphql-transport-ws.
  • Borrowed connectors: the client never creates or stops an HTTP stack; the server adapter is a plain Restlet attached to a router.
  • Constructor and void setter configuration, frozen at start. Finite limits by default.
  • Dependencies: org.restlet, graphql-java, jackson-databind 3 (not org.restlet.ext.jackson, to avoid its extra data formats). org.restlet.ext.websocket is optional.
  • Out of scope: batching, federation, code generation, a cross-protocol layer, remote schema fetching by the extension.

Phases

Stacked PRs, each on the previous one (merge in order):

Findings so far

  • graphql-java 26.1, java-dataloader and reactive-streams have no module descriptor; their automatic module names (com.graphqljava, org.dataloader, org.reactivestreams) resolve from a named consumer (tested in G0).
  • graphql-java's default DataFetcherExceptionHandler puts exception messages in errors; hiding them is the application's choice.
  • graphql-java 26.1 implements @defer but not @stream.
  • EventStreamReader has no line or event size limit; EventStreamRepresentation doesn't send the response head before the first event (the extension sends an initial comment).
  • WebSocketClient.setClient (fix: let WebSocketClient share a borrowed client's event loop #1568) needs a borrowed client with a Context; new Client(Protocol.HTTP) can't be borrowed.
  • MultiPartRepresentation.setBoundary drops the other media type parameters (the new streaming representation keeps them).

Activity

  1. jlouvel commented on Oct 30, 2026

    @jlouvel
    CollaboratorAuthor

    Hardening issues filed for the snags found during delivery:

    None of them blocks the GraphQL PRs, which work around each one today.

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