0
# Request Matching
1
2
This document covers advanced request matching capabilities including headers, query parameters, request bodies, and path filtering.
3
4
## Query Parameter Matching
5
6
Match requests based on query string parameters using the `query` method.
7
8
```javascript { .api }
9
interface Interceptor {
10
query(matcher: QueryMatcher): this;
11
}
12
13
type QueryMatcher =
14
| boolean
15
| string
16
| DataMatcherMap
17
| URLSearchParams
18
| ((parsedObj: ParsedUrlQuery) => boolean);
19
```
20
21
### Boolean Matching
22
23
```javascript
24
// Match any query parameters (or none)
25
nock("https://api.example.com")
26
.get("/users")
27
.query(true)
28
.reply(200, []);
29
30
// Match only requests with no query parameters
31
nock("https://api.example.com")
32
.get("/users")
33
.query(false)
34
.reply(200, []);
35
```
36
37
### String Matching
38
39
```javascript
40
// Match exact query string
41
nock("https://api.example.com")
42
.get("/users")
43
.query("page=1&limit=10")
44
.reply(200, []);
45
46
// Order doesn't matter
47
nock("https://api.example.com")
48
.get("/users")
49
.query("limit=10&page=1") // Will also match page=1&limit=10
50
.reply(200, []);
51
```
52
53
### Object Matching
54
55
```javascript
56
// Match specific parameters with exact values
57
nock("https://api.example.com")
58
.get("/users")
59
.query({ page: "1", limit: "10" })
60
.reply(200, []);
61
62
// Use matchers for flexible matching
63
nock("https://api.example.com")
64
.get("/users")
65
.query({
66
page: /^\d+$/, // Any numeric page
67
limit: (value) => parseInt(value) <= 100 // Limit validation
68
})
69
.reply(200, []);
70
```
71
72
### URLSearchParams Matching
73
74
```javascript
75
const params = new URLSearchParams();
76
params.set("category", "electronics");
77
params.set("inStock", "true");
78
79
nock("https://api.example.com")
80
.get("/products")
81
.query(params)
82
.reply(200, []);
83
```
84
85
### Function-Based Matching
86
87
```javascript
88
nock("https://api.example.com")
89
.get("/search")
90
.query((queryObj) => {
91
// Match if query contains 'q' parameter and it's not empty
92
return queryObj.q && queryObj.q.length > 0;
93
})
94
.reply(200, { results: [] });
95
```
96
97
## Header Matching
98
99
Match requests based on HTTP headers using various header matching methods.
100
101
### Scope-Level Header Matching
102
103
```javascript { .api }
104
interface Scope {
105
matchHeader(name: string, value: RequestHeaderMatcher): this;
106
}
107
```
108
109
Apply header matching to all interceptors in a scope:
110
111
```javascript
112
// All requests in this scope must have this header
113
const scope = nock("https://api.example.com")
114
.matchHeader("authorization", "Bearer token123")
115
.matchHeader("content-type", "application/json");
116
117
scope.get("/users").reply(200, []);
118
scope.post("/users").reply(201, { id: 1 });
119
```
120
121
### Interceptor-Level Header Matching
122
123
```javascript { .api }
124
interface Interceptor {
125
matchHeader(name: string, value: RequestHeaderMatcher): this;
126
}
127
128
type RequestHeaderMatcher = string | RegExp | ((fieldValue: string) => boolean);
129
```
130
131
Apply header matching to specific interceptors:
132
133
```javascript
134
// Exact string match
135
nock("https://api.example.com")
136
.get("/users")
137
.matchHeader("accept", "application/json")
138
.reply(200, []);
139
140
// RegExp match
141
nock("https://api.example.com")
142
.get("/users")
143
.matchHeader("user-agent", /Chrome/)
144
.reply(200, []);
145
146
// Function match
147
nock("https://api.example.com")
148
.get("/users")
149
.matchHeader("authorization", (value) => {
150
return value.startsWith("Bearer ") && value.length > 20;
151
})
152
.reply(200, []);
153
```
154
155
### Basic Authentication
156
157
```javascript { .api }
158
interface Interceptor {
159
basicAuth(options: { user: string; pass?: string }): this;
160
}
161
```
162
163
Match HTTP Basic Authentication credentials:
164
165
```javascript
166
// With username and password
167
nock("https://api.example.com")
168
.get("/secure")
169
.basicAuth({ user: "admin", pass: "secret" })
170
.reply(200, { message: "Authorized" });
171
172
// Username only
173
nock("https://api.example.com")
174
.get("/user-only")
175
.basicAuth({ user: "admin" })
176
.reply(200, { message: "User authenticated" });
177
```
178
179
## Request Body Matching
180
181
Match requests based on their body content. This is commonly used with POST, PUT, and PATCH requests.
182
183
### Exact Body Matching
184
185
```javascript
186
// Exact string match
187
nock("https://api.example.com")
188
.post("/users", "name=Alice&email=alice@example.com")
189
.reply(201, { id: 1 });
190
191
// Exact JSON object match
192
nock("https://api.example.com")
193
.post("/users", { name: "Alice", email: "alice@example.com" })
194
.reply(201, { id: 1 });
195
196
// Buffer match
197
const bodyBuffer = Buffer.from("binary data");
198
nock("https://api.example.com")
199
.post("/upload", bodyBuffer)
200
.reply(200, { uploaded: true });
201
```
202
203
### RegExp Body Matching
204
205
```javascript
206
// Match body with regex
207
nock("https://api.example.com")
208
.post("/users", /Alice/)
209
.reply(201, { id: 1 });
210
211
// Match JSON structure with regex
212
nock("https://api.example.com")
213
.post("/users", {
214
name: /^[A-Z][a-z]+$/, // Capitalized name
215
email: /@example\.com$/ // Example.com email
216
})
217
.reply(201, { id: 1 });
218
```
219
220
### Function-Based Body Matching
221
222
```javascript { .api }
223
type RequestBodyMatcher =
224
| string
225
| Buffer
226
| RegExp
227
| DataMatcherArray
228
| DataMatcherMap
229
| ((body: any) => boolean);
230
```
231
232
```javascript
233
nock("https://api.example.com")
234
.post("/users", (body) => {
235
const user = JSON.parse(body);
236
return user.name && user.email && user.email.includes("@");
237
})
238
.reply(201, { id: 1 });
239
```
240
241
## Path Filtering
242
243
Transform request paths before matching, useful for handling dynamic segments or normalizing paths.
244
245
### RegExp Path Filtering
246
247
```javascript { .api }
248
interface Scope {
249
filteringPath(regex: RegExp, replace: string): this;
250
}
251
```
252
253
```javascript
254
// Replace user IDs with a placeholder
255
const scope = nock("https://api.example.com")
256
.filteringPath(/\/users\/\d+/, "/users/123");
257
258
// This will match requests to /users/456, /users/789, etc.
259
scope.get("/users/123").reply(200, { id: 123, name: "User" });
260
```
261
262
### Function-Based Path Filtering
263
264
```javascript { .api }
265
interface Scope {
266
filteringPath(fn: (path: string) => string): this;
267
}
268
```
269
270
```javascript
271
const scope = nock("https://api.example.com")
272
.filteringPath((path) => {
273
// Normalize paths by removing query parameters
274
return path.split("?")[0];
275
});
276
277
// This interceptor will match /users regardless of query params
278
scope.get("/users").reply(200, []);
279
```
280
281
## Request Body Filtering
282
283
Transform request bodies before matching, useful for normalizing data or ignoring certain fields.
284
285
### RegExp Body Filtering
286
287
```javascript { .api }
288
interface Scope {
289
filteringRequestBody(regex: RegExp, replace: string): this;
290
}
291
```
292
293
```javascript
294
// Replace timestamps with a fixed value
295
const scope = nock("https://api.example.com")
296
.filteringRequestBody(/"timestamp":\d+/, '"timestamp":1234567890');
297
298
scope.post("/events", { event: "login", timestamp: 1234567890 })
299
.reply(200, { recorded: true });
300
301
// Will match requests with any timestamp value
302
```
303
304
### Function-Based Body Filtering
305
306
```javascript { .api }
307
interface Scope {
308
filteringRequestBody(
309
fn: (body: string, recordedBody: string) => string
310
): this;
311
}
312
```
313
314
```javascript
315
const scope = nock("https://api.example.com")
316
.filteringRequestBody((body, recordedBody) => {
317
// Remove dynamic fields before matching
318
const parsed = JSON.parse(body);
319
delete parsed.timestamp;
320
delete parsed.requestId;
321
return JSON.stringify(parsed);
322
});
323
324
scope.post("/users", '{"name":"Alice","email":"alice@example.com"}')
325
.reply(201, { id: 1 });
326
327
// Will match requests regardless of timestamp/requestId fields
328
```
329
330
## Data Matching Types
331
332
Advanced data structures for flexible matching in query parameters and request bodies.
333
334
```javascript { .api }
335
type DataMatcher =
336
| boolean
337
| number
338
| string
339
| null
340
| undefined
341
| RegExp
342
| DataMatcherArray
343
| DataMatcherMap;
344
345
interface DataMatcherArray extends ReadonlyArray<DataMatcher> {}
346
interface DataMatcherMap {
347
[key: string]: DataMatcher;
348
}
349
```
350
351
### Complex Object Matching
352
353
```javascript
354
// Nested object matching with various matcher types
355
nock("https://api.example.com")
356
.post("/complex", {
357
user: {
358
name: /^[A-Z]/, // Name starts with capital letter
359
age: (age) => age >= 18 && age <= 100, // Age validation
360
preferences: {
361
theme: "dark",
362
notifications: true
363
}
364
},
365
tags: ["user", /^pref_/], // Array with string and regex
366
metadata: null,
367
active: true
368
})
369
.reply(201, { success: true });
370
```
371
372
## Examples: Complete Request Matching
373
374
### API with Authentication and Validation
375
376
```javascript
377
const apiScope = nock("https://api.example.com")
378
.matchHeader("authorization", /^Bearer .+/)
379
.matchHeader("content-type", "application/json")
380
.filteringPath(/\/v\d+/, "/v1"); // Normalize API versions
381
382
// Create user with validation
383
apiScope
384
.post("/v1/users", (body) => {
385
const user = JSON.parse(body);
386
return user.name && user.email && user.email.includes("@");
387
})
388
.reply(201, { id: 1, name: "Alice", email: "alice@example.com" });
389
390
// Search users with pagination
391
apiScope
392
.get("/v1/users")
393
.query({
394
page: /^\d+$/,
395
limit: (limit) => parseInt(limit) <= 100,
396
search: true // Any search term
397
})
398
.reply(200, { users: [], total: 0, page: 1 });
399
```
400
401
### File Upload Endpoint
402
403
```javascript
404
nock("https://api.example.com")
405
.post("/upload", (body) => {
406
// Check if body contains multipart form data
407
return body.includes("Content-Disposition: form-data");
408
})
409
.matchHeader("content-type", /^multipart\/form-data/)
410
.reply(200, {
411
uploadId: "abc123",
412
status: "uploaded"
413
});
414
```
415
416
### GraphQL Endpoint
417
418
```javascript
419
nock("https://api.example.com")
420
.post("/graphql", {
421
query: /query\s+GetUser/,
422
variables: {
423
userId: /^\d+$/
424
}
425
})
426
.matchHeader("content-type", "application/json")
427
.reply(200, {
428
data: {
429
user: { id: "1", name: "Alice" }
430
}
431
});
432
```