or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

client.mderrors.mdexchanges.mdindex.mdinternal.mdoperations.mdutilities.md

exchanges.mddocs/

0

# Exchange System

1

2

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.

3

4

## Capabilities

5

6

### Core Exchanges

7

8

Essential exchanges for basic GraphQL client functionality.

9

10

```typescript { .api }

11

/**

12

* Document-based cache with automatic invalidation by typename

13

* @returns Exchange for document caching

14

*/

15

function cacheExchange(): Exchange;

16

17

/**

18

* HTTP fetch transport for GraphQL requests with multipart/streaming support

19

* @returns Exchange for HTTP request handling

20

*/

21

function fetchExchange(): Exchange;

22

23

/**

24

* Development logging exchange for debugging operations and results

25

* @returns Exchange for development debugging

26

*/

27

function debugExchange(): Exchange;

28

```

29

30

**Usage Examples:**

31

32

```typescript

33

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

34

35

// Basic client with caching and fetching

36

const client = createClient({

37

url: "https://api.example.com/graphql",

38

exchanges: [cacheExchange, fetchExchange],

39

});

40

41

// Development client with debugging

42

const devClient = createClient({

43

url: "https://api.example.com/graphql",

44

exchanges: [debugExchange, cacheExchange, fetchExchange],

45

});

46

```

47

48

### Subscription Exchange

49

50

Handle GraphQL subscriptions with custom transport implementations.

51

52

```typescript { .api }

53

/**

54

* Generic subscription transport exchange

55

* @param options - Subscription transport configuration

56

* @returns Exchange for handling subscriptions

57

*/

58

function subscriptionExchange(options: SubscriptionExchangeOpts): Exchange;

59

60

interface SubscriptionExchangeOpts {

61

/** Function that creates subscription transport */

62

forwardSubscription: SubscriptionForwarder;

63

/** Enable subscriptions via fetch instead of custom transport */

64

enableAllOperations?: boolean;

65

}

66

67

type SubscriptionForwarder = (operation: SubscriptionOperation) =>

68

Subscription | Source<ExecutionResult>;

69

70

type SubscriptionOperation = Operation & {

71

kind: 'subscription';

72

};

73

```

74

75

**Usage Examples:**

76

77

```typescript

78

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

79

import { Client as WSClient, createClient as createWSClient } from 'graphql-ws';

80

81

// WebSocket subscription transport

82

const wsClient = createWSClient({

83

url: 'wss://api.example.com/graphql',

84

});

85

86

const client = createClient({

87

url: "https://api.example.com/graphql",

88

exchanges: [

89

cacheExchange,

90

subscriptionExchange({

91

forwardSubscription: (operation) => ({

92

subscribe: (sink) => ({

93

unsubscribe: wsClient.subscribe(operation, sink),

94

}),

95

}),

96

}),

97

fetchExchange,

98

],

99

});

100

```

101

102

### Server-Side Rendering Exchange

103

104

Cache and rehydrate results for server-side rendering scenarios.

105

106

```typescript { .api }

107

/**

108

* Server-side rendering exchange factory

109

* @param params - SSR configuration options

110

* @returns SSR exchange with serialization methods

111

*/

112

function ssrExchange(params: SSRExchangeParams): SSRExchange;

113

114

interface SSRExchangeParams {

115

/** Initial data for hydration */

116

initialState?: SSRData;

117

/** Enable server-side result serialization */

118

isClient?: boolean;

119

}

120

121

interface SSRExchange extends Exchange {

122

/** Extract serialized data for client hydration */

123

extractData(): SSRData;

124

/** Replace current data with serialized data */

125

restoreData(data: SSRData): void;

126

}

127

128

interface SSRData {

129

[key: string]: SerializedResult;

130

}

131

132

interface SerializedResult {

133

data?: any;

134

error?: {

135

networkError?: string;

136

graphQLErrors?: Array<{ message: string; [key: string]: any }>;

137

};

138

}

139

```

140

141

**Usage Examples:**

142

143

```typescript

144

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

145

146

// Server-side setup

147

const ssr = ssrExchange({ isClient: false });

148

149

const serverClient = createClient({

150

url: "https://api.example.com/graphql",

151

exchanges: [cacheExchange, ssr, fetchExchange],

152

});

153

154

// Execute queries on server

155

await serverClient.query(GetPageDataQuery, {}).toPromise();

156

157

// Extract data for client

158

const ssrData = ssr.extractData();

159

160

// Client-side setup with hydration

161

const clientSsr = ssrExchange({

162

initialState: ssrData,

163

isClient: true

164

});

165

166

const clientClient = createClient({

167

url: "https://api.example.com/graphql",

168

exchanges: [cacheExchange, clientSsr, fetchExchange],

169

});

170

```

171

172

### Map Exchange

173

174

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

175

176

```typescript { .api }

177

/**

178

* Exchange for transforming operations and results

179

* @param options - Transformation functions

180

* @returns Exchange that applies transformations

181

*/

182

function mapExchange(options: MapExchangeOpts): Exchange;

183

184

// Alias for backward compatibility

185

const errorExchange = mapExchange;

186

187

interface MapExchangeOpts {

188

/** Transform operations before processing */

189

onOperation?: (operation: Operation) => Operation;

190

/** Transform results after processing */

191

onResult?: (result: OperationResult, operation: Operation) => OperationResult;

192

/** Handle errors from operations */

193

onError?: (error: CombinedError, operation: Operation) => void;

194

}

195

```

196

197

**Usage Examples:**

198

199

