Intended outcome:
I'm setting a custom header on the response I'm supposed being able to access in the afterware link
Actual outcome:
The headers object is empty
How to reproduce the issue:
Some context:
I have a custom header in response visible in network tab but not in the response object.
I know that the custom header is stripped away by the browser as this is a CORS request so I put it into Access-Control-Expose-Headers.
Now the browser exposes the custom header just fine as you may see in the screenshot and I should be able to access it within the afterwareLink. Though that's not the case.
The headers object is always empty though getContext yields forceFetch
and cache
fields
I'm using:
CLIENT:
"apollo": "0.2.2",
"apollo-cache-inmemory": "1.1.0",
"apollo-client": "2.0.2",
"apollo-link": "1.0.0",
"apollo-link-context": "1.0.0",
"apollo-link-error": "1.0.0",
"apollo-link-http": "1.1.0",
"graphql": "0.11.7",
"graphql-tag": "2.5.0",
"vue": "2.5.3",
"vue-apollo": "3.0.0-alpha.2",
SERVER:
"apollo-server-express": "1.2.0",
"body-parser": "^1.17.1",
"cors": "^2.8.3",
"express": "^4.15.2",
"graphql-tools": "^1.2.3",
Afterware code:
const afterwareLink = new ApolloLink((operation, forward) => {
const { headers } = operation.getContext();
console.log(operation.getContext());
if (headers) {
const refreshToken = headers.get('x-refresh-token');
console.log('refreshToken', refreshToken);
}
return forward(operation);
}
Version
I ran into this issue as well and found that I had to get the context after forward(operation).map
in order for the correct response object with headers to be available.
Afterware code:
const afterwareLink = new ApolloLink((operation, forward) => {
return forward(operation).map(response => {
const context = operation.getContext();
const { response: { headers } } = context;
if (headers) {
const refreshToken = headers.get('x-refresh-token');
if (refreshToken) {
localStorage.setItem('refreshToken', refreshToken);
}
}
return response;
});
});
@Thr1ve You saved my day! Although I was looking in that direction I wasn't able to get it right.
Thanks a lot :+1:
Now I'm just wondering if that's the way it was supposed to be. In the Context section of the readme it's written:
This link also attaches the response from the fetch operation on the context as response so you can access it...
Now that you've post it I have to say that it reassembles your code example although it wasn't so obvious to me.
@ecerroni we should def improve that portion of the docs! It is on the ApolloLink repo, would you be up for improving that?
@Thr1ve thanks for the assist here!
After trying to get it to work for several hours, i have to ask here.
I have the exact same problem and tried what @Thr1ve suggested.
I use the same dependencies and versions as @ecerroni
I tried using "Access-Control-Expose-Headers: *" and "Access-Control-Expose-Headers: x-refresh-token".
heres is what i see in the chrome network tab
and the console.log of the response.
What i am missing here ?
Thanks a lot @jbaxleyiii and the whole apollo team!
@Knaackee
That's ok I guess as it is the same thing I see in mine.
However headers.get('x-refresh-token')
correctly retrieves the token and I can both console.log it and set it in the localStorage in the afterware code.
Do you mean it is not working for you? I mean retrieving the header using headers.get('x-refresh-token')
A complete example:
import { ApolloLink } from 'apollo-link';
import { createHttpLink } from 'apollo-link-http';
const httpLink = createHttpLink({ uri: 'http://localhost:3001/graphql' });
const afterwareLink = new ApolloLink((operation, forward) => {
return forward(operation).map(response => {
const context = operation.getContext();
const { response: { headers } } = context;
if (headers) {
const token = headers.get("x-refresh-token");
if (token) {
localStorage.setItem("token", token);
}
}
return response;
});
});
// use with apollo-client
const link = afterwareLink.concat(httpLink);
@ecerroni
Sorry i missed that. Now its working as expected. Thank you!
In addition i had to change Access-Control-Expose-Headers: *
to Access-Control-Expose-Headers: x-refresh-token
to make it work.
Thanks a lot!
Hi, sorry @ecerroni , I got same issue with Express but not solve it out yet, would you please to take a look?
const authLink = new ApolloLink((operation, forward) => {
const token = localStorage.getItem(AUTH_TOKEN)
const refresh_token = localStorage.getItem(REFRESH_TOKEN)
if (token) {
operation.setContext({
headers: {
'x-token': token,
'x-refresh-token': refresh_token
}
})
}
return forward(operation).map(response => {
const context = operation.getContext()
const { response: { headers } } = context
if (headers) {
const token = headers.get("x-token")
const refresh_token = headers.get("x-refresh-token")
if (token) {
localStorage.setItem(AUTH_TOKEN, token)
localStorage.setItem(REFRESH_TOKEN, refresh_token)
}
}
return response
})
})
nvm, I got this, pass the token to headers on the auth user context, not on the resolver
res.set('Access-Control-Expose-Headers', '*') working for me
Just in case someone wants to know more about Access-Control-Expose-Headers
: it's useful when making cors request because by default server only exposes CORS-safelisted request-header
. Adding this header will allow you to expose any http headers (including custom headers) to client.
Wasted a couple of hours too :)
Looks like if you console.log
headers, it will appear as empty. Iterating over it doesn't work either.
Whereas getting the header by name: context.response.headers.get('...')
magically works. Now only to find the Chrome engineer who is responsible for this behavior...
I read carefully all of your posts, but I still cannot have access to the headers.
Here you have my code :
const middlewareAuthLink = new ApolloLink((operation, forward) => {
const token = localStorage.getItem(AUTH_TOKEN)
const authorizationHeader = token ? `Bearer ${token}` : null
operation.setContext({
headers: {
authorization: authorizationHeader
}
})
return forward(operation).map(response => {
const context = operation.getContext()
const {
response: { headers }
} = context
if (headers) {
const refresh_token = headers.get('x-token')
console.log(refresh_token) // index.js:37
}
return response
})
})
The response:
and the console:
What am I missing?
related topic: https://stackoverflow.com/questions/47443858/apollo-link-response-headers
I got it
@ecerroni
Sorry i missed that. Now its working as expected. Thank you!
In addition i had to change
Access-Control-Expose-Headers: *
toAccess-Control-Expose-Headers: x-refresh-token
to make it work.Thanks a lot!
Must be set in the backend! (express). Then the frontend can access to this field!
Now it works!
My full code is here: https://stackoverflow.com/a/58986484/5449450
Most helpful comment
@Knaackee
That's ok I guess as it is the same thing I see in mine.
However
headers.get('x-refresh-token')
correctly retrieves the token and I can both console.log it and set it in the localStorage in the afterware code.Do you mean it is not working for you? I mean retrieving the header using
headers.get('x-refresh-token')
A complete example: