Skip to content

Add response middlewares to the network queryers - #2

Merged
cideM merged 1 commit into
masterfrom
feat/response-middlewares
Sep 17, 2026
Merged

cideM merged 1 commit into
masterfrom
feat/response-middlewares

Conversation

@cideM

@cideM cideM commented Sep 9, 2026

Copy link
Copy Markdown
Collaborator

Context

The Content API team has a feature in its GraphQL API that requires both custom directives to work (already fixed) and extensions support in GQL responses (this PR + one in the gateway library fork + one in our GQL gateway). Specifically, we want to forward the extensions.relations key that Content API returns for queries carrying the @capiRelationMap directive.

Problem

Both network queryers parse the full downstream response and then decode only data into the receiver. SingleRequestQueryer.Query (queryerNetwork.go):

result := map[string]interface{}{}
if err = json.Unmarshal(response, &result); err != nil {
    return err
}
// ...
if err = decoder.Decode(result["data"]); err != nil {
    return err
}
return q.queryer.ExtractErrors(result)

Notice that extensions is ignored and thrown away. MultiOpQueryer.Query (queryerMultiOp.go) has the same problem.

Solution

There is a request middleware in the repo already, NetworkMiddleware func(*http.Request) error, but there's no equivalent for responses, so we'll add one:

type ResponseMiddleware func(ctx context.Context, response map[string]interface{}) error

type QueryerWithResponseMiddlewares interface {
    WithResponseMiddlewares(wares []ResponseMiddleware) Queryer
}

The middlewares run after the response is parsed and before data is decoded, with the full top-level object (data, errors, extensions, ...). Returning an error aborts the query with that error, like a NetworkMiddleware does on the request side.

The library makes no assumptions about what a middleware does with the response. Our use is to collect extensions per operation in the GQL gateway (https://github.com/amboss-mededu/graphql-gateway/pull/695), but it is equally usable for logging, metrics or tracing on the response side.

Tests cover the single request queryer (JSON and multipart, error propagation) and the multi-op queryer (each bundled query sees its own entry).

This change is written to be upstreamable to nautilus/graphql as is.

NetworkMiddleware lets callers see every outgoing request, but nothing lets
them see the response. SingleRequestQueryer and MultiOpQueryer both parse
the full response object and then decode only "data" into the receiver, so
anything else the service returned, notably top-level "extensions", is
dropped before the caller can look at it.

ResponseMiddleware is the response-side counterpart: a function that is
handed the parsed response and the query's context, after parsing and
before "data" is decoded. Both queryers accept a list of them through
WithResponseMiddlewares, mirroring WithMiddlewares. In MultiOpQueryer the
middlewares run once per bundled query, with that query's own context and
its own entry of the batched response.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

@greenmato greenmato left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cideM
cideM merged commit 6bd1359 into master Sep 17, 2026
@cideM

cideM commented Sep 17, 2026

Copy link
Copy Markdown
Collaborator Author

nautilus#47

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants