diff --git a/README.md b/README.md
index 8daf2b3f..d4744d39 100644
--- a/README.md
+++ b/README.md
@@ -566,6 +566,26 @@ ReactDOM.render(document.getElementByID('graphiql'), );
+
+🔗 Client usage with retry on any connection problem
+
+```typescript
+import { createClient } from 'graphql-ws';
+import { waitForHealthy } from './my-servers';
+
+const client = createClient({
+ url: 'ws://any.retry:4000/graphql',
+ // by default the client will immediately fail on any non-fatal
+ // `CloseEvent` problem thrown during the connection phase
+ //
+ // see `retryAttempts` documentation about which `CloseEvent`s are
+ // considered fatal regardless
+ shouldRetry: () => true,
+});
+```
+
+
+
🔗 Client usage with custom retry timeout strategy
diff --git a/docs/interfaces/client.ClientOptions.md b/docs/interfaces/client.ClientOptions.md
index 79164422..b3f653df 100644
--- a/docs/interfaces/client.ClientOptions.md
+++ b/docs/interfaces/client.ClientOptions.md
@@ -35,6 +35,7 @@ Configuration used for the GraphQL over WebSocket client.
- [isFatalConnectionProblem](client.ClientOptions.md#isfatalconnectionproblem)
- [onNonLazyError](client.ClientOptions.md#onnonlazyerror)
- [retryWait](client.ClientOptions.md#retrywait)
+- [shouldRetry](client.ClientOptions.md#shouldretry)
## Properties
@@ -259,6 +260,8 @@ ___
â–¸ `Optional` **isFatalConnectionProblem**(`errOrCloseEvent`): `boolean`
+**`deprecated`** Use `shouldRetry` instead.
+
Check if the close event or connection error is fatal. If you return `true`,
the client will fail immediately without additional retries; however, if you
return `false`, the client will keep retrying until the `retryAttempts` have
@@ -271,7 +274,7 @@ Beware, the library classifies a few close events as fatal regardless of
what is returned. They are listed in the documentation of the `retryAttempts`
option.
-**`default`** 'Any non-CloseEvent'
+**`default`** 'Any non-`CloseEvent`'
#### Parameters
@@ -339,3 +342,32 @@ by timing the resolution of the returned promise with the retries count.
#### Returns
`Promise`<`void`\>
+
+___
+
+### shouldRetry
+
+â–¸ `Optional` **shouldRetry**(`errOrCloseEvent`): `boolean`
+
+Check if the close event or connection error is fatal. If you return `false`,
+the client will fail immediately without additional retries; however, if you
+return `true`, the client will keep retrying until the `retryAttempts` have
+been exceeded.
+
+The argument is whatever has been thrown during the connection phase.
+
+Beware, the library classifies a few close events as fatal regardless of
+what is returned here. They are listed in the documentation of the `retryAttempts`
+option.
+
+**`default`** 'Only `CloseEvent`s'
+
+#### Parameters
+
+| Name | Type |
+| :------ | :------ |
+| `errOrCloseEvent` | `unknown` |
+
+#### Returns
+
+`boolean`
diff --git a/src/__tests__/client.ts b/src/__tests__/client.ts
index c2ae0dbd..05919200 100644
--- a/src/__tests__/client.ts
+++ b/src/__tests__/client.ts
@@ -1612,7 +1612,10 @@ describe('reconnecting', () => {
createClient({
url,
retryAttempts: Infinity, // keep retrying forever
- isFatalConnectionProblem: () => true, // even if all connection probles are fatal
+ // even if all connection problems are fatal
+ shouldRetry: () => false,
+ // @deprecated
+ isFatalConnectionProblem: () => true,
}),
{
query: 'subscription { ping }',
@@ -1648,6 +1651,33 @@ describe('reconnecting', () => {
it('should report fatal connection problems immediately', async () => {
const { url, ...server } = await startTServer();
+ const sub = tsubscribe(
+ createClient({
+ url,
+ retryAttempts: Infinity, // keep retrying forever
+ shouldRetry: (err) => {
+ expect((err as CloseEvent).code).toBe(4444);
+ expect((err as CloseEvent).reason).toBe('Is fatal?');
+ return false;
+ },
+ }),
+ {
+ query: 'subscription { ping }',
+ },
+ );
+
+ await server.waitForClient((client) => {
+ client.close(4444, 'Is fatal?');
+ });
+
+ await sub.waitForError((err) => {
+ expect((err as CloseEvent).code).toBe(4444);
+ }, 20);
+ });
+
+ it('should report fatal connection problems immediately (using deprecated `isFatalConnectionProblem`)', async () => {
+ const { url, ...server } = await startTServer();
+
const sub = tsubscribe(
createClient({
url,
diff --git a/src/client.ts b/src/client.ts
index 1cad17e3..84990500 100644
--- a/src/client.ts
+++ b/src/client.ts
@@ -355,6 +355,23 @@ export interface ClientOptions<
*/
retryWait?: (retries: number) => Promise;
/**
+ * Check if the close event or connection error is fatal. If you return `false`,
+ * the client will fail immediately without additional retries; however, if you
+ * return `true`, the client will keep retrying until the `retryAttempts` have
+ * been exceeded.
+ *
+ * The argument is whatever has been thrown during the connection phase.
+ *
+ * Beware, the library classifies a few close events as fatal regardless of
+ * what is returned here. They are listed in the documentation of the `retryAttempts`
+ * option.
+ *
+ * @default 'Only `CloseEvent`s'
+ */
+ shouldRetry?: (errOrCloseEvent: unknown) => boolean;
+ /**
+ * @deprecated Use `shouldRetry` instead.
+ *
* Check if the close event or connection error is fatal. If you return `true`,
* the client will fail immediately without additional retries; however, if you
* return `false`, the client will keep retrying until the `retryAttempts` have
@@ -367,7 +384,7 @@ export interface ClientOptions<
* what is returned. They are listed in the documentation of the `retryAttempts`
* option.
*
- * @default 'Any non-CloseEvent'
+ * @default 'Any non-`CloseEvent`'
*/
isFatalConnectionProblem?: (errOrCloseEvent: unknown) => boolean;
/**
@@ -470,6 +487,7 @@ export function createClient<
),
);
},
+ shouldRetry = isLikeCloseEvent,
isFatalConnectionProblem = (errOrCloseEvent) =>
// non `CloseEvent`s are fatal by default
!isLikeCloseEvent(errOrCloseEvent),
@@ -827,7 +845,10 @@ export function createClient<
// retries are not allowed or we tried to many times, report error
if (!retryAttempts || retries >= retryAttempts) throw errOrCloseEvent;
- // throw fatal connection problems immediately
+ // throw non-retryable connection problems
+ if (!shouldRetry(errOrCloseEvent)) throw errOrCloseEvent;
+
+ // @deprecated throw fatal connection problems immediately
if (isFatalConnectionProblem(errOrCloseEvent)) throw errOrCloseEvent;
// looks good, start retrying