Is your feature request related to a problem? Please describe.
The goal is to intercept every query/mutation requests and cycle the token in the header before the request is sent to the server, especially when the token has expired.
Since I'm using Firebase Auth, refreshing the token is promise-based; therefore, the middleware needs to wait for the token to be acquired then proceed with setting the authorization header.
Am using:
For the record, below is the code that works without async/await/promise:
// /plugins/apollo.config.js
import { ApolloLink } from 'apollo-link'
import { HttpLink } from 'apollo-link-http'
export default (context) => {
const httpLink = new HttpLink({ uri: 'http://localhost:9000/graphql' })
// middleware
const middlewareLink = new ApolloLink((operation, forward) => {
const token = 'manually-paste-firebase-auth-token-string-here'
const payload = {
headers: {
authorization: `Bearer ${token}`,
},
}
operation.setContext(payload)
return forward(operation)
})
const link = middlewareLink.concat(httpLink)
return { link }
}
Describe the solution you'd like
We need to cycle the promise-based firebase auth token thus using async/await but it keeps on throwing an error TypeError: forward(...).subscribe is not a function. Here is the slightly modified code (from above) using async/await:
// /plugins/apollo.config.js
import { ApolloLink } from 'apollo-link'
import { HttpLink } from 'apollo-link-http'
export default (context) => {
const httpLink = new HttpLink({ uri: 'http://localhost:9000/graphql' })
// middleware
const middlewareLink = new ApolloLink(async (operation, forward) => {
const token = await context.app.$fireAuth.currentUser.getIdToken(true)
const payload = {
headers: {
authorization: `Bearer ${token}`,
},
}
// error is thrown here:
// TypeError: forward(...).subscribe is not a function
operation.setContext(payload)
return forward(operation)
})
const link = middlewareLink.concat(httpLink)
return { link }
}
I did a lot of googling for the last two days to find answers on using the middleware alongside promise/async/await. I see a lot of suggestions but cannot come down to a working solution. For example, the Stackoverflow question reflects the same error forward(...).subscribe is not a function: https://stackoverflow.com/questions/57983192/react-apollo-link-how-to-forward-operation-after-a-promise-has-resolved
Describe alternatives you've considered
I have considered cycling the firebase auth token behind the scenes like every 30 minutes or so -- and save it to a localStorage or Vuex. But that feels dirty and hacky.
Additional context
If you can provide a working example, that would be great. I can feel that I'm almost there to get all this wrapped up but am going in circles. Thank you so much in advance.
馃檹
After digging around and significantly contributing to Google hits, I was able to narrow things down and got a working solution. I wanted to share the piece for those who would need such a solution too -- and for the experts of this module to confirm or make recommendations on a better alternative if any.
Here's the working code:
// /plugins/apollo.config.js
import { setContext } from 'apollo-link-context'
import { HttpLink } from 'apollo-link-http'
export default (context) => {
const httpLink = new HttpLink({ uri: 'http://localhost:9000/graphql' })
// middleware
const middlewareLink = setContext(async (request) => {
const token = await context.app.$fireAuth.currentUser.getIdToken(true)
return {
headers: {
authorization: `Bearer ${token}`,
},
}
})
const link = middlewareLink.concat(httpLink)
return { link }
}
You could done that even easier by setting getAuth config option
getAuth: tokenName => {
if (typeof window === 'undefined') {
return null
}
return token ? `Bearer ${token}` : null
}
I think you can close this if you have solved it @chadwtaylor. And I think SO might be a better place for questions like these in the future. FWIW the reason you can't do it the first way is that the ApolloLink constructer is effectively:
constructor(request: RequestHandler) {
this.request = request
}
where RequestHandler is (op: Operation, forward: NextLink) => Observable | null, meaning it doesn't support async methods out of the box, you would need to create an Observable in place of the promise and return that. The simple way would be to do something like:
(op: Operation, forward: NextLink) => {
fromPromise(context.app.$fireAuth.currentUser.getIdToken(true)).flatMap(token => {
const payload = {
headers: {
authorization: `Bearer ${token}`,
},
}
return forward(operation.setContext(payload))
})
}
Though as you found it may be even easier to use a pre-built link instead
Most helpful comment
After digging around and significantly contributing to Google hits, I was able to narrow things down and got a working solution. I wanted to share the piece for those who would need such a solution too -- and for the experts of this module to confirm or make recommendations on a better alternative if any.
Here's the working code: