Repository navigation
Add a GraphQL extension on graphql-java (org.restlet.ext.graphql) #1581
Copy link
Copy link
Open
Description
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-javatypes 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+jsonand legacyapplication/json),graphql-sse(distinct connections) andgraphql-transport-ws. - Borrowed connectors: the client never creates or stops an HTTP stack; the server adapter is a plain
Restletattached to a router. - Constructor and
voidsetter configuration, frozen at start. Finite limits by default. - Dependencies:
org.restlet,graphql-java,jackson-databind3 (notorg.restlet.ext.jackson, to avoid its extra data formats).org.restlet.ext.websocketis 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):
- G0 GraphQL G0: module and spike of server and client adapters #1582: module and spike, PR feat: add the GraphQL extension spike (G0) #1589
- G1 GraphQL G1: server HTTP binding, limits and request context #1583: server HTTP binding, limits and request context, PR feat: complete the GraphQL server HTTP binding (G1) #1590
- G2 GraphQL G2: client HTTP binding, errors and local validation #1584: client HTTP binding, errors and local validation, PR feat: complete the GraphQL client HTTP binding (G2) #1591
- G3 GraphQL G3: subscriptions over SSE (graphql-sse) #1585: subscriptions over SSE (
graphql-sse), PR feat: add GraphQL subscriptions over SSE (G3) #1592 - G4 GraphQL G4: WebSocket subscriptions (graphql-transport-ws) #1586: WebSocket (
graphql-transport-ws): server PR feat: add the GraphQL WebSocket server (G4, graphql-transport-ws) #1593; client draft PR feat: add the GraphQL WebSocket client (G4, graphql-transport-ws) #1594, which needs refactor: replace the WebSocketClient builder with setters #1566 and fix: let WebSocketClient share a borrowed client's event loop #1568 (WebSocketClient should reuse the application's Netty event loop instead of creating its own #1567) - S5 Add a streaming multipart/mixed representation and reader #1587: streaming
multipart/mixedin core, PR feat: add a streaming multipart representation and reader #1595 (on3.0, independent) - G5 GraphQL G5: incremental delivery (@defer / @stream), experimental #1588: incremental delivery (
@defer), experimental, draft PR feat: add experimental GraphQL incremental delivery (G5) #1596, which needs feat: add a streaming multipart representation and reader #1595
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
DataFetcherExceptionHandlerputs exception messages in errors; hiding them is the application's choice. - graphql-java 26.1 implements
@deferbut not@stream. EventStreamReaderhas no line or event size limit;EventStreamRepresentationdoesn'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 aContext;new Client(Protocol.HTTP)can't be borrowed.MultiPartRepresentation.setBoundarydrops the other media type parameters (the new streaming representation keeps them).
Activity
jlouvel commentedon Oct 30, 2026
Hardening issues filed for the snags found during delivery:
EventStreamReadershould limit the size of an event (S12)EventStreamRepresentationshould send the response head before the first event (S13)WebSocketClient.setClientcan't borrow a client created without aContext(S14)MultiPartRepresentation.setBoundarydrops the media type's other parameters (S15)None of them blocks the GraphQL PRs, which work around each one today.