Hi,
I'm looking to use the DefaultNetworkLayer but I need to set a header (X-Request-ID) with a different value on every request. Right now I'm passing a harcoded value:
Relay.injectNetworkLayer(
new Relay.DefaultNetworkLayer('http://localhost:9000/v1/graphql', {
headers: {
'X-Request-ID': "123e4567-e89as-asdasdads-asdasdasdasdasd"
}
})
);
but I was hoping to pass a function so that it get evaluated every time a new request is built, something like this:
Relay.injectNetworkLayer(
new Relay.DefaultNetworkLayer('http://localhost:9000/v1/graphql', {
headers: {
'X-Request-ID': function() { return uuid.v4(); }
}
})
);
Is anything like this possible without creating a new NetworkLayer? If not I'm happy to contribute that patch, just let me know.
Thanks!
RelayDefaultNetworkLayer simply forwards any options to the used fetch-polyfill, so no this won't work. An easy work-around would be to create your own network layer that simply creates a new RelayDefaultNetworkLayer for every request. This could have heavy runtime cost!
@luisobo This seems like a good use-case for a custom network layer. As @clentfort suggested, you can delegate to the default layer internally. I suspect that the runtime cost would be negligible. Let us know how this works out for you!
@luisobo Does this suggestion help? I'll close this issue for now, but feel free to comment with more questions or provide feedback on this approach.
Hi, a bit late but I faced the same problem and used the following solution:
Relay.injectNetworkLayer(
new Relay.DefaultNetworkLayer('/graphql', {
get headers() {
return {
Authorization: 'Bearer ' + localStorage.getItem('id_token')
}
}
})
)
cheers
Example of adding X-Request-ID to request headers (which changes with every request) can be found here https://github.com/nodkz/react-relay-network-layer#example-of-injecting-networklayer-with-middlewares-on-the-client-side
import Relay from 'react-relay';
import { RelayNetworkLayer, urlMiddleware } from 'react-relay-network-layer';
// Be aware `RelayNetworkLayer` is community realization of network layer. (this is not relay default network layer)
Relay.injectNetworkLayer(new RelayNetworkLayer([
// example of thunk, which generate new url with every request
urlMiddleware({
url: (req) => '/graphql?' + Math.random(),
}),
// example of the custom inline middleware (add `X-Request-ID` to request headers)
next => req => {
req.headers['X-Request-ID'] = uuid.v4();
return next(req);
}
]));
@IcanDivideBy0 tokens are implemented via authMiddleware
import Relay from 'react-relay';
import { RelayNetworkLayer, authMiddleware } from 'react-relay-network-layer';
Relay.injectNetworkLayer(new RelayNetworkLayer([
authMiddleware({
token: () => localStorage.getItem('id_token'),
// this promise will be called if server returns 401 status code
// and made implicitly for Relay re-request with new token to GraphQL server
tokenRefreshPromise: (req) => {
console.log('[client.js] resolve token refresh', req);
return fetch('/jwt/refresh')
.then(res => res.json())
.then(json => {
const token = json.token;
localStorage.setItem('id_token', token);
return token;
})
.catch(err => console.log('[client.js] ERROR can not refresh token', err));
},
})
]));
@nodkz thanks ! this looks definitly cleaner ;)
@IcanDivideBy0 Clever idea, but that actually didn't work for me. I had to use:
var self = this;
Relay.injectNetworkLayer(
new Relay.DefaultNetworkLayer(`${window.API_URL}/graphql`, {
headers: {
get Authorization() { return self.state && self.state.auth }
}
})
);
@miracle2k I finally used the authMiddleware which handle a refresh token function as well. You should try the @nodkz solution too, the getter/setter is definitly some kind of a hack
PS: we still write var self = this; in 2016 ? xD
This is untested, but for relay-modern I believe the solution from @IcanDivideBy0 should still work. The new NetworkLayer interface just needs a function that returns a Promise. Modifed version from their docs:
const {Environment, Network} = require('relay-runtime');
// Define a function that fetches the results of an operation (query/mutation/etc)
// and returns its results as a Promise:
function fetchQuery(
operation,
variables,
cacheConfig,
uploadables,
) {
return fetch('/graphql', {
method: 'POST',
headers: {
// Add header(s) here as obj getters
get Authorization(): 'Bearer ' + localStorage.getItem('id_token'),
},
body: JSON.stringify({
query: operation.text, // GraphQL text from input
variables,
}),
}).then(response => {
return response.json();
});
}
// Create a network layer from the fetch function
const network = Network.create(fetchQuery);
source: https://facebook.github.io/relay/docs/network-layer.html
We have this up and running in Relay Modern. Here's our implementation. (Note that this works if additional fetches are called while authentication is in progress, since they all reference the same promise instance.)
async function fetchQuery(
operation: any,
variables: any,
cacheConfig: any,
uploadables: any,
) {
if (!_accessTokenPromise)
_accessTokenPromise = fetchAccessToken();
const authToken = await this._accessTokenPromise;
const response = await fetch(
...
{
method: 'POST',
headers: {
'content-type': 'application/json',
Authorization: "Bearer " + authToken
},
body: JSON.stringify({
query: operation.text, // GraphQL text from input
variables,
}),
});
return await response.json();
}
async fetchAccessToken() {
// tries to get from local storage, or returns null
const cachedAccessToken = await getJwtOrNull();
if (cachedAccessToken)
return cachedAccessToken;
return await getTokenFromMyAuthProvider();
}
Most helpful comment
Hi, a bit late but I faced the same problem and used the following solution:
cheers