The shared core for the highly customizable and versatile GraphQL client
—
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.
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],
});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,
],
});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],
});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,
],
});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,
]);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
)
])
);
};
};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;
}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;
};// 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