Skip to content

Commit e72ab10

Browse files
feat(exchanges): add binding for @urql/retry-exchange (#244)
1 parent a0d1ff9 commit e72ab10

File tree

7 files changed

+152
-40
lines changed

7 files changed

+152
-40
lines changed

__tests__/Client_test.res

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -210,19 +210,36 @@ describe("Client", () => {
210210
}))
211211

212212
describe("Ecosystem exchanges", () => {
213-
it("should support passing the multipartFetchExchange", () => {
214-
let client = Client.make(
215-
~url="https://localhost:3000",
216-
~exchanges=[
217-
Client.Exchanges.dedupExchange,
218-
Client.Exchanges.cacheExchange,
219-
Client.Exchanges.multipartFetchExchange,
220-
],
221-
(),
222-
)
213+
describe("retryExchange", () => {
214+
it("should apply the default retryExchange options from urql if none are applied", () => {
215+
let retryExchangeOptions = Client.Exchanges.makeRetryExchangeOptions()
223216

224-
open Expect
225-
expect(client) |> toMatchSnapshot
217+
open Expect
218+
expect(retryExchangeOptions) |> toEqual({
219+
Client.Exchanges.initialDelayMs: None,
220+
maxDelayMs: None,
221+
maxNumberAttempts: None,
222+
randomDelay: None,
223+
retryIf: None,
224+
})
225+
})
226+
227+
it("should apply any specified options to the retryExchange", () => {
228+
let retryExchangeOptions = Client.Exchanges.makeRetryExchangeOptions(
229+
~initialDelayMs=200,
230+
~randomDelay=false,
231+
(),
232+
)
233+
234+
open Expect
235+
expect(retryExchangeOptions) |> toEqual({
236+
Client.Exchanges.initialDelayMs: Some(200),
237+
maxDelayMs: None,
238+
maxNumberAttempts: None,
239+
randomDelay: Some(false),
240+
retryIf: None,
241+
})
242+
})
226243
})
227244
})
228245
})

__tests__/__snapshots__/Client_test.bs.js.snap

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -151,30 +151,6 @@ Client {
151151
}
152152
`;
153153

154-
exports[`Client Ecosystem exchanges should support passing the multipartFetchExchange 1`] = `
155-
Client {
156-
"activeOperations": Object {},
157-
"createOperationContext": [Function],
158-
"createRequestOperation": [Function],
159-
"dispatchOperation": [Function],
160-
"executeMutation": [Function],
161-
"executeQuery": [Function],
162-
"executeSubscription": [Function],
163-
"fetch": undefined,
164-
"fetchOptions": undefined,
165-
"maskTypename": false,
166-
"operations$": [Function],
167-
"preferGetMethod": false,
168-
"queue": Array [],
169-
"reexecuteOperation": [Function],
170-
"requestPolicy": "cache-first",
171-
"results$": [Function],
172-
"subscribeToDebugTarget": [Function],
173-
"suspense": false,
174-
"url": "https://localhost:3000",
175-
}
176-
`;
177-
178154
exports[`Client ssrExchange should exist and be callable 1`] = `[Function]`;
179155

180156
exports[`Client with custom fetch implementation should accept a custom fetch implementation 1`] = `

docs/exchanges.md

Lines changed: 67 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -157,22 +157,86 @@ yarn add @urql/exchange-multipart-fetch
157157

158158
Then, substitute the `fetchExchange` with the `multipartFetchExchange`:
159159