```typescript

200

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

201

202

// Add authentication headers to operations

203

const authExchange = mapExchange({

204

onOperation: (operation) => ({

205

...operation,

206

context: {

207

...operation.context,

208

fetchOptions: {

209

...operation.context.fetchOptions,

210

headers: {

211

...operation.context.fetchOptions?.headers,

212

authorization: `Bearer ${getAuthToken()}`,

213

},

214

},

215

},

216

}),

217

});

218

219

// Global error handling

220

const errorHandlingExchange = mapExchange({

221

onError: (error, operation) => {

222

if (error.networkError?.message?.includes('401')) {

223

// Handle authentication error

224

redirectToLogin();

225

}

226

console.error(`Operation ${operation.kind} failed:`, error);

227

},

228

});

229

230

const client = createClient({

231

url: "https://api.example.com/graphql",

232

exchanges: [

233

errorHandlingExchange,

234

authExchange,

235

cacheExchange,

236

fetchExchange,

237

],

238

});

239

```

240

241

### Exchange Composition

242

243

Combine multiple exchanges into a single exchange pipeline.

244

245

```typescript { .api }

246

/**

247

* Compose multiple exchanges into a single exchange

248

* @param exchanges - Array of exchanges to compose

249

* @returns Single composed exchange

250

*/

251

function composeExchanges(exchanges: Exchange[]): Exchange;

252

```

253

254

**Usage Examples:**

255

256

```typescript

257

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

258

259

// Create a reusable exchange combination

260

const standardExchanges = composeExchanges([

261

cacheExchange,

262

fetchExchange,

263

]);

264

265

const client = createClient({

266

url: "https://api.example.com/graphql",

267

exchanges: [standardExchanges],

268

});

269

270

// Compose with additional exchanges

271

const advancedExchanges = composeExchanges([

272

debugExchange,

273

cacheExchange,

274

subscriptionExchange({ forwardSubscription }),

275

fetchExchange,

276

]);

277

```

278

279

### Custom Exchange Development

280

281

Create custom exchanges for specialized functionality.

282

283

```typescript { .api }

284

/**

285

* Exchange function signature

286

* @param input - Exchange input with client and forward function

287

* @returns ExchangeIO function that processes operations

288

*/

289

type Exchange = (input: ExchangeInput) => ExchangeIO;

290

291

/**

292

* Exchange I/O function that processes operation streams

293

* @param ops$ - Stream of operations to process

294

* @returns Stream of operation results

295

*/

296

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

297

298

interface ExchangeInput {

299

/** Client instance */

300

client: Client;

301

/** Forward function to next exchange */

302

forward: ExchangeIO;

303

/** Debug event dispatcher */

304

dispatchDebug<T extends keyof DebugEventTypes | string>(

305

event: DebugEventArg<T>

306

): void;

307

}

308

```

309

310

**Usage Examples:**

311

312

```typescript

313

import { pipe, filter, onPush, merge, fromValue } from 'wonka';

314

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

315

316

// Simple logging exchange

317

const loggingExchange: Exchange = ({ forward }) => {

318

return ops$ => pipe(

319

ops$,

320

onPush(operation => {

321

console.log(`Executing ${operation.kind}:`, operation.query);

322

}),

323

forward,

324

onPush(result => {

325

console.log(`Result for ${result.operation.kind}:`, result);

326

})

327

);

328

};

329

330

// Caching exchange example

331

const memoryCacheExchange: Exchange = ({ forward }) => {

332

const cache = new Map();

333

334

return ops$ => {

335

const sharedOps$ = pipe(ops$, share);

336

337

return pipe(

338

merge([

339

// Check cache for queries

340

pipe(

341

sharedOps$,

342

filter(op => op.kind === 'query'),

343

mergeMap(operation => {

344

const cached = cache.get(operation.key);

345

if (cached) {

346

return fromValue(cached);

347

}

348

return pipe(

349

forward(fromValue(operation)),

350

onPush(result => {

351

if (result.data && !result.error) {

352

cache.set(operation.key, result);

353

}

354

})

355

);

356

})

357

),

358

359

// Forward non-queries normally

360

pipe(

361

sharedOps$,

362

filter(op => op.kind !== 'query'),

363

forward

364

)

365

])

366

);

367

};

368

};

369

```

370

371

## Types

372

373

### Exchange Core Types

374

375

```typescript { .api }

376

type Exchange = (input: ExchangeInput) => ExchangeIO;

377

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

378

379

interface ExchangeInput {

380

client: Client;

381

forward: ExchangeIO;

382

dispatchDebug<T extends keyof DebugEventTypes | string>(

383

t: DebugEventArg<T>

384

): void;

385

}

386

```

387

388

### Debug Event Types

389

390

```typescript { .api }

391

interface DebugEventTypes {

392

cacheHit: { value: any };

393

cacheInvalidation: { typenames: string[]; response: OperationResult };

394

fetchRequest: { url: string; fetchOptions: RequestInit };

395

fetchSuccess: { url: string; fetchOptions: RequestInit; value: object };

396

fetchError: { url: string; fetchOptions: RequestInit; value: Error };

397

retryRetrying: { retryCount: number };

398

}

399

400

type DebugEventArg<T extends keyof DebugEventTypes | string> = {

401

type: T;

402

message: string;

403

operation: Operation;

404

} & (T extends keyof DebugEventTypes

405

? { data: DebugEventTypes[T] }

406

: { data?: any });

407

408

type DebugEvent<T extends keyof DebugEventTypes | string = string> =

409

DebugEventArg<T> & {

410

timestamp: number;

411

source: string;

412

};

413

```

414

415

### Wonka Source Types

416

417

```typescript { .api }

418

// Re-exported from wonka for exchange development

419

type Source<T> = import('wonka').Source<T>;

420

type Subscription = import('wonka').Subscription;

421

```