Skip to content

Retry Exchange

Automatically retry failed requests with exponential backoff.

Basic Usage

typescript
import { createClient, retryExchange, httpExchange } from '@mearie/react'; // or @mearie/vue, @mearie/svelte, @mearie/solid
import { schema } from '$mearie';

export const client = createClient({
  schema,
  exchanges: [retryExchange(), httpExchange({ url: 'https://api.example.com/graphql' })],
});

Configuration

Max Attempts

Set the maximum number of retry attempts:

typescript
export const client = createClient({
  schema,
  exchanges: [
    retryExchange({
      maxAttempts: 3, // Default: 3
    }),
    httpExchange({ url: 'https://api.example.com/graphql' }),
  ],
});

Backoff Strategy

Customize the delay between retries:

typescript
export const client = createClient({
  schema,
  exchanges: [
    retryExchange({
      backoff: (attempt) => {
        // Exponential backoff: 1s, 2s, 4s, 8s...
        return Math.min(1000 * 2 ** attempt, 30000);
      },
    }),
    httpExchange({ url: 'https://api.example.com/graphql' }),
  ],
});

Default backoff: Math.min(1000 * 2 ** attempt, 30000) (max 30 seconds)

Jitter

Add randomness to prevent thundering herd:

typescript
export const client = createClient({
  schema,
  exchanges: [
    retryExchange({
      jitter: true, // Default: false
    }),
    httpExchange({ url: 'https://api.example.com/graphql' }),
  ],
});

When enabled, jitter adds random variation to backoff delays, preventing simultaneous retries across multiple users.

Retry Condition

Control which errors should be retried:

typescript
export const client = createClient({
  schema,
  exchanges: [
    retryExchange({
      retryIf: (error) => {
        // Retry on network errors
        if (error.name === 'NetworkError') return true;

        // Retry on 5xx server errors
        if (error.status >= 500 && error.status < 600) return true;

        // Don't retry on other errors
        return false;
      },
    }),
    httpExchange({ url: 'https://api.example.com/graphql' }),
  ],
});

Default: Retries on network errors and 5xx server errors.

How It Works

  1. Catches errors from downstream exchanges
  2. Checks if error should be retried (using retryIf)
  3. Waits for backoff delay
  4. Retries operation until success or max attempts reached

Common Patterns

Retry Only Network Errors

typescript
export const client = createClient({
  schema,
  exchanges: [
    retryExchange({
      retryIf: (error) => error.name === 'NetworkError',
    }),
    httpExchange({ url: 'https://api.example.com/graphql' }),
  ],
});

Retry with Linear Backoff

typescript
export const client = createClient({
  schema,
  exchanges: [
    retryExchange({
      backoff: (attempt) => attempt * 1000, // 1s, 2s, 3s, 4s...
    }),
    httpExchange({ url: 'https://api.example.com/graphql' }),
  ],
});

Monitor Retry Attempts

typescript
export const client = createClient({
  schema,
  exchanges: [
    retryExchange({
      maxAttempts: 3,
      onRetry: (attempt, error) => {
        console.log(`Retry attempt ${attempt}:`, error.message);
      },
    }),
    httpExchange({ url: 'https://api.example.com/graphql' }),
  ],
});

Mutation Safety

Mutations are never retried by default, since they may not be idempotent:

typescript
// This will NOT be retried, even with retryExchange
const [createUser] = useMutation(
  graphql(`
    mutation CreateUser($name: String!) {
      createUser(input: { name: $name }) {
        id
        name
      }
    }
  `),
);

Subscription Reconnection

Subscriptions automatically reconnect when the connection is lost, independent of retryExchange. See Subscription Exchange for more information.

Request Cancellation

Retries respect AbortController:

typescript
const controller = new AbortController();

client.query(GetUserQuery, { id: '1' }, { signal: controller.signal });

// Aborts the current attempt and all future retries
controller.abort();

Exchange Chain Placement

Place retryExchange early in the chain, after dedup but before cache:

typescript
export const client = createClient({
  schema,
  exchanges: [
    dedupExchange(),
    retryExchange(), // After dedup
    cacheExchange(),
    httpExchange({ url: 'https://api.example.com/graphql' }),
  ],
});

This ensures retries trigger the entire cache + HTTP flow.

Next Steps