160-
```res
160+
```rescript
161161
open ReasonUrql
162162
163163
let client = Client.make(
164164
~url="http://localhost:3000",
165-
~exchanges=[|
165+
~exchanges=[
166166
Client.Exchanges.dedupExchange,
167167
Client.Exchanges.cacheExchange,
168168
Client.Exchanges.multipartFetchExchange
169-
|],
169+
],
170170
()
171171
)
172172
```
173173

174174
Read more on the `multipartFetchExchange` [here](https://github.com/FormidableLabs/urql/tree/main/exchanges/multipart-fetch).
175175

176+
### `retryExchange`
177+
178+
The `retryExchange` is useful for retrying particular operations. By default, adding this exchange with the base options will retry any operations that failed due to network errors. However, we can customize the exchange to catch more specific error cases as well.
179+
180+
To use the `retryExchange`, add the package to your dependencies:
181+
182+
```sh
183+
yarn add @urql/exchange-retry
184+
```
185+
186+
Then, add the exchange to your array of `exchanges`, specifying the options you want to configure:
187+
188+
```rescript
189+
open ReasonUrql
190+
191+
let retryExchangeOptions =
192+
Client.Exchanges.makeRetryExchangeOptions(~initialDelayMs=2000, ~randomDelay=false, ())
193+
194+
let client = Client.make(
195+
~url="http://localhost:3000",
196+
~exchanges=[
197+
Client.Exchanges.dedupExchange,
198+
Client.Exchanges.cacheExchange,
199+
Client.Exchanges.retryExchange(retryExchangeOptions),
200+
Client.Exchanges.fetchExchange
201+
],
202+
()
203+
)
204+
```
205+
206+
By default, `urql` will apply the following configuration for you:
207+
208+
```typescript
209+
{
210+
initialDelayMs: 1000,
211+
maxDelayMs: 15000,
212+
randomDelay: true,
213+
maxNumberAttempts: 2,
214+
retryIf: err => err && err.networkError,
215+
}
216+
```
217+
218+
If you want to use the defaults from `urql`, call `makeRetryExchangeOptions` with just a `unit` parameter.
219+
220+
```rescript
221+
open ReasonUrql
222+
223+
let retryExchangeOptions =
224+
Client.Exchanges.makeRetryExchangeOptions()
225+
226+
let client = Client.make(
227+
~url="http://localhost:3000",
228+
~exchanges=[
229+
Client.Exchanges.dedupExchange,
230+
Client.Exchanges.cacheExchange,
231+
Client.Exchanges.retryExchange(retryExchangeOptions),
232+
Client.Exchanges.fetchExchange
233+
],
234+
()
235+
)
236+
```
237+
238+
Read more on the `retryExchange` [here](https://formidable.com/open-source/urql/docs/advanced/retry-operations/).
239+
176240
## Custom Exchanges
177241

178242
`reason-urql` also allows you to write your own exchanges to modify outgoing GraphQL requests and incoming responses. To read up on the basics of exchanges, check out the excellent [`urql` documentation](https://formidable.com/open-source/urql/docs/concepts/exchanges/).

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
"@glennsl/bs-jest": "^0.6.0",
3636
"@reasonml-community/graphql-ppx": "^1.0.1",
3737
"@urql/exchange-multipart-fetch": "^0.1.11",
38+
"@urql/exchange-retry": "^0.2.0",
3839
"all-contributors-cli": "^6.19.0",
3940
"babel-jest": "^26.6.3",
4041
"bs-platform": "^8.3.2",

src/Client.res

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,32 @@ module Exchanges = {
7474
@module("@urql/exchange-multipart-fetch")
7575
external multipartFetchExchange: t = "multipartFetchExchange"
7676

77+
type retryExchangeOptions = {
78+
initialDelayMs: option<int>,
79+
maxDelayMs: option<int>,
80+
randomDelay: option<bool>,
81+
maxNumberAttempts: option<int>,
82+
retryIf: option<(CombinedError.t, Types.operation) => bool>,
83+
}
84+
85+
let makeRetryExchangeOptions = (
86+
~initialDelayMs=?,
87+
~maxDelayMs=?,
88+
~randomDelay=?,
89+
~maxNumberAttempts=?,
90+
~retryIf=?,
91+
(),
92+
) => {
93+
initialDelayMs: initialDelayMs,
94+
maxDelayMs: maxDelayMs,
95+
randomDelay: randomDelay,
96+
maxNumberAttempts: maxNumberAttempts,
97+
retryIf: retryIf,
98+
}
99+
100+
@module("@urql/exchange-retry")
101+
external retryExchange: retryExchangeOptions => t = "retryExchange"
102+
77103
/* Specific types for the subscriptionExchange. */
78104
type observerLike<'value> = {
79105
next: 'value => unit,

src/Client.resi

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,26 @@ module Exchanges: {
5656
@module("@urql/exchange-multipart-fetch")
5757
external multipartFetchExchange: t = "multipartFetchExchange"
5858

59+
type retryExchangeOptions = {
60+
initialDelayMs: option<int>,
61+
maxDelayMs: option<int>,
62+
randomDelay: option<bool>,
63+
maxNumberAttempts: option<int>,
64+
retryIf: option<(CombinedError.t, Types.operation) => bool>,
65+
}
66+
67+
let makeRetryExchangeOptions: (
68+
~initialDelayMs: int=?,
69+
~maxDelayMs: int=?,
70+
~randomDelay: bool=?,
71+
~maxNumberAttempts: int=?,
72+
~retryIf: (CombinedError.t, Types.operation) => bool=?,
73+
unit,
74+
) => retryExchangeOptions
75+
76+
@module("@urql/exchange-retry")
77+
external retryExchange: retryExchangeOptions => t = "retryExchange"
78+
5979
/* Specific types for the subscriptionExchange. */
6080
type observerLike<'value> = {
6181
next: 'value => unit,

yarn.lock

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -918,7 +918,7 @@
918918
dependencies:
919919
"@types/yargs-parser" "*"
920920

921-
"@urql/core@>=1.14.1":
921+
"@urql/core@>=1.14.1", "@urql/core@>=1.15.0":
922922
version "1.16.1"
923923
resolved "https://registry.yarnpkg.com/@urql/core/-/core-1.16.1.tgz#a41487dd4436cbdc92fce714c6c1fc04315761d3"
924924
integrity sha512-lcEMCS/rB+ug7jKSRDVZIsqNhZxRooTNa1kHfvSjJT2k4SXDyPmjNSfXBUJF2pDJmvv9EIKl9Tk0AF1CvG3Q/g==
@@ -943,6 +943,14 @@
943943
extract-files "^8.1.0"
944944
wonka "^4.0.14"
945945

946+
"@urql/exchange-retry@^0.2.0":
947+
version "0.2.0"
948+
resolved "https://registry.yarnpkg.com/@urql/exchange-retry/-/exchange-retry-0.2.0.tgz#e90a99bf8280ad2b8926bea7f2157a6f59dc8aec"
949+
integrity sha512-eawDIkTSVudv+zMaOlm898UX9lkJnUry2PYqD7INeFWghkHmlIPm6wg5J/GBGAyFjqaOj1OWgAWYcu7sV4eljg==
950+
dependencies:
951+
"@urql/core" ">=1.15.0"
952+
wonka "^4.0.14"
953+
946954
abab@^2.0.3:
947955
version "2.0.5"
948956
resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.5.tgz#c0b678fb32d60fc1219c784d6a826fe385aeb79a"

0 commit comments

Comments
 (0)