or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

browser-setup.mdcli.mdgraphql-handlers.mdhttp-handlers.mdindex.mdnodejs-setup.mdreact-native-setup.mdresponse-creation.mdutilities.mdwebsocket-handlers.md

utilities.mddocs/

0

# Request Utilities

1

2

Request utilities provide helper functions for handling request flow, including delays, bypassing MSW interception, and passthrough behavior.

3

4

## Capabilities

5

6

### Delay Function

7

8

Add realistic response delays to simulate network latency and server processing time.

9

10

```typescript { .api }

11

/**

12

* Delays the response by the given duration or mode

13

* @param durationOrMode - Delay duration in milliseconds, or delay mode ('real', 'infinite')

14

* @returns Promise that resolves after the specified delay

15

*/

16

function delay(durationOrMode?: DelayMode | number): Promise<void>;

17

18

type DelayMode = 'real' | 'infinite';

19

```

20

21

**Usage Examples:**

22

23

```typescript

24

import { http, HttpResponse, delay } from "msw";

25

26

// Default realistic delay (100-400ms in browser, 5ms in Node.js)

27

http.get('/api/user', async () => {

28

await delay(); // Random realistic delay

29

return HttpResponse.json({ name: 'John Doe' });

30

});

31

32

// Specific delay duration

33

http.get('/api/slow-endpoint', async () => {

34

await delay(2000); // 2 second delay

35

return HttpResponse.json({ data: 'slow data' });

36

});

37

38

// Realistic delay mode (same as default)

39

http.get('/api/realistic', async () => {

40

await delay('real'); // Random 100-400ms (browser) or 5ms (Node.js)

41

return HttpResponse.json({ data: 'realistic timing' });

42

});

43

44

// Infinite delay (useful for testing timeouts)

45

http.get('/api/timeout-test', async () => {

46

await delay('infinite'); // Never resolves

47

return HttpResponse.json({ data: 'never returned' });

48

});

49

50

// Conditional delays

51

http.get('/api/conditional-delay', async ({ request }) => {

52

const url = new URL(request.url);

53

const slow = url.searchParams.has('slow');

54

55

if (slow) {

56

await delay(3000); // Slow mode

57

} else {

58

await delay(100); // Fast mode

59

}

60

61

return HttpResponse.json({ mode: slow ? 'slow' : 'fast' });

62

});

63

64

// Simulating different network conditions

65

http.get('/api/network-simulation', async ({ request }) => {

66

const connection = request.headers.get('x-connection-type');

67

68

switch (connection) {

69

case 'slow-3g':

70

await delay(1500);

71

break;

72

case 'fast-3g':

73

await delay(800);

74

break;

75

case '4g':

76

await delay(200);

77

break;

78

case 'wifi':

79

await delay(50);

80

break;

81

default:

82

await delay(); // Default realistic delay

83

}

84

85

return HttpResponse.json({

86

data: 'network-dependent response',

87

connection

88

});

89

});

90

91

// Progressive delays for retry testing

92

let attemptCount = 0;

93

http.get('/api/retry-test', async () => {

94

attemptCount++;

95

96

// Exponential backoff simulation

97

const delayMs = Math.min(1000 * Math.pow(2, attemptCount - 1), 10000);

98

await delay(delayMs);

99

100

if (attemptCount < 3) {

101

return HttpResponse.json(

102

{ error: 'Temporary failure' },

103

{ status: 503 }

104

);

105

}

106

107

attemptCount = 0; // Reset for next test

108

return HttpResponse.json({ success: true, attempts: 3 });

109

});

110

```

111

112

### Bypass Function

113

114

Create requests that bypass MSW interception entirely, allowing real network requests.

115

116

```typescript { .api }

117

/**

118

* Creates a Request instance that will always be ignored by MSW

119

* @param input - URL string, URL object, or Request instance to bypass

120

* @param init - Optional RequestInit for additional request configuration

121

* @returns Request instance marked to bypass MSW interception

122

*/

123

function bypass(input: BypassRequestInput, init?: RequestInit): Request;

124

125

type BypassRequestInput = string | URL | Request;

126

```

127

128

**Usage Examples:**

129

130

```typescript

131

import { bypass } from "msw";

132

133

// Bypass MSW for specific request

134

async function fetchRealData() {

135

const response = await fetch(bypass('https://api.real-service.com/data'));

136

return response.json();

137

}

138

139

// Bypass with URL object

140

async function fetchWithURL() {

141

const url = new URL('https://api.example.com/real');

142

url.searchParams.set('param', 'value');

143

144

const response = await fetch(bypass(url));

145

return response.json();

146

}

147

148

// Bypass with Request object

149

async function fetchWithRequest() {

150

const request = new Request('https://api.example.com/data', {

151

method: 'POST',

152

body: JSON.stringify({ test: true })

153

});

154

155

const response = await fetch(bypass(request));

156

return response.json();

157

}

158

159

// Bypass with additional init options

160

async function fetchWithInit() {

161

const response = await fetch(

162

bypass('https://api.example.com/data'),

163

{

164

headers: {

165

'Authorization': 'Bearer real-token'

166

}

167

}

168

);

169

return response.json();

170

}

171

172

// Conditional bypass based on environment

173

async function fetchConditionally(url: string) {

174

const request = process.env.NODE_ENV === 'production'

175

? bypass(url) // Use real API in production

176

: url; // Use MSW in development/testing

177

178

const response = await fetch(request);

179

return response.json();

180

}

181

182

// Mixing real and mocked requests

183

http.get('/api/mixed-data', async () => {

184

// Fetch some real data alongside mocked response

185

const realData = await fetch(bypass('https://api.external.com/status'))

186

.then(res => res.json())

187

.catch(() => ({ status: 'unavailable' }));

188

189

return HttpResponse.json({

190

mockedData: { message: 'This is mocked' },

191

realData: realData

192

});

193

});

194

195

// Testing real API integration

196

describe('Real API Integration', () => {

197

test('should work with real API', async () => {

198

// Bypass MSW for this specific test

199

const response = await fetch(bypass('https://jsonplaceholder.typicode.com/posts/1'));

200

const data = await response.json();

201

202

expect(data).toHaveProperty('id', 1);

203

});

204

});

205

206

// Graceful fallback pattern

207

async function fetchWithFallback(url: string) {

208

try {

209

// Try real API first

210

const response = await fetch(bypass(url));

211

if (response.ok) {

212

return response.json();

213

}

214

} catch (error) {

215

console.warn('Real API failed, using fallback');

216

}

217

218

// Fallback to mocked data (regular fetch will be intercepted by MSW)

219

const fallbackResponse = await fetch(url);

220

return fallbackResponse.json();

221

}

222

```

223

224

### Passthrough Function

225

226

Allow intercepted requests to pass through without mocking, continuing to their original destination.

227

228

```typescript { .api }

229

/**

230

* Performs the intercepted request as-is without modification

231

* Stops request handler lookup and allows request to proceed normally

232

* @returns HttpResponse indicating passthrough behavior

233

*/

234

function passthrough(): HttpResponse<any>;

235

```

236

237

**Usage Examples:**

238

239

```typescript

240

import { http, passthrough } from "msw";

241

242

// Conditional passthrough based on request properties

243

http.get('/api/*', ({ request }) => {

244

const url = new URL(request.url);

245

246

// Pass through requests with specific header

247

if (request.headers.get('x-passthrough') === 'true') {

248

return passthrough();

249

}

250

251

// Mock other requests

252

return HttpResponse.json({ mocked: true });

253

});

254

255

// Passthrough for specific environments

256

http.all('*', ({ request }) => {

257

// In production, pass through all requests to real API

258

if (process.env.NODE_ENV === 'production') {

259

return passthrough();

260

}

261

262

// In development/test, continue with other handlers

263

return HttpResponse.json({ environment: 'development' });

264

});

265

266

// Passthrough based on authentication

267

http.get('/api/protected/*', ({ request }) => {

268

const authHeader = request.headers.get('authorization');

269

270

// If real auth token present, pass through to real API

271

if (authHeader?.startsWith('Bearer real_')) {

272

return passthrough();

273

}

274

275

// Mock for test tokens

276

return HttpResponse.json({

277

data: 'mocked protected data',

278

user: 'test-user'

279

});

280

});

281

282

// Selective mocking with passthrough fallback

283

http.get('/api/users/:id', ({ params }) => {

284

const userId = params.id;

285

286

// Mock specific test users

287

if (userId === '1' || userId === '2') {

288

return HttpResponse.json({

289

id: userId,

290

name: `Test User ${userId}`,

291

email: `test${userId}@example.com`

292

});

293

}

294

295

// Pass through for other user IDs

296

return passthrough();

297

});

298

299

// Development mode with passthrough option

300

http.get('/api/feature-flag', ({ request }) => {

301

const url = new URL(request.url);

302

const useReal = url.searchParams.get('real') === 'true';

303

304

if (useReal) {

305

console.log('Using real feature flag service');

306

return passthrough();

307

}

308

309

// Mock feature flags for development

310

return HttpResponse.json({

311

featureA: true,

312

featureB: false,

313

featureC: Math.random() > 0.5

314

});

315

});

316

317

// Hybrid testing approach

318

http.post('/api/analytics', ({ request }) => {

319

// Always passthrough analytics in test environment

320

// to avoid affecting real metrics while still testing integration

321

if (process.env.NODE_ENV === 'test') {

322

console.log('Analytics call passed through');

323

return passthrough();

324

}

325

326

// Mock in development to avoid sending test data

327

return HttpResponse.json({ tracked: true });

328

});

329

330

// Geographic routing

331

http.get('/api/content', ({ request }) => {

332

const geo = request.headers.get('x-geo-country');

333

334

// Pass through for specific regions that need real API

335

if (geo === 'US' || geo === 'CA') {

336

return passthrough();

337

}

338

339

// Mock for other regions

340

return HttpResponse.json({

341

content: 'localized mock content',

342

region: geo || 'unknown'

343

});

344

});

345

346

// Performance testing scenario

347

http.get('/api/performance-test', ({ request }) => {

348

const testMode = request.headers.get('x-test-mode');

349

350

switch (testMode) {

351

case 'real-api':

352

// Test against real API for performance baseline

353

return passthrough();

354

355

case 'mock-fast':

356

// Fast mock response for speed testing

357

return HttpResponse.json({ data: 'fast mock' });

358

359

case 'mock-slow':

360

// Slow mock response for timeout testing

361

return delay(5000).then(() =>

362

HttpResponse.json({ data: 'slow mock' })

363

);

364

365

default:

366

return HttpResponse.json({ data: 'default mock' });

367

}

368

});

369

```

370

371

### Response Resolution

372

373

Programmatically resolve requests against handlers for advanced use cases and custom request handling logic.

374

375

```typescript { .api }

376

/**

377

* Finds and executes the first matching request handler for a given request

378

* @param handlers - Array of MSW request handlers to test against the request

379

* @param request - The incoming HTTP request (standard Fetch API Request object)

380

* @param resolutionContext - Optional configuration object with baseUrl for URL matching

381

* @returns Promise resolving to Response object if a handler matches, undefined otherwise

382

*/

383

function getResponse(

384

handlers: Array<RequestHandler>,

385

request: Request,

386

resolutionContext?: ResponseResolutionContext

387

): Promise<Response | undefined>;

388

389

interface ResponseResolutionContext {

390

baseUrl?: string;

391

}

392

```

393

394

**Usage Examples:**

395

396

```typescript

397

import { getResponse, http, HttpResponse } from "msw";

398

399

// Basic usage - resolve request against handlers

400

const handlers = [

401

http.get('/api/user', () => HttpResponse.json({ name: 'John' })),

402

http.post('/api/user', () => new Response('Created', { status: 201 }))

403

];

404

405

const request = new Request('https://example.com/api/user');

406

const response = await getResponse(handlers, request);

407

// Returns: Response with JSON { name: 'John' }

408

409

// Advanced: Batched GraphQL operations

410

function batchedGraphQLQuery(url: string, handlers: Array<RequestHandler>) {

411

return http.post(url, async ({ request }) => {

412

const operations = await request.json();

413

414

const responses = await Promise.all(

415

operations.map(async (operation) => {

416

const scopedRequest = new Request(request, {

417

body: JSON.stringify(operation),

418

});

419

const response = await getResponse(handlers, scopedRequest);

420

return response || fetch(bypass(scopedRequest));

421

})

422

);

423

424

return HttpResponse.json(responses.map(r => r.json()));

425

});

426

}

427

428

// Custom handler composition

429

const compositeHandler = http.get('/api/data', async ({ request }) => {

430

// Try specialized handlers first

431

const specializedResponse = await getResponse(specializedHandlers, request);

432

if (specializedResponse) {

433

return specializedResponse;

434

}

435

436

// Fall back to default behavior

437

return HttpResponse.json({ message: 'Default response' });

438

});

439

440

// Testing utility - programmatically test handler behavior

441

const testHandlers = [

442

http.get('/test', () => HttpResponse.json({ test: true }))

443

];

444

445

const testRequest = new Request('http://localhost/test');

446

const testResponse = await getResponse(testHandlers, testRequest);

447

console.log(await testResponse?.json()); // { test: true }

448

```

449

450

### URL Matching Utilities

451

452

Utilities for working with URL patterns and matching.

453

454

```typescript { .api }

455

/**

456

* Match request URL against a predicate pattern

457

* @param url - URL to match against

458

* @param predicate - String pattern, RegExp, or Path to match

459

* @returns Match result with success status and extracted parameters

460

*/

461

function matchRequestUrl(url: URL, predicate: Path): Match;

462

463

/**

464

* Clean and normalize URL for consistent matching

465

* @param url - URL string to clean

466

* @returns Cleaned URL string

467

*/

468

function cleanUrl(url: string): string;

469

470

type Path = string | RegExp;

471

472

interface Match<Params = Record<string, string>> {

473

matches: boolean;

474

params: Params;

475

}

476

```

477

478

**Usage Examples:**

479

480

```typescript

481

import { matchRequestUrl, cleanUrl } from "msw";

482

483

// Custom request matching logic

484

function customMatcher(request: Request): boolean {

485

const url = new URL(request.url);

486

const match = matchRequestUrl(url, '/api/users/:id');

487

488

if (match.matches) {

489

console.log('Matched user ID:', match.params.id);

490

return true;

491

}

492

493

return false;

494

}

495

496

// URL cleaning for consistent matching

497

function normalizeUrl(url: string): string {

498

const cleaned = cleanUrl(url);

499

console.log('Original:', url);

500

console.log('Cleaned:', cleaned);

501

return cleaned;

502

}

503

504

// Pattern matching examples

505

const testCases = [

506

{ url: 'https://api.example.com/users/123', pattern: '/users/:id' },

507

{ url: 'https://api.example.com/posts?page=1', pattern: '/posts' },

508

{ url: 'https://api.example.com/files/doc.pdf', pattern: /\/files\/.+\.pdf$/ }

509

];

510

511

testCases.forEach(({ url, pattern }) => {

512

const match = matchRequestUrl(new URL(url), pattern);

513

console.log(`${url} matches ${pattern}:`, match);

514

});

515

```

516

517

### Asset Request Detection

518

519

Utility to identify common asset requests that typically shouldn't be mocked.

520

521

```typescript { .api }

522

/**

523

* Check if request is for a common static asset

524

* @param request - Request object to check

525

* @returns True if request appears to be for a static asset

526

*/

527

function isCommonAssetRequest(request: Request): boolean;

528

```

529

530

**Usage Examples:**

531

532

```typescript

533

import { http, HttpResponse, isCommonAssetRequest, passthrough } from "msw";

534

535

// Skip mocking for asset requests

536

http.all('*', ({ request }) => {

537

// Let asset requests pass through to real server

538

if (isCommonAssetRequest(request)) {

539

return passthrough();

540

}

541

542

// Continue with API mocking for non-asset requests

543

return HttpResponse.json({ intercepted: true });

544

});

545

546

// Conditional handling based on request type

547

http.get('*', ({ request }) => {

548

const url = new URL(request.url);

549

550

if (isCommonAssetRequest(request)) {

551

console.log('Asset request detected:', url.pathname);

552

return passthrough();

553

}

554

555

if (url.pathname.startsWith('/api/')) {

556

return HttpResponse.json({ api: 'mocked' });

557

}

558

559

// Default fallback

560

return passthrough();

561

});

562

563

// Logging asset vs API requests

564

http.all('*', ({ request }) => {

565

const isAsset = isCommonAssetRequest(request);

566

567

console.log(`Request type: ${isAsset ? 'Asset' : 'API'} - ${request.url}`);

568

569

if (isAsset) {

570

return passthrough();

571

}

572

573

return HttpResponse.text('Caught by MSW');

574

});

575

```

576

577

### Advanced Utility Patterns

578

579

Combine utilities for sophisticated request handling scenarios.

580

581

```typescript

582

// Smart passthrough with delay simulation

583

http.get('/api/smart-proxy', async ({ request }) => {

584

const url = new URL(request.url);

585

const simulateDelay = url.searchParams.get('delay');

586

587

if (simulateDelay) {

588

await delay(parseInt(simulateDelay));

589

}

590

591

// Pass through to real API after delay

592

return passthrough();

593

});

594

595

// Conditional bypass based on request content

596

http.post('/api/conditional-bypass', async ({ request }) => {

597

const body = await request.json();

598

599

// Bypass for real data, mock for test data

600

if (body.useRealApi === true) {

601

// Create new request with bypass

602

return fetch(bypass(request.url, {

603

method: request.method,

604

body: JSON.stringify(body),

605

headers: request.headers

606

}));

607

}

608

609

await delay(100);

610

return HttpResponse.json({

611

mocked: true,

612

received: body

613

});

614

});

615

616

// Hybrid real/mock response

617

http.get('/api/hybrid-data', async ({ request }) => {

618

try {

619

// Try to get real data

620

const realResponse = await fetch(bypass(request.url));

621

const realData = await realResponse.json();

622

623

// Combine with mock data

624

await delay(50); // Add some realistic delay

625

626

return HttpResponse.json({

627

real: realData,

628

mock: { timestamp: Date.now(), generated: true }

629

});

630

} catch (error) {

631

// Fall back to pure mock if real API fails

632

await delay(100);

633

return HttpResponse.json({

634

real: null,

635

mock: { error: 'Real API unavailable', fallback: true },

636

timestamp: Date.now()

637

});

638

}

639

});

640

```

641

642

## Types

643

644

```typescript { .api }

645

// Delay types

646

type DelayMode = 'real' | 'infinite';

647

648

// Bypass types

649

type BypassRequestInput = string | URL | Request;

650

651

// URL matching types

652

type Path = string | RegExp;

653

type PathParams<T extends string = string> = Record<T, string>;

654

655

interface Match<Params = Record<string, string>> {

656

matches: boolean;

657

params: Params;

658

}

659

660

// Utility function signatures

661

declare function delay(durationOrMode?: DelayMode | number): Promise<void>;

662

declare function bypass(input: BypassRequestInput, init?: RequestInit): Request;

663

declare function passthrough(): HttpResponse<any>;

664

declare function matchRequestUrl(url: URL, predicate: Path): Match;

665

declare function cleanUrl(url: string): string;

666

declare function isCommonAssetRequest(request: Request): boolean;

667

668

// Constants

669

declare const SET_TIMEOUT_MAX_ALLOWED_INT: number;

670

declare const MIN_SERVER_RESPONSE_TIME: number;

671

declare const MAX_SERVER_RESPONSE_TIME: number;

672

declare const NODE_SERVER_RESPONSE_TIME: number;

673

```