-
Notifications
You must be signed in to change notification settings - Fork 130
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Pass dynamic header to federated services #383
Comments
In WunderGraph, we do it this way, works also for Federation: https://wundergraph.com/docs/guides/strategies/inject_short_lived_token_to_upstream_requests graphql-go-tools/pkg/engine/datasource/graphql_datasource/graphql_datasource_test.go Line 48 in 9fe3016
Client Request Headers need to be passed to the resolver as part of the execution context to be able to use them during resolving:
|
@jensneuse diff --git a/examples/federation/gateway/http/http.go b/examples/federation/gateway/http/http.go
index a29a4091..a90907ad 100644
--- a/examples/federation/gateway/http/http.go
+++ b/examples/federation/gateway/http/http.go
@@ -27,7 +27,9 @@ func (g *GraphQLHTTPRequestHandler) handleHTTP(w http.ResponseWriter, r *http.Re
buf := bytes.NewBuffer(make([]byte, 0, 4096))
resultWriter := graphql.NewEngineResultWriterFromBuffer(buf)
- if err = g.engine.Execute(r.Context(), &gqlRequest, &resultWriter); err != nil {
+ if err = g.engine.Execute(r.Context(), &gqlRequest, &resultWriter, graphql.WithAdditionalHttpHeaders(http.Header{
+ "X-Request-ID": []string{"123456"},
+ })); err != nil {
g.log.Error("engine.Execute", log.Error(err))
w.WriteHeader(http.StatusInternalServerError)
return
diff --git a/examples/federation/products/server.go b/examples/federation/products/server.go
index 1cf18943..46a3ca7e 100644
--- a/examples/federation/products/server.go
+++ b/examples/federation/products/server.go
@@ -20,7 +20,10 @@ func main() {
}
http.Handle("/", playground.Handler("GraphQL playground", "/query"))
- http.Handle("/query", graph.GraphQLEndpointHandler(graph.EndpointOptions{EnableDebug: true, EnableRandomness: true}))
+ http.Handle("/query", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ log.Println(r.Header)
+ graph.GraphQLEndpointHandler(graph.EndpointOptions{EnableDebug: true, EnableRandomness: true}).ServeHTTP(w, r)
+ }))
http.HandleFunc("/websocket_connections", graph.WebsocketConnectionsHandler)
log.Printf("connect to http://localhost:%s/ for GraphQL playground", port) treat X-Request-ID by any user defined header. If I debugged correctly it's because
does not take into account any header that is in ctx.Request.Header. |
@aerfio there is another step required to have the HTTP Headers passed to the underlying services for the federated gateway. When creating your schema configuration you need to add the header with a custom syntax that will look for a value in the original HTTP request.
|
Should a similar config work for subscriptions? I'm trying to propagate headers in the init message by doing something similar to what is here, but the options.Header is always empty. I've seen this commit, that just landed in master, which does it in a different way, but similar in the end, but I should still have the problem of the headers being empty. |
Bump! Same as @reistiago , I am having difficulty propagating the headers from my subscription request to the federated subgraphs. It works fine for queries and mutations. Here's my data source config:
I've debugged in my |
Any updates on this? I am using github.com/wundergraph/graphql-go-tools v1.67.2 and the initialPayload is not being propagated to the subgraphs |
This is a major issue for us as well, using the example code we are unable to pass custom headers to our code when using |
Ok I managed to propagate the headers to a subscription by doing the following. import (
"github.com/wundergraph/graphql-go-tools/pkg/subscription/websocket"
)
func HandleWebsocket(done chan bool, errChan chan error, conn net.Conn,
executorPool *subscription.ExecutorV2Pool, logger *slog.Logger) {
defer func() {
if err := conn.Close(); err != nil {
logger.Error("http.HandleWebsocket(): could not close connection to client.", err)
}
}()
transportProtocol := websocket.Protocol(helper.GetEnv(configuration.EnvTransportProtocol,
string(websocket.ProtocolGraphQLTransportWS)))
websocket.Handle(
done,
errChan,
conn,
executorPool,
websocket.WithProtocol(transportProtocol),
websocket.WithCustomSubscriptionUpdateInterval(50*time.Millisecond),
websocket.WithCustomKeepAliveInterval(graph_server.WebsocketKeepAliveInterval),
)
}
import (
"encoding/json"
"fmt"
"github.com/wundergraph/graphql-go-tools/pkg/subscription/websocket"
"net/http"
"time"
"github.com/gobwas/ws"
"github.com/gobwas/ws/wsutil"
"github.com/wundergraph/graphql-go-tools/pkg/subscription"
"log/slog"
"net"
)
// handleWebsocket will handle the websocket connection.
func (g *GraphQLHTTPRequestHandler) handleWebsocket(req http.Request, conn net.Conn) {
done := make(chan bool)
errChan := make(chan error)
newContext := subscription.NewInitialHttpRequestContext(&req)
executorPool := subscription.NewExecutorV2Pool(g.engine, newContext)
go HandleWebsocket(done, errChan, conn, executorPool, g.log)
select {
case err := <-errChan:
g.log.Error("http.GraphQLHTTPRequestHandler.handleWebsocket()", err)
case <-done:
}
} This also meant that I had to update the func (g *GraphQLHTTPRequestHandler) upgradeWithNewGoroutine(w http.ResponseWriter, r *http.Request) error {
conn, _, _, err := g.wsUpgrader.Upgrade(r, w)
if err != nil {
return err
}
g.handleWebsocket(*r, conn)
return nil
} previously only the context was being sent to the my func (d *DatasourcePollerPoller) createDatasourceConfig() []graphqlDataSource.Configuration {
dataSourceConfigs := make([]graphqlDataSource.Configuration, 0, len(d.config.Services))
for _, serviceConfig := range d.config.Services {
sdl, exists := d.sdlMap[serviceConfig.Name]
if !exists {
continue
}
dataSourceConfig := graphqlDataSource.Configuration{
Fetch: graphqlDataSource.FetchConfiguration{
URL: serviceConfig.URL,
Method: http.MethodPost,
Header: http.Header{
// .request.headers.XXX transmits incoming HTTP headers value to the
// internal sub-requests made
"X-Customer-Id": []string{"{{ .request.headers.X-Customer-Id }}"},
"X-SOB": []string{"{{ .request.headers.X-SOB }}"},
},
},
Subscription: graphqlDataSource.SubscriptionConfiguration{
URL: serviceConfig.WS,
},
Federation: graphqlDataSource.FederationConfiguration{
Enabled: true,
ServiceSDL: sdl,
},
}
dataSourceConfigs = append(dataSourceConfigs, dataSourceConfig)
}
return dataSourceConfigs
} @waeljammal I hope this helps |
@mxncson were you able to find a solution to your request? Ultimately, I would like to hook into the fetch request before it is sent to the downstream service to overwrite it's headers. |
i just want to pass cookies / auth data / referrer down to service, and still don't see how it can be done int v2 version.. |
In Cosmo Router, we add the client request Header to the context and forward it to Subgraphs in the transport like so: Cosmo Router has a Header Rule Engine to propagate Headers dynamically, with defaults and such. You can replicate this. |
Hello @jensneuse,
How do you pass a dynamic header to the http handler (engine) ?
I have a simple use case where the client send an auth id that i need to validate(gateway side) then forward others different headers to my services.
I don't see a direct way to interface with the underlying http components in the engine. How can i pass a custom and non-generic headers to services ?
I've read #270 and the other unanswered issue similar to mine, i have tested all of the above and nothing fit this case.
The text was updated successfully, but these errors were encountered: