0
# @pollyjs/adapter-fetch
1
2
The @pollyjs/adapter-fetch package provides a fetch adapter that enables seamless recording and replaying of HTTP interactions using the browser's native fetch API and Request/Response objects. It integrates with Polly.JS to intercept fetch calls, preserve request/response data, and replay interactions for deterministic testing.
3
4
## Package Information
5
6
- **Package Name**: @pollyjs/adapter-fetch
7
- **Package Type**: npm
8
- **Language**: JavaScript (with TypeScript definitions)
9
- **Installation**: `npm install @pollyjs/adapter-fetch -D`
10
11
## Core Imports
12
13
```javascript
14
import FetchAdapter from '@pollyjs/adapter-fetch';
15
```
16
17
For CommonJS:
18
19
```javascript
20
const FetchAdapter = require('@pollyjs/adapter-fetch');
21
```
22
23
## Basic Usage
24
25
```javascript
26
import { Polly } from '@pollyjs/core';
27
import FetchAdapter from '@pollyjs/adapter-fetch';
28
29
// Register the adapter with Polly
30
Polly.register(FetchAdapter);
31
32
// Create a Polly instance that uses the fetch adapter
33
const polly = new Polly('Recording Name', {
34
adapters: ['fetch']
35
});
36
37
// The adapter will now intercept all fetch calls
38
const response = await fetch('/api/users');
39
const data = await response.json();
40
41
// Stop recording/replaying
42
await polly.stop();
43
```
44
45
## Capabilities
46
47
### FetchAdapter Class
48
49
The main adapter class that extends the base Polly.JS Adapter to provide fetch API interception and request/response handling.
50
51
```javascript { .api }
52
/**
53
* Fetch adapter that patches the global fetch function and Request constructor
54
* to enable recording and replaying of HTTP interactions.
55
*/
56
export default class FetchAdapter extends Adapter {
57
/**
58
* Static identifier for the adapter
59
* @returns {string} Always returns 'fetch'
60
*/
61
static get id(): string;
62
63
/**
64
* Default configuration options for the adapter
65
* @returns {object} Configuration object with context property
66
*/
67
get defaultOptions(): {
68
context?: any; // Global context object (default: global)
69
};
70
71
/**
72
* Called when adapter connects to Polly instance.
73
* Patches global fetch and Request constructor.
74
*/
75
onConnect(): void;
76
77
/**
78
* Called when adapter disconnects from Polly instance.
79
* Restores original global fetch and Request constructor.
80
*/
81
onDisconnect(): void;
82
83
/**
84
* Called when a request is being processed.
85
* Handles abort signal setup and event listeners.
86
* @param {object} pollyRequest - The Polly request object
87
*/
88
onRequest(pollyRequest: object): void;
89
90
/**
91
* Makes the actual fetch request and processes the response.
92
* Handles binary data conversion and buffer compatibility.
93
* @param {object} pollyRequest - The Polly request object
94
* @returns {Promise<object>} Response object with statusCode, headers, body, encoding
95
*/
96
async onFetchResponse(pollyRequest: object): Promise<{
97
statusCode: number;
98
headers: object;
99
body: string;
100
encoding?: string;
101
}>;
102
103
/**
104
* Called when responding to the original caller.
105
* Handles response construction and error scenarios.
106
* @param {object} pollyRequest - The Polly request object
107
* @param {Error} error - Any error that occurred during processing
108
*/
109
onRespond(pollyRequest: object, error?: Error): void;
110
}
111
```
112
113
### Configuration Options
114
115
The adapter accepts configuration options through the Polly instance:
116
117
```javascript { .api }
118
interface AdapterOptions {
119
/**
120
* Global context object containing fetch, Request, Response, Headers.
121
* Defaults to the global object (window in browsers, global in Node.js).
122
*/
123
context?: {
124
fetch: Function;
125
Request: new (url: string | Request, options?: RequestInit) => Request;
126
Response: new (body?: any, options?: ResponseInit) => Response;
127
Headers: new (headers?: HeadersInit) => Headers;
128
};
129
}
130
```
131
132
**Usage Example:**
133
134
```javascript
135
const polly = new Polly('Recording', {
136
adapters: ['fetch'],
137
adapterOptions: {
138
fetch: {
139
context: window // Explicitly specify the global context
140
}
141
}
142
});
143
```
144
145
## Features
146
147
### Request Interception
148
149
The adapter patches the global `fetch` function to intercept all fetch calls, supporting:
150
151
- **URL objects**: Accepts both string URLs and URL instances
152
- **Request objects**: Supports fetch calls with Request instances
153
- **All fetch options**: Headers, method, body, credentials, etc.
154
- **Request cloning**: Maintains fetch API clone semantics
155
156
### Response Handling
157
158
Comprehensive response processing including:
159
160
- **Binary data support**: Handles both text and binary response bodies
161
- **Encoding preservation**: Uses base64 encoding for binary content
162
- **Header serialization**: Converts Headers instances to plain objects
163
- **Status code mapping**: Preserves HTTP status codes and status text
164
- **Empty response handling**: Correctly handles 204 No Content responses
165
166
### Abort Signal Support
167
168
Full AbortController/AbortSignal integration:
169
170
```javascript
171
const controller = new AbortController();
172
173
// This request can be aborted
174
fetch('/api/data', { signal: controller.signal });
175
176
// Abort the request
177
controller.abort(); // Throws DOMException with name 'AbortError'
178
```
179
180
### Cross-Environment Compatibility
181
182
The adapter works across different JavaScript environments:
183
184
- **Browser environments**: Uses window.fetch and related globals
185
- **Node.js environments**: Works with fetch polyfills or native Node.js fetch
186
- **Different contexts**: Supports custom global contexts for testing scenarios
187
188
### Error Handling
189
190
Robust error handling for various scenarios:
191
192
- **Missing globals**: Validates fetch, Request, Response, Headers are available
193
- **Concurrent adapters**: Prevents multiple fetch adapters from running simultaneously
194
- **Network errors**: Properly propagates fetch network failures
195
- **Abort scenarios**: Handles request cancellation with proper DOMException
196
- **Buffer compatibility**: Manages ArrayBuffer differences across contexts
197
198
## Internal Implementation Details
199
200
The adapter uses several internal symbols and patterns:
201
202
- **Symbol-based marking**: Uses symbols to mark patched globals and prevent double-patching
203
- **Argument preservation**: Stores original Request constructor arguments for proper cloning
204
- **Lazy execution**: Operations are queued and executed when needed
205
- **Response URL fixing**: Manually sets response.url since Response constructor doesn't allow it
206
207
## Error Types
208
209
```javascript { .api }
210
/**
211
* Errors that may be thrown by the adapter
212
*/
213
interface AdapterErrors {
214
/** Thrown when required globals (fetch, Request, etc.) are not found */
215
AssertionError: Error;
216
217
/** Thrown when request is aborted via AbortSignal */
218
AbortError: DOMException;
219
220
/** Network or other fetch-related errors */
221
TypeError: Error;
222
}
223
```
224
225
**Common Error Scenarios:**
226
227
```javascript
228
// Missing globals
229
// Error: "fetch global not found."
230
231
// Concurrent adapters
232
// Error: "Running concurrent fetch adapters is unsupported, stop any running Polly instances."
233
234
// Aborted request
235
// DOMException: "The user aborted a request." (name: 'AbortError')
236
```
237
238
## Advanced Usage Patterns
239
240
### Custom Context
241
242
For testing or specialized environments:
243
244
```javascript
245
const customGlobals = {
246
fetch: mockFetch,
247
Request: MockRequest,
248
Response: MockResponse,
249
Headers: MockHeaders
250
};
251
252
const polly = new Polly('Test', {
253
adapters: ['fetch'],
254
adapterOptions: {
255
fetch: {
256
context: customGlobals
257
}
258
}
259
});
260
```
261
262
### Integration with Testing
263
264
Common pattern for test suites:
265
266
```javascript
267
import { Polly } from '@pollyjs/core';
268
import FetchAdapter from '@pollyjs/adapter-fetch';
269
270
// Register once in test setup
271
Polly.register(FetchAdapter);
272
273
describe('API Tests', () => {
274
let polly;
275
276
beforeEach(() => {
277
polly = new Polly('API Test', {
278
adapters: ['fetch'],
279
recordIfMissing: false // Use existing recordings
280
});
281
});
282
283
afterEach(async () => {
284
await polly.stop();
285
});
286
287
it('fetches user data', async () => {
288
const response = await fetch('/api/users/1');
289
const user = await response.json();
290
291
expect(user.id).toBe(1);
292
});
293
});
294
```