Open AshDevFr opened 2 years ago
Ok after a couple of hours I came up with this:
let makeRetryLink = ApolloClient.Link.RetryLink.make(
~attempts=RetryFunctionOptions({
max: Some(3),
retryIf: (~error, ~operation) =>
Js.Promise.resolve(
switch error {
| Some(e) => e->Js.Json.decodeBoolean->Belt.Option.getWithDefault(false)
| None => false
},
),
}),
~delay=DelayFunctionOptions({
initial: Some(200),
max: Some(2000),
jitter: Some(1),
}),
)
I don't know if it's correct.
According to the documentation jitter
seems to be a bool
No sure if it will help anybody but I was able to make my errorLink
but it's pretty ugly.
First of all, two things were missing from react-apollo-client
to make it work for me so I added them:
@module("@apollo/client")
external fromPromise: Promise.t<'t> => ApolloClient__Link_Core_Types.Observable.Js_.t<
ApolloClient__Link_Core_Types.FetchResult.Js_.t<Js.Json.t>,
Js.Exn.t,
> = "fromPromise"
@send
external flatMap: (
ApolloClient__Link_Core_Types.Observable.Js_.t<'t, 'error>,
't => ApolloClient__Link_Core_Types.Observable.Js_.t<'r, 'error>,
) => ApolloClient__Link_Core_Types.Observable.Js_.t<'r, 'error> = "flatMap"
Here is the code I'm using to refresh the tooken on 401
errors and forward the operation (DISCLAIMER: it's super ugly and dirty code)
In order to refresh the token inside the errorLink
I had to create a small client
let simpleClient = {
open ApolloClient
make(~cache=Cache.InMemoryCache.make(), ~connectToDevTools=true, ~link=httpLink, ())
}
Then in the errorLink
let handleCode = code =>
switch code->Js.Json.decodeString {
| Some(code') =>
switch code' {
| "UNAUTHENTICATED" =>
let refreshToken = getTokenFromSessioon()
Some(
simpleClient.mutate(
~mutation=module(RefreshTokenMutation),
{token: refreshToken},
)->Promise.thenResolve(result =>
switch result {
| Ok({data: {tokens}}) =>
saveTokens()
true
| Error(_) =>
logout()
false
}
),
)
| _ => None
}
| None => None
}
let errorLink = ApolloClient.Link.ErrorLink.make(({
networkError,
graphQLErrors,
operation,
response,
forward,
}) => {
let promise = switch graphQLErrors {
| Some(e') =>
e'->Belt.Array.reduce(None, (errP, error) => {
switch errP {
| Some(_) => errP
| None =>
switch error.extensions {
| Some(ext) =>
switch ext->Js.Dict.get("code") {
| Some(code) => handleCode(code)
| None => None
}
| None => None
}
}
})
| None => None
}
let _ = switch networkError {
| Some(networkError') => Js.log2("Network error", networkError')
| None => ()
}
let _ = switch response {
| Some(response') => Js.log2("Error Link response", response')
| None => ()
}
switch promise {
| Some(p) => Some(flatMap(fromPromise(p), _ => forward(operation)))
| None => None
}
})
Excellent! I'm glad you got things resolved. I have a possible alternative solution, but it's been a minute since I've actually used Apollo, so take it with a grain of salt. If I remember correctly, returning out of the error link essentially says "retry one more time". If you had a context link that builds up your auth headers from state or storage (ApolloClient.Link.ContextLink.makeAsync
), the only thing the error link would need to be responsible for is invalidating the token on a 401 and forwarding the operation. A new token would get fetched asynchronously on the retry triggered from forwarding in the error link.
That aside, there are a bunch of good improvements here. Jitter is definitely a bool
, observable.flatMap
and fromPromise
seem like reasonable bindings to add, and documentation was only ever partially fleshed out. If you're interested in doing any of that, PRs welcome!
Hi,
I think I could make a PR for flatMap
since it's pretty straightforward but having started ReScript yesterday, some of the syntax and the way the library is built it still too complex at the moment for me.
I realized today that errorLink
expect a return of an observable
when using forward(operation)
but can also get null
as a return value if there is no forward operation.
I tried to changed errorLLink
but I don't know how we can say that this method gets either observable
or nothing.
Any idea?
having started ReScript yesterday, some of the syntax and the way the library is built it still too complex at the moment for me.
No worries, then. I'll handle the changes.
I realized today that errorLink expect a return of an observable when using forward(operation) but can also get null as a return value if there is no forward operation.
I'm not sure I understand what you're saying here. It sounds like you're asking how to return null inside the error link. Are the bindings wrong? At the time of writing the typescript types were:
interface ErrorHandler {
(error: ErrorResponse): Observable<FetchResult> | void;
}
Which means you should either return Some(forward(operation))
or None
?
Hi, First of alll, thank you for your work on this project.
I tried to use
RetryLink
and no matter what I put inattempts
anddelay
I get type errors. I looked at the type definitions but I can't get to make it work.Would it be possible for you to either update the
EXAMPLE
in the repo or in the website to provide example for bothRetryLink
andErrorLink
.Thanks a lot.