Skip to content

Commit

Permalink
feat(exchanges): add binding for @urql/retry-exchange (#244)
Browse files Browse the repository at this point in the history
  • Loading branch information
parkerziegler authored Dec 29, 2020
1 parent a0d1ff9 commit e72ab10
Show file tree
Hide file tree
Showing 7 changed files with 152 additions and 40 deletions.
41 changes: 29 additions & 12 deletions __tests__/Client_test.res
Original file line number Diff line number Diff line change
Expand Up @@ -210,19 +210,36 @@ describe("Client", () => {
}))

describe("Ecosystem exchanges", () => {
it("should support passing the multipartFetchExchange", () => {
let client = Client.make(
~url="https://localhost:3000",
~exchanges=[
Client.Exchanges.dedupExchange,
Client.Exchanges.cacheExchange,
Client.Exchanges.multipartFetchExchange,
],
(),
)
describe("retryExchange", () => {
it("should apply the default retryExchange options from urql if none are applied", () => {
let retryExchangeOptions = Client.Exchanges.makeRetryExchangeOptions()

open Expect
expect(client) |> toMatchSnapshot
open Expect
expect(retryExchangeOptions) |> toEqual({
Client.Exchanges.initialDelayMs: None,
maxDelayMs: None,
maxNumberAttempts: None,
randomDelay: None,
retryIf: None,
})
})

it("should apply any specified options to the retryExchange", () => {
let retryExchangeOptions = Client.Exchanges.makeRetryExchangeOptions(
~initialDelayMs=200,
~randomDelay=false,
(),
)

open Expect
expect(retryExchangeOptions) |> toEqual({
Client.Exchanges.initialDelayMs: Some(200),
maxDelayMs: None,
maxNumberAttempts: None,
randomDelay: Some(false),
retryIf: None,
})
})
})
})
})
24 changes: 0 additions & 24 deletions __tests__/__snapshots__/Client_test.bs.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -151,30 +151,6 @@ Client {
}
`;

exports[`Client Ecosystem exchanges should support passing the multipartFetchExchange 1`] = `
Client {
"activeOperations": Object {},
"createOperationContext": [Function],
"createRequestOperation": [Function],
"dispatchOperation": [Function],
"executeMutation": [Function],
"executeQuery": [Function],
"executeSubscription": [Function],
"fetch": undefined,
"fetchOptions": undefined,
"maskTypename": false,
"operations$": [Function],
"preferGetMethod": false,
"queue": Array [],
"reexecuteOperation": [Function],
"requestPolicy": "cache-first",
"results$": [Function],
"subscribeToDebugTarget": [Function],
"suspense": false,
"url": "https://localhost:3000",
}
`;

exports[`Client ssrExchange should exist and be callable 1`] = `[Function]`;

exports[`Client with custom fetch implementation should accept a custom fetch implementation 1`] = `
Expand Down
70 changes: 67 additions & 3 deletions docs/exchanges.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,22 +157,86 @@ yarn add @urql/exchange-multipart-fetch

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

```res
```rescript
open ReasonUrql
let client = Client.make(
~url="http://localhost:3000",
~exchanges=[|
~exchanges=[
Client.Exchanges.dedupExchange,
Client.Exchanges.cacheExchange,
Client.Exchanges.multipartFetchExchange
|],
],
()
)
```

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

### `retryExchange`

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.

To use the `retryExchange`, add the package to your dependencies:

```sh
yarn add @urql/exchange-retry
```

Then, add the exchange to your array of `exchanges`, specifying the options you want to configure:

```rescript
open ReasonUrql
let retryExchangeOptions =
Client.Exchanges.makeRetryExchangeOptions(~initialDelayMs=2000, ~randomDelay=false, ())
let client = Client.make(
~url="http://localhost:3000",
~exchanges=[
Client.Exchanges.dedupExchange,
Client.Exchanges.cacheExchange,
Client.Exchanges.retryExchange(retryExchangeOptions),
Client.Exchanges.fetchExchange
],
()
)
```

By default, `urql` will apply the following configuration for you:

```typescript
{
initialDelayMs: 1000,
maxDelayMs: 15000,
randomDelay: true,
maxNumberAttempts: 2,
retryIf: err => err && err.networkError,
}
```

If you want to use the defaults from `urql`, call `makeRetryExchangeOptions` with just a `unit` parameter.

```rescript
open ReasonUrql
let retryExchangeOptions =
Client.Exchanges.makeRetryExchangeOptions()
let client = Client.make(
~url="http://localhost:3000",
~exchanges=[
Client.Exchanges.dedupExchange,
Client.Exchanges.cacheExchange,
Client.Exchanges.retryExchange(retryExchangeOptions),
Client.Exchanges.fetchExchange
],
()
)
```

Read more on the `retryExchange` [here](https://formidable.com/open-source/urql/docs/advanced/retry-operations/).

## Custom Exchanges

`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/).
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"@glennsl/bs-jest": "^0.6.0",
"@reasonml-community/graphql-ppx": "^1.0.1",
"@urql/exchange-multipart-fetch": "^0.1.11",
"@urql/exchange-retry": "^0.2.0",
"all-contributors-cli": "^6.19.0",
"babel-jest": "^26.6.3",
"bs-platform": "^8.3.2",
Expand Down
26 changes: 26 additions & 0 deletions src/Client.res
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,32 @@ module Exchanges = {
@module("@urql/exchange-multipart-fetch")
external multipartFetchExchange: t = "multipartFetchExchange"

type retryExchangeOptions = {
initialDelayMs: option<int>,
maxDelayMs: option<int>,
randomDelay: option<bool>,
maxNumberAttempts: option<int>,
retryIf: option<(CombinedError.t, Types.operation) => bool>,
}

let makeRetryExchangeOptions = (
~initialDelayMs=?,
~maxDelayMs=?,
~randomDelay=?,
~maxNumberAttempts=?,
~retryIf=?,
(),
) => {
initialDelayMs: initialDelayMs,
maxDelayMs: maxDelayMs,
randomDelay: randomDelay,
maxNumberAttempts: maxNumberAttempts,
retryIf: retryIf,
}

@module("@urql/exchange-retry")
external retryExchange: retryExchangeOptions => t = "retryExchange"

/* Specific types for the subscriptionExchange. */
type observerLike<'value> = {
next: 'value => unit,
Expand Down
20 changes: 20 additions & 0 deletions src/Client.resi
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,26 @@ module Exchanges: {
@module("@urql/exchange-multipart-fetch")
external multipartFetchExchange: t = "multipartFetchExchange"

type retryExchangeOptions = {
initialDelayMs: option<int>,
maxDelayMs: option<int>,
randomDelay: option<bool>,
maxNumberAttempts: option<int>,
retryIf: option<(CombinedError.t, Types.operation) => bool>,
}

let makeRetryExchangeOptions: (
~initialDelayMs: int=?,
~maxDelayMs: int=?,
~randomDelay: bool=?,
~maxNumberAttempts: int=?,
~retryIf: (CombinedError.t, Types.operation) => bool=?,
unit,
) => retryExchangeOptions

@module("@urql/exchange-retry")
external retryExchange: retryExchangeOptions => t = "retryExchange"

/* Specific types for the subscriptionExchange. */
type observerLike<'value> = {
next: 'value => unit,
Expand Down
10 changes: 9 additions & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -918,7 +918,7 @@
dependencies:
"@types/yargs-parser" "*"

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

"@urql/exchange-retry@^0.2.0":
version "0.2.0"
resolved "https://registry.yarnpkg.com/@urql/exchange-retry/-/exchange-retry-0.2.0.tgz#e90a99bf8280ad2b8926bea7f2157a6f59dc8aec"
integrity sha512-eawDIkTSVudv+zMaOlm898UX9lkJnUry2PYqD7INeFWghkHmlIPm6wg5J/GBGAyFjqaOj1OWgAWYcu7sV4eljg==
dependencies:
"@urql/core" ">=1.15.0"
wonka "^4.0.14"

abab@^2.0.3:
version "2.0.5"
resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.5.tgz#c0b678fb32d60fc1219c784d6a826fe385aeb79a"
Expand Down

0 comments on commit e72ab10

Please sign in to comment.