CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-urql--core

The shared core for the highly customizable and versatile GraphQL client

Pending
Overview
Eval results
Files

exchanges.mddocs/

Exchange System

The exchange system is @urql/core's modular pipeline architecture for processing GraphQL operations. Exchanges handle caching, fetching, debugging, subscriptions, and can be composed to create custom operation processing workflows.

Capabilities

Core Exchanges

Essential exchanges for basic GraphQL client functionality.

/**
 * Document-based cache with automatic invalidation by typename
 * @returns Exchange for document caching
 */
function cacheExchange(): Exchange;

/**
 * HTTP fetch transport for GraphQL requests with multipart/streaming support
 * @returns Exchange for HTTP request handling
 */
function fetchExchange(): Exchange;

/**
 * Development logging exchange for debugging operations and results
 * @returns Exchange for development debugging
 */
function debugExchange(): Exchange;

Usage Examples:

import { createClient, cacheExchange, fetchExchange, debugExchange } from "@urql/core";

// Basic client with caching and fetching
const client = createClient({
  url: "https://api.example.com/graphql",
  exchanges: [cacheExchange, fetchExchange],
});

// Development client with debugging
const devClient = createClient({
  url: "https://api.example.com/graphql", 
  exchanges: [debugExchange, cacheExchange, fetchExchange],
});

Subscription Exchange

Handle GraphQL subscriptions with custom transport implementations.

/**
 * Generic subscription transport exchange
 * @param options - Subscription transport configuration
 * @returns Exchange for handling subscriptions
 */
function subscriptionExchange(options: SubscriptionExchangeOpts): Exchange;

interface SubscriptionExchangeOpts {
  /** Function that creates subscription transport */
  forwardSubscription: SubscriptionForwarder;
  /** Enable subscriptions via fetch instead of custom transport */
  enableAllOperations?: boolean;
}

type SubscriptionForwarder = (operation: SubscriptionOperation) => 
  Subscription | Source<ExecutionResult>;

type SubscriptionOperation = Operation & {
  kind: 'subscription';
};

Usage Examples:

import { subscriptionExchange } from "@urql/core";
import { Client as WSClient, createClient as createWSClient } from 'graphql-ws';

// WebSocket subscription transport
const wsClient = createWSClient({
  url: 'wss://api.example.com/graphql',
});

const client = createClient({
  url: "https://api.example.com/graphql",
  exchanges: [
    cacheExchange,
    subscriptionExchange({
      forwardSubscription: (operation) => ({
        subscribe: (sink) => ({
          unsubscribe: wsClient.subscribe(operation, sink),
        }),
      }),
    }),
    fetchExchange,
  ],
});

Server-Side Rendering Exchange

Cache and rehydrate results for server-side rendering scenarios.

/**
 * Server-side rendering exchange factory
 * @param params - SSR configuration options
 * @returns SSR exchange with serialization methods
 */
function ssrExchange(params: SSRExchangeParams): SSRExchange;

interface SSRExchangeParams {
  /** Initial data for hydration */
  initialState?: SSRData;
  /** Enable server-side result serialization */
  isClient?: boolean;
}

interface SSRExchange extends Exchange {
  /** Extract serialized data for client hydration */
  extractData(): SSRData;
  /** Replace current data with serialized data */
  restoreData(data: SSRData): void;
}

interface SSRData {
  [key: string]: SerializedResult;
}

interface SerializedResult {
  data?: any;
  error?: { 
    networkError?: string;
    graphQLErrors?: Array<{ message: string; [key: string]: any }>;
  };
}

Usage Examples:

import { ssrExchange } from "@urql/core";

// Server-side setup
const ssr = ssrExchange({ isClient: false });

const serverClient = createClient({
  url: "https://api.example.com/graphql",
  exchanges: [cacheExchange, ssr, fetchExchange],
});

// Execute queries on server
await serverClient.query(GetPageDataQuery, {}).toPromise();

// Extract data for client
const ssrData = ssr.extractData();

// Client-side setup with hydration
const clientSsr = ssrExchange({ 
  initialState: ssrData,
  isClient: true 
});

const clientClient = createClient({
  url: "https://api.example.com/graphql",
  exchanges: [cacheExchange, clientSsr, fetchExchange],
});

Map Exchange

Transform operations and results for custom processing, error handling, and middleware-like functionality.

/**
 * Exchange for transforming operations and results
 * @param options - Transformation functions
 * @returns Exchange that applies transformations
 */
function mapExchange(options: MapExchangeOpts): Exchange;

// Alias for backward compatibility
const errorExchange = mapExchange;

interface MapExchangeOpts {
  /** Transform operations before processing */
  onOperation?: (operation: Operation) => Operation;
  /** Transform results after processing */
  onResult?: (result: OperationResult, operation: Operation) => OperationResult;
  /** Handle errors from operations */
  onError?: (error: CombinedError, operation: Operation) => void;
}

Usage Examples:

import { mapExchange } from "@urql/core";

// Add authentication headers to operations
const authExchange = mapExchange({
  onOperation: (operation) => ({
    ...operation,
    context: {
      ...operation.context,
      fetchOptions: {
        ...operation.context.fetchOptions,
        headers: {
          ...operation.context.fetchOptions?.headers,
          authorization: `Bearer ${getAuthToken()}`,
        },
      },
    },
  }),
});

// Global error handling
const errorHandlingExchange = mapExchange({
  onError: (error, operation) => {
    if (error.networkError?.message?.includes('401')) {
      // Handle authentication error
      redirectToLogin();
    }
    console.error(`Operation ${operation.kind} failed:`, error);
  },
});

const client = createClient({
  url: "https://api.example.com/graphql",
  exchanges: [
    errorHandlingExchange,
    authExchange,
    cacheExchange,
    fetchExchange,
  ],
});

Exchange Composition

Combine multiple exchanges into a single exchange pipeline.

/**
 * Compose multiple exchanges into a single exchange
 * @param exchanges - Array of exchanges to compose
 * @returns Single composed exchange
 */
function composeExchanges(exchanges: Exchange[]): Exchange;

Usage Examples:

import { composeExchanges, cacheExchange, fetchExchange } from "@urql/core";

// Create a reusable exchange combination
const standardExchanges = composeExchanges([
  cacheExchange,
  fetchExchange,
]);

const client = createClient({
  url: "https://api.example.com/graphql", 
  exchanges: [standardExchanges],
});

// Compose with additional exchanges
const advancedExchanges = composeExchanges([
  debugExchange,
  cacheExchange,
  subscriptionExchange({ forwardSubscription }),
  fetchExchange,
]);

Custom Exchange Development

Create custom exchanges for specialized functionality.

/**
 * Exchange function signature
 * @param input - Exchange input with client and forward function
 * @returns ExchangeIO function that processes operations
 */
type Exchange = (input: ExchangeInput) => ExchangeIO;

/**
 * Exchange I/O function that processes operation streams
 * @param ops$ - Stream of operations to process
 * @returns Stream of operation results
 */
type ExchangeIO = (ops$: Source<Operation>) => Source<OperationResult>;

interface ExchangeInput {
  /** Client instance */
  client: Client;
  /** Forward function to next exchange */
  forward: ExchangeIO;
  /** Debug event dispatcher */
  dispatchDebug<T extends keyof DebugEventTypes | string>(
    event: DebugEventArg<T>
  ): void;
}

Usage Examples:

import { pipe, filter, onPush, merge, fromValue } from 'wonka';
import { Exchange } from "@urql/core";

// Simple logging exchange
const loggingExchange: Exchange = ({ forward }) => {
  return ops$ => pipe(
    ops$,
    onPush(operation => {
      console.log(`Executing ${operation.kind}:`, operation.query);
    }),
    forward,
    onPush(result => {
      console.log(`Result for ${result.operation.kind}:`, result);
    })
  );
};

// Caching exchange example
const memoryCacheExchange: Exchange = ({ forward }) => {
  const cache = new Map();
  
  return ops$ => {
    const sharedOps$ = pipe(ops$, share);
    
    return pipe(
      merge([
        // Check cache for queries
        pipe(
          sharedOps$,
          filter(op => op.kind === 'query'),
          mergeMap(operation => {
            const cached = cache.get(operation.key);
            if (cached) {
              return fromValue(cached);
            }
            return pipe(
              forward(fromValue(operation)),
              onPush(result => {
                if (result.data && !result.error) {
                  cache.set(operation.key, result);
                }
              })
            );
          })
        ),
        
        // Forward non-queries normally
        pipe(
          sharedOps$,
          filter(op => op.kind !== 'query'),
          forward
        )
      ])
    );
  };
};

Types

Exchange Core Types

type Exchange = (input: ExchangeInput) => ExchangeIO;
type ExchangeIO = (ops$: Source<Operation>) => Source<OperationResult>;

interface ExchangeInput {
  client: Client;
  forward: ExchangeIO;
  dispatchDebug<T extends keyof DebugEventTypes | string>(
    t: DebugEventArg<T>
  ): void;
}

Debug Event Types

interface DebugEventTypes {
  cacheHit: { value: any };
  cacheInvalidation: { typenames: string[]; response: OperationResult };
  fetchRequest: { url: string; fetchOptions: RequestInit };
  fetchSuccess: { url: string; fetchOptions: RequestInit; value: object };
  fetchError: { url: string; fetchOptions: RequestInit; value: Error };
  retryRetrying: { retryCount: number };
}

type DebugEventArg<T extends keyof DebugEventTypes | string> = {
  type: T;
  message: string;
  operation: Operation;
} & (T extends keyof DebugEventTypes
  ? { data: DebugEventTypes[T] }
  : { data?: any });

type DebugEvent<T extends keyof DebugEventTypes | string = string> =
  DebugEventArg<T> & {
    timestamp: number;
    source: string;
  };

Wonka Source Types

// Re-exported from wonka for exchange development
type Source<T> = import('wonka').Source<T>;
type Subscription = import('wonka').Subscription;

Install with Tessl CLI

npx tessl i tessl/npm-urql--core

docs

client.md

errors.md

exchanges.md

index.md

internal.md

operations.md

utilities.md

tile.json