0
# Response Creation
1
2
Response creation provides an enhanced Response class with convenience methods for creating various response types with proper headers and content handling.
3
4
## Capabilities
5
6
### HttpResponse Class
7
8
Enhanced Response class that extends the standard Response with additional features for mocking.
9
10
```typescript { .api }
11
/**
12
* Enhanced Response class with additional mocking features
13
* Drop-in replacement for standard Response with MSW-specific enhancements
14
*/
15
class HttpResponse<BodyType extends DefaultBodyType> extends Response {
16
/** Response body type marker for TypeScript */
17
readonly [bodyType]: BodyType;
18
19
/**
20
* Create an HttpResponse instance
21
* @param body - Response body content
22
* @param init - Response initialization options
23
*/
24
constructor(body?: NoInfer<BodyType> | null, init?: HttpResponseInit);
25
26
/** Create network error response */
27
static error(): HttpResponse<any>;
28
}
29
30
interface HttpResponseInit extends ResponseInit {
31
/** Response type (basic, cors, error, opaque, opaqueredirect) */
32
type?: ResponseType;
33
}
34
35
type DefaultBodyType =
36
| string
37
| number
38
| boolean
39
| null
40
| undefined
41
| ArrayBuffer
42
| Blob
43
| FormData
44
| ReadableStream;
45
```
46
47
**Usage Examples:**
48
49
```typescript
50
import { HttpResponse, http } from "msw";
51
52
// Basic HttpResponse with string body
53
http.get('/api/message', () => {
54
return new HttpResponse('Hello, world!', {
55
status: 200,
56
headers: {
57
'Content-Type': 'text/plain'
58
}
59
});
60
});
61
62
// HttpResponse with custom headers and status
63
http.post('/api/data', () => {
64
return new HttpResponse(JSON.stringify({ success: true }), {
65
status: 201,
66
statusText: 'Created',
67
headers: {
68
'Content-Type': 'application/json',
69
'X-Custom-Header': 'custom-value'
70
}
71
});
72
});
73
74
// Network error response
75
http.get('/api/network-error', () => {
76
return HttpResponse.error();
77
});
78
```
79
80
### Text Response Creation
81
82
Create text responses with proper Content-Type headers.
83
84
```typescript { .api }
85
/**
86
* Create a Response with Content-Type: "text/plain" body
87
* @param body - Text content for the response body
88
* @param init - Response initialization options
89
* @returns HttpResponse with text/plain content type
90
*/
91
static text<BodyType extends string>(
92
body?: NoInfer<BodyType> | null,
93
init?: HttpResponseInit
94
): HttpResponse<BodyType>;
95
```
96
97
**Usage Examples:**
98
99
```typescript
100
// Simple text response
101
http.get('/api/message', () => {
102
return HttpResponse.text('Hello, world!');
103
});
104
105
// Text response with custom status
106
http.get('/api/error-message', () => {
107
return HttpResponse.text('Something went wrong', {
108
status: 500
109
});
110
});
111
112
// Text response with custom headers
113
http.get('/api/plain-text', () => {
114
return HttpResponse.text('Plain text content', {
115
headers: {
116
'Cache-Control': 'no-cache',
117
'X-Source': 'mock'
118
}
119
});
120
});
121
122
// Empty text response
123
http.delete('/api/resource', () => {
124
return HttpResponse.text(null, { status: 204 });
125
});
126
127
// Multiline text response
128
http.get('/api/readme', () => {
129
return HttpResponse.text(`
130
# README
131
132
This is a multiline text response
133
with proper formatting.
134
135
## Features
136
- Feature 1
137
- Feature 2
138
`.trim());
139
});
140
```
141
142
### JSON Response Creation
143
144
Create JSON responses with proper Content-Type headers and automatic serialization.
145
146
```typescript { .api }
147
/**
148
* Create a Response with Content-Type: "application/json" body
149
* @param body - JavaScript object/array to serialize as JSON
150
* @param init - Response initialization options
151
* @returns HttpResponse with application/json content type
152
*/
153
static json<BodyType extends JsonBodyType>(
154
body?: NoInfer<BodyType> | null | undefined,
155
init?: HttpResponseInit
156
): HttpResponse<BodyType>;
157
158
type JsonBodyType = Record<string, any> | any[];
159
```
160
161
**Usage Examples:**
162
163
```typescript
164
// Simple JSON object response
165
http.get('/api/user', () => {
166
return HttpResponse.json({
167
id: 1,
168
name: 'John Doe',
169
email: 'john@example.com'
170
});
171
});
172
173
// JSON array response
174
http.get('/api/users', () => {
175
return HttpResponse.json([
176
{ id: 1, name: 'John Doe' },
177
{ id: 2, name: 'Jane Smith' }
178
]);
179
});
180
181
// JSON response with custom status
182
http.post('/api/users', () => {
183
return HttpResponse.json(
184
{ id: 3, name: 'New User', created: true },
185
{ status: 201 }
186
);
187
});
188
189
// Error JSON response
190
http.get('/api/protected', () => {
191
return HttpResponse.json(
192
{
193
error: 'Unauthorized',
194
code: 'AUTH_REQUIRED',
195
message: 'Please provide valid authentication'
196
},
197
{ status: 401 }
198
);
199
});
200
201
// Complex nested JSON
202
http.get('/api/dashboard', () => {
203
return HttpResponse.json({
204
user: {
205
id: 1,
206
profile: { name: 'John', avatar: 'avatar.jpg' }
207
},
208
stats: {
209
views: 1234,
210
likes: 567,
211
comments: 89
212
},
213
posts: [
214
{ id: 1, title: 'First Post', published: true },
215
{ id: 2, title: 'Second Post', published: false }
216
]
217
});
218
});
219
220
// Empty JSON response
221
http.delete('/api/user/1', () => {
222
return HttpResponse.json(null, { status: 204 });
223
});
224
225
// JSON response with custom headers
226
http.get('/api/data', () => {
227
return HttpResponse.json(
228
{ data: 'example' },
229
{
230
headers: {
231
'X-Total-Count': '1',
232
'Cache-Control': 'max-age=300'
233
}
234
}
235
);
236
});
237
```
238
239
### XML Response Creation
240
241
Create XML responses with proper Content-Type headers.
242
243
```typescript { .api }
244
/**
245
* Create a Response with Content-Type: "application/xml" body
246
* @param body - XML string content
247
* @param init - Response initialization options
248
* @returns HttpResponse with text/xml content type
249
*/
250
static xml<BodyType extends string>(
251
body?: BodyType | null,
252
init?: HttpResponseInit
253
): HttpResponse<BodyType>;
254
```
255
256
**Usage Examples:**
257
258
```typescript
259
// Simple XML response
260
http.get('/api/data.xml', () => {
261
return HttpResponse.xml(`
262
<?xml version="1.0" encoding="UTF-8"?>
263
<user>
264
<id>1</id>
265
<name>John Doe</name>
266
<email>john@example.com</email>
267
</user>
268
`);
269
});
270
271
// XML response with custom status
272
http.post('/api/soap', () => {
273
return HttpResponse.xml(`
274
<?xml version="1.0" encoding="UTF-8"?>
275
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
276
<soap:Body>
277
<response>Success</response>
278
</soap:Body>
279
</soap:Envelope>
280
`, { status: 201 });
281
});
282
283
// RSS feed response
284
http.get('/api/feed', () => {
285
return HttpResponse.xml(`
286
<?xml version="1.0" encoding="UTF-8"?>
287
<rss version="2.0">
288
<channel>
289
<title>My Blog</title>
290
<description>Latest posts</description>
291
<item>
292
<title>First Post</title>
293
<description>Content of first post</description>
294
</item>
295
</channel>
296
</rss>
297
`);
298
});
299
300
// XML error response
301
http.get('/api/error.xml', () => {
302
return HttpResponse.xml(`
303
<?xml version="1.0" encoding="UTF-8"?>
304
<error>
305
<code>404</code>
306
<message>Resource not found</message>
307
</error>
308
`, { status: 404 });
309
});
310
```
311
312
### HTML Response Creation
313
314
Create HTML responses with proper Content-Type headers.
315
316
```typescript { .api }
317
/**
318
* Create a Response with Content-Type: "text/html" body
319
* @param body - HTML string content
320
* @param init - Response initialization options
321
* @returns HttpResponse with text/html content type
322
*/
323
static html<BodyType extends string>(
324
body?: BodyType | null,
325
init?: HttpResponseInit
326
): HttpResponse<BodyType>;
327
```
328
329
**Usage Examples:**
330
331
```typescript
332
// Simple HTML page
333
http.get('/login', () => {
334
return HttpResponse.html(`
335
<!DOCTYPE html>
336
<html>
337
<head><title>Login</title></head>
338
<body>
339
<form>
340
<input type="text" placeholder="Username" />
341
<input type="password" placeholder="Password" />
342
<button type="submit">Login</button>
343
</form>
344
</body>
345
</html>
346
`);
347
});
348
349
// HTML fragment
350
http.get('/api/widget', () => {
351
return HttpResponse.html(`
352
<div class="widget">
353
<h3>Dynamic Widget</h3>
354
<p>This content was generated by MSW</p>
355
</div>
356
`);
357
});
358
359
// HTML error page
360
http.get('/not-found', () => {
361
return HttpResponse.html(`
362
<!DOCTYPE html>
363
<html>
364
<head><title>404 - Not Found</title></head>
365
<body>
366
<h1>Page Not Found</h1>
367
<p>The requested page could not be found.</p>
368
</body>
369
</html>
370
`, { status: 404 });
371
});
372
373
// Server-side rendered component
374
http.get('/ssr-component', () => {
375
return HttpResponse.html(`
376
<div id="app">
377
<header>
378
<h1>Server Rendered</h1>
379
</header>
380
<main>
381
<p>This HTML was "rendered" by MSW</p>
382
</main>
383
</div>
384
`);
385
});
386
```
387
388
### Binary Response Creation
389
390
Create responses with binary data using ArrayBuffer.
391
392
```typescript { .api }
393
/**
394
* Create a Response with an ArrayBuffer body
395
* @param body - ArrayBuffer or SharedArrayBuffer content
396
* @param init - Response initialization options
397
* @returns HttpResponse with application/octet-stream content type
398
*/
399
static arrayBuffer<BodyType extends ArrayBuffer | SharedArrayBuffer>(
400
body?: BodyType,
401
init?: HttpResponseInit
402
): HttpResponse<BodyType>;
403
```
404
405
**Usage Examples:**
406
407
```typescript
408
// Binary file response
409
http.get('/api/file.bin', () => {
410
const buffer = new ArrayBuffer(16);
411
const view = new Uint8Array(buffer);
412
413
// Fill with sample binary data
414
for (let i = 0; i < view.length; i++) {
415
view[i] = i * 2;
416
}
417
418
return HttpResponse.arrayBuffer(buffer);
419
});
420
421
// Image response (simulated)
422
http.get('/api/image.png', () => {
423
// Create a minimal PNG-like binary structure
424
const buffer = new ArrayBuffer(100);
425
const view = new Uint8Array(buffer);
426
427
// PNG signature (not a real PNG, just for demo)
428
view.set([0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]);
429
430
return HttpResponse.arrayBuffer(buffer, {
431
headers: {
432
'Content-Type': 'image/png'
433
}
434
});
435
});
436
437
// Binary data with custom content type
438
http.get('/api/data.custom', () => {
439
const buffer = new ArrayBuffer(64);
440
const view = new Float32Array(buffer);
441
442
// Fill with float data
443
for (let i = 0; i < view.length; i++) {
444
view[i] = Math.random();
445
}
446
447
return HttpResponse.arrayBuffer(buffer, {
448
headers: {
449
'Content-Type': 'application/x-custom-binary'
450
}
451
});
452
});
453
454
// Download response
455
http.get('/api/download/:filename', ({ params }) => {
456
const buffer = new ArrayBuffer(1024);
457
// ... fill buffer with file content
458
459
return HttpResponse.arrayBuffer(buffer, {
460
headers: {
461
'Content-Disposition': `attachment; filename="${params.filename}"`,
462
'Content-Type': 'application/octet-stream'
463
}
464
});
465
});
466
```
467
468
### Form Data Response Creation
469
470
Create responses with FormData bodies.
471
472
```typescript { .api }
473
/**
474
* Create a Response with a FormData body
475
* @param body - FormData instance
476
* @param init - Response initialization options
477
* @returns HttpResponse with FormData body
478
*/
479
static formData(
480
body?: FormData,
481
init?: HttpResponseInit
482
): HttpResponse<FormData>;
483
```
484
485
**Usage Examples:**
486
487
```typescript
488
// Form data response
489
http.get('/api/form-data', () => {
490
const formData = new FormData();
491
formData.append('name', 'John Doe');
492
formData.append('email', 'john@example.com');
493
formData.append('age', '30');
494
495
return HttpResponse.formData(formData);
496
});
497
498
// File upload simulation
499
http.post('/api/upload', () => {
500
const formData = new FormData();
501
formData.append('status', 'success');
502
formData.append('fileId', '12345');
503
formData.append('filename', 'uploaded-file.txt');
504
505
return HttpResponse.formData(formData, {
506
status: 201
507
});
508
});
509
510
// Multi-part response with files
511
http.get('/api/export', () => {
512
const formData = new FormData();
513
514
// Simulate file content
515
const blob = new Blob(['CSV data content'], { type: 'text/csv' });
516
formData.append('file', blob, 'export.csv');
517
formData.append('format', 'csv');
518
formData.append('records', '1000');
519
520
return HttpResponse.formData(formData);
521
});
522
```
523
524
### Custom Response Headers
525
526
Set custom headers for enhanced response simulation.
527
528
```typescript
529
// Response with caching headers
530
http.get('/api/cached-data', () => {
531
return HttpResponse.json(
532
{ data: 'cached content' },
533
{
534
headers: {
535
'Cache-Control': 'public, max-age=3600',
536
'ETag': '"abc123"',
537
'Last-Modified': 'Wed, 21 Oct 2015 07:28:00 GMT'
538
}
539
}
540
);
541
});
542
543
// CORS headers
544
http.options('/api/cors-endpoint', () => {
545
return new HttpResponse(null, {
546
status: 204,
547
headers: {
548
'Access-Control-Allow-Origin': '*',
549
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE',
550
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
551
'Access-Control-Max-Age': '86400'
552
}
553
});
554
});
555
556
// Security headers
557
http.get('/api/secure-data', () => {
558
return HttpResponse.json(
559
{ sensitiveData: 'protected' },
560
{
561
headers: {
562
'X-Content-Type-Options': 'nosniff',
563
'X-Frame-Options': 'DENY',
564
'X-XSS-Protection': '1; mode=block',
565
'Strict-Transport-Security': 'max-age=31536000'
566
}
567
}
568
);
569
});
570
571
// Custom API headers
572
http.get('/api/data', () => {
573
return HttpResponse.json(
574
{ results: [] },
575
{
576
headers: {
577
'X-Total-Count': '0',
578
'X-Page': '1',
579
'X-Per-Page': '20',
580
'X-Rate-Limit-Remaining': '99'
581
}
582
}
583
);
584
});
585
```
586
587
### Response Status Codes
588
589
Handle various HTTP status codes appropriately.
590
591
```typescript
592
// Success responses
593
http.get('/api/data', () => {
594
return HttpResponse.json({ data: 'ok' }); // 200 OK (default)
595
});
596
597
http.post('/api/resource', () => {
598
return HttpResponse.json(
599
{ id: 1, created: true },
600
{ status: 201 } // 201 Created
601
);
602
});
603
604
http.put('/api/resource/1', () => {
605
return HttpResponse.json(
606
{ id: 1, updated: true },
607
{ status: 200 } // 200 OK for updates
608
);
609
});
610
611
http.delete('/api/resource/1', () => {
612
return new HttpResponse(null, { status: 204 }); // 204 No Content
613
});
614
615
// Client error responses
616
http.get('/api/unauthorized', () => {
617
return HttpResponse.json(
618
{ error: 'Unauthorized' },
619
{ status: 401 }
620
);
621
});
622
623
http.get('/api/forbidden', () => {
624
return HttpResponse.json(
625
{ error: 'Forbidden' },
626
{ status: 403 }
627
);
628
});
629
630
http.get('/api/not-found', () => {
631
return HttpResponse.json(
632
{ error: 'Not found' },
633
{ status: 404 }
634
);
635
});
636
637
http.post('/api/invalid-data', () => {
638
return HttpResponse.json(
639
{
640
error: 'Validation failed',
641
details: { name: 'Required field' }
642
},
643
{ status: 422 }
644
);
645
});
646
647
// Server error responses
648
http.get('/api/server-error', () => {
649
return HttpResponse.json(
650
{ error: 'Internal server error' },
651
{ status: 500 }
652
);
653
});
654
655
http.get('/api/service-unavailable', () => {
656
return HttpResponse.json(
657
{ error: 'Service temporarily unavailable' },
658
{ status: 503 }
659
);
660
});
661
662
// Redirect responses
663
http.get('/api/redirect', () => {
664
return new HttpResponse(null, {
665
status: 302,
666
headers: {
667
'Location': '/api/new-location'
668
}
669
});
670
});
671
```
672
673
## Types
674
675
```typescript { .api }
676
// Response class types
677
class HttpResponse<BodyType extends DefaultBodyType> extends Response {
678
readonly [bodyType]: BodyType;
679
constructor(body?: NoInfer<BodyType> | null, init?: HttpResponseInit);
680
681
static error(): HttpResponse<any>;
682
static text<T extends string>(body?: T | null, init?: HttpResponseInit): HttpResponse<T>;
683
static json<T extends JsonBodyType>(body?: T | null, init?: HttpResponseInit): HttpResponse<T>;
684
static xml<T extends string>(body?: T | null, init?: HttpResponseInit): HttpResponse<T>;
685
static html<T extends string>(body?: T | null, init?: HttpResponseInit): HttpResponse<T>;
686
static arrayBuffer<T extends ArrayBuffer | SharedArrayBuffer>(body?: T, init?: HttpResponseInit): HttpResponse<T>;
687
static formData(body?: FormData, init?: HttpResponseInit): HttpResponse<FormData>;
688
}
689
690
// Body types
691
type DefaultBodyType =
692
| string
693
| number
694
| boolean
695
| null
696
| undefined
697
| ArrayBuffer
698
| Blob
699
| FormData
700
| ReadableStream;
701
702
type JsonBodyType = Record<string, any> | any[];
703
704
// Response initialization
705
interface HttpResponseInit extends ResponseInit {
706
status?: number;
707
statusText?: string;
708
headers?: HeadersInit;
709
type?: ResponseType;
710
}
711
712
// Utility types
713
type NoInfer<T> = [T][T extends any ? 0 : never];
714
715
// Symbol for body type tracking
716
declare const bodyType: unique symbol;
717
```