-
Notifications
You must be signed in to change notification settings - Fork 2
Description
There are three types of header propagation;
- From the client(original gw request), to the subgraphs (internal subgraph calls)
- From the subgraphs, to the client(final gw response)
- Between subgraphs
From the client to the subgraphs
--In Hive Gateway JS,--
it is pretty simple thanks to the JavaScript syntax. It takes a function that has a payload with the original request object and subgraph information, then the returned object is passed as headers directly to the specified subgraph;
propagateHeaders: {
fromClientToSubgraphs({ request, subgraphName }) {
if (subgraphName === 'subgraph1') {
return {
'x-custom-header': request.headers.get('x-custom-header')
}
}
}
}
This is ok for JavaScript, but we can only have something like this something like Rhai and VRL.
--Apollo Router--
It has a different solution for this.
So in the following example, you can set a global configuration for all subgraphs or configure a specific subgraph.
You can propagate headers directly from the request, or the context(this context can contain any auth data etc from other plugins), or some operation specific data such as operationName
.
You can also benefit from regular expressions etc.
And headers are modified by following the order, so first it takes, then removes etc. propagate
, remove
etc are the name of the operations which are applied in the order, user provides.
headers:
# Header rules for all subgraphs
all:
request:
# Propagate matching headers
- propagate:
matching: ^upstream-header-.*
# Propagate matching headers
- propagate:
named: "some-header"
default: "default-value"
rename: "destination-header"
# Remove the "x-legacy-account-id" header
- remove:
named: "x-legacy-account-id"
# Remove matching headers
- remove:
matching: ^x-deprecated-.*
# Insert the 'my-company' header
- insert:
name: "my-company"
value: "acme"
# Subgraph-specific header rules
subgraphs:
products:
request:
# Calls to the products subgraph have the "router-subgraph-name" header set to `products`.
- insert:
name: "router-subgraph-name"
value: "products"
accounts:
request:
# Calls to the accounts subgraph have the "router-subgraph-name" header set to `accounts`.
- insert:
name: "router-subgraph-name"
value: "accounts"
request:
- insert:
name: "router-subgraph-name"
value: "products"
- insert:
name: "sent-from-our-apollo-router-context"
from_context: "my_key_in_context"
- insert:
name: "sent-from-our-apollo-router-request-body"
path: ".operationName" # It's a JSON path query to fetch the operation name from request body
default: "UNKNOWN" # If no operationName has been specified
--GraphQL Mesh--
It uses string interpolation to set headers. But you have to define each header specifically. There is no regexp matching etc.
But compared to Apollo, it is easier to access the stuff from context
directly such as request details, operation information etc. It also supports root value, argument access etc but not sure if those are the cases for our gateway.
operationHeaders:
X-header: "some-prefix{context.headers['x-header']}"
It also allows you to add more to the value taken from the context/headers as an extra compared to other declarative solutions.
--Cosmo--
As you can see below, Cosmo has expression
which you can give an expression to specify the condition when the header should be sent.
headers:
all: # Header rules for all subgraph requests.
request:
- op: "propagate" # Forward a client header
named: X-Test-Header # Exact match (Use the canonicalized version)
# Regex match (Case insensitive)
- op: "propagate"
matching: (?i)^X-Custom-.*
- op: "propagate"
matching: ^(Header-1|Key)$
negate_match: true # Ensure that all headers except the Header-1 and Key are propagated
# Sets the value when the header was not set
- op: "propagate"
named: "X-User-Id"
default: "123"
# Rewrites the header from X-Test-1 to X-Test
- op: "propagate"
named: "X-Test-1"
rename: "X-Test"
- op: "set"
name: "X-Custom-Header"
value: "my-required-key"
# Dynamic value using template expression
- op: "set"
name: "X-User-ID"
expression: "request.auth.isAuthenticated ? request.auth.claims.sub : ''"
subgraphs:
products: # Will only affect the Subgraph named "products"
request:
- op: "propagate"
named: "Subgraph-Secret"
default: "some-secret"
My take @ardatan
Apollo's solution seems ok at first. We can implement something similar to forward headers matching the regexp or the pattern. Then if users ask more, we can expose a VRL option or something like Mesh has. But at first, pattern matching, and applying changes on the headers based on the operations given in the list makes sense. As an extra, expression
can be implemented as in Cosmo but only when a user asks for it.
From the subgraphs, to the client(final gw response)
In that case, both Hive Gateway JS and Apollo Router need the user to implement a Rhai or JavaScript code. So you basically access the request data etc to create a header object to be passed to the subgraphs.
But Cosmo allows you to configure this propagation in a declarative way;
# config.yaml
headers:
all: # Header rules for all subgraph responses.
response:
- op: "propagate" # Forward a client header
named: X-Test-Header # Exact match (Use the canonicalized version)
algorithm: "first_write" # This algorithm retains the first value encountered
- op: "propagate"
matching: (?i)^X-Custom-.* # Regex match (Case insensitive)
algorithm: "last_write"
- op: "propagate"
matching: ^(Header-1|Key)$
negate_match: true # Ensure that all headers except the Header-1 and Key are propagated
algorithm: "last_write"
- op: "propagate"
named: "X-User-Id"
default: "123" # Set the value when the header was not set
algorithm: "last_write"
- op: "set"
name: "X-Custom-Header"
value: "my-required-key"
subgraphs:
specific-subgraph: # Will only affect this subgraph
response:
- op: "propagate"
named: "X-User-Id"
default: "456" # Set the value when the header was not set
algorithm: "last_write"
So we can have a similar API which is similar to what we'll implement for the other way of propagation but there is an important thing here is last_write
, first_write
and append
- first_write: Propagates the first occurrence of a header from any subgraph to the client. Once a header is set, subsequent values from other subgraphs are ignored.
- last_write: Propagates the last occurrence of a header from any subgraph to the client, overwriting earlier values.
- append: Combines all header values from different subgraphs into a single, comma-separated list. This is helpful for aggregating values such as roles or flags that need to be merged.
My take @ardatan
We can implement the same approach that Cosmo takes. Then if a user needs more, we can expose an option to provide a VRL/Rhai script.
Between subgraphs
In Hive Gateway JS, it is not well documented but it is possible to take a header from a subgraph and pass it to another in a custom plugin.
In Apollo Router, they have a similar approach but using the context. So you take the value from one subgraph and set it to the context, then take the held value to set in the another's headers.
Until a user really asks for it, we can document this functionality in our docs using VRL etc. But I don't really see a good reason to implement this at first.