or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

fixture-testing.mdglobal-management.mdindex.mdrecording-playback.mdrequest-interception.mdrequest-matching.mdresponse-definition.md

request-matching.mddocs/

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

```