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

http-handlers.mddocs/

0

# HTTP Handlers

1

2

HTTP handlers provide Express-like routing for intercepting and mocking HTTP requests with support for path parameters, query strings, and custom predicates.

3

4

## Capabilities

5

6

### HTTP Request Handler Function

7

8

Creates handlers for specific HTTP methods and URL patterns.

9

10

```typescript { .api }

11

/**

12

* Creates an HTTP request handler for a specific method and URL pattern

13

* @param predicate - URL pattern (string/RegExp) or custom predicate function

14

* @param resolver - Function that returns a Response for matching requests

15

* @param options - Optional configuration including 'once' for single-use handlers

16

* @returns HttpHandler instance

17

*/

18

interface HttpRequestHandler {

19

<Params extends PathParams<keyof Params> = PathParams,

20

RequestBodyType extends DefaultBodyType = DefaultBodyType,

21

ResponseBodyType extends DefaultBodyType = undefined>(

22

predicate: HttpRequestPredicate<Params>,

23

resolver: HttpResponseResolver<Params, RequestBodyType, ResponseBodyType>,

24

options?: RequestHandlerOptions

25

): HttpHandler;

26

}

27

28

type HttpRequestPredicate<Params> =

29

| string

30

| RegExp

31

| HttpCustomPredicate<Params>;

32

33

type HttpCustomPredicate<Params = Record<string, string>> = (info: {

34

request: Request;

35

parsedResult: HttpRequestParsedResult<Params>;

36

}) => boolean;

37

38

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

39

match: Match<Params>;

40

query: RequestQuery;

41

}

42

43

type HttpResponseResolver<

44

Params extends PathParams<keyof Params> = PathParams,

45

RequestBodyType extends DefaultBodyType = DefaultBodyType,

46

ResponseBodyType extends DefaultBodyType = DefaultBodyType

47

> = (info: HttpRequestResolverExtras<Params>, request: Request, requestBody: RequestBodyType) =>

48

| Response

49

| Promise<Response>

50

| HttpResponse<ResponseBodyType>

51

| Promise<HttpResponse<ResponseBodyType>>;

52

53

interface HttpRequestResolverExtras<Params> {

54

request: Request;

55

params: Params;

56

cookies: Record<string, string>;

57

}

58

```

59

60

### HTTP Method Handlers

61

62

The `http` namespace provides handlers for all standard HTTP methods.

63

64

```typescript { .api }

65

const http: {

66

/** Handle requests with any HTTP method */

67

all: HttpRequestHandler;

68

/** Handle HTTP GET requests */

69

get: HttpRequestHandler;

70

/** Handle HTTP POST requests */

71

post: HttpRequestHandler;

72

/** Handle HTTP PUT requests */

73

put: HttpRequestHandler;

74

/** Handle HTTP DELETE requests */

75

delete: HttpRequestHandler;

76

/** Handle HTTP PATCH requests */

77

patch: HttpRequestHandler;

78

/** Handle HTTP HEAD requests */

79

head: HttpRequestHandler;

80

/** Handle HTTP OPTIONS requests */

81

options: HttpRequestHandler;

82

};

83

```

84

85

**Usage Examples:**

86

87

```typescript

88

import { http, HttpResponse } from "msw";

89

90

// Basic GET handler with static response

91

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

92

return HttpResponse.json([

93

{ id: 1, name: 'John Doe' },

94

{ id: 2, name: 'Jane Smith' }

95

]);

96

});

97

98

// POST handler with request body access

99

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

100

const newUser = await request.json();

101

102

return HttpResponse.json(

103

{ ...newUser, id: Date.now() },

104

{ status: 201 }

105

);

106

});

107

108

// Handler with path parameters

109

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

110

const { userId } = params;

111

112

return HttpResponse.json({

113

id: userId,

114

name: `User ${userId}`

115

});

116

});

117

118

// Handler with query parameters

119

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

120

const url = new URL(request.url);

121

const query = url.searchParams.get('q');

122

const limit = url.searchParams.get('limit') || '10';

123

124

return HttpResponse.json({

125

query,

126

results: [],

127

limit: parseInt(limit)

128

});

129

});

130

131

// Handler with cookies

132

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

133

const sessionId = cookies.sessionId;

134

135

if (!sessionId) {

136

return HttpResponse.json(

137

{ error: 'Unauthorized' },

138

{ status: 401 }

139

);

140

}

141

142

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

143

});

144

145

// RegExp pattern handler

146

http.get(/\/api\/files\/(.+)\.json$/, ({ request }) => {

147

const url = new URL(request.url);

148

const filename = url.pathname.match(/\/api\/files\/(.+)\.json$/)?.[1];

149

150

return HttpResponse.json({ filename, content: {} });

151

});

152

153

// Custom predicate handler

154

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

155

const contentType = request.headers.get('content-type');

156

157

if (!contentType?.includes('multipart/form-data')) {

158

return HttpResponse.json(

159

{ error: 'Invalid content type' },

160

{ status: 400 }

161

);

162

}

163

164

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

165

}, {

166

predicate: ({ request }) => {

167

return request.headers.get('content-type')?.includes('multipart/form-data') || false;

168

}

169

});

170

171

// One-time handler (auto-removes after first match)

172

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

173

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

174

}, { once: true });

175

176

// Handler with different response based on request

177

http.all('/api/echo', async ({ request }) => {

178

const method = request.method;

179

const body = method !== 'GET' ? await request.text() : null;

180

181

return HttpResponse.json({

182

method,

183

url: request.url,

184

headers: Object.fromEntries(request.headers.entries()),

185

body

186

});

187

});

188

```

189

190

### Path Parameter Extraction

191

192

MSW automatically extracts path parameters from URL patterns using Express-style syntax.

193

194

```typescript { .api }

195

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

196

197

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

198

matches: boolean;

199

params: Params;

200

}

201

```

202

203

**Supported Path Patterns:**

204

205

```typescript

206

// Named parameters

207

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

208

// params.userId is available

209

});

210

211

// Multiple parameters

212

http.get('/users/:userId/posts/:postId', ({ params }) => {

213

// params.userId and params.postId are available

214

});

215

216

// Optional parameters

217

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

218

// params.id may be undefined

219

});

220

221

// Wildcard patterns

222

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

223

// Matches any path under /assets/

224

});

225

226

// RegExp with capture groups

227

http.get(/^\/api\/v(\d+)\/users\/(\w+)$/, ({ request }) => {

228

const matches = new URL(request.url).pathname.match(/^\/api\/v(\d+)\/users\/(\w+)$/);

229

const version = matches?.[1];

230

const userId = matches?.[2];

231

});

232

```

233

234

### Query Parameter Access

235

236

Access query parameters through the standard URL API in request handlers.

237

238

```typescript

239

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

240

const url = new URL(request.url);

241

const searchParams = url.searchParams;

242

243

// Get single values

244

const query = searchParams.get('q');

245

const page = searchParams.get('page') || '1';

246

247

// Get all values for a parameter

248

const tags = searchParams.getAll('tag');

249

250

// Check if parameter exists

251

const hasFilter = searchParams.has('filter');

252

253

return HttpResponse.json({

254

query,

255

page: parseInt(page),

256

tags,

257

hasFilter

258

});

259

});

260

```

261

262

### Request Body Access

263

264

Access and parse request bodies using standard Request API methods.

265

266

```typescript

267

// JSON body

268

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

269

const jsonData = await request.json();

270

return HttpResponse.json({ received: jsonData });

271

});

272

273

// Form data

274

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

275

const formData = await request.formData();

276

const username = formData.get('username');

277

return HttpResponse.text(`Hello ${username}`);

278

});

279

280

// Text body

281

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

282

const textData = await request.text();

283

return HttpResponse.text(`Received: ${textData}`);

284

});

285

286

// Binary data

287

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

288

const arrayBuffer = await request.arrayBuffer();

289

return HttpResponse.json({ size: arrayBuffer.byteLength });

290

});

291

292

// Stream body

293

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

294

if (request.body) {

295

const reader = request.body.getReader();

296

// Process stream...

297

}

298

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

299

});

300

```

301

302

### Handler Options

303

304

Configure handler behavior with options object.

305

306

```typescript { .api }

307

interface RequestHandlerOptions {

308

/** Remove handler after first match */

309

once?: boolean;

310

}

311

```

312

313

```typescript

314

// One-time handler

315

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

316

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

317

}, { once: true });

318

```

319

320

## Types

321

322

```typescript { .api }

323

// Handler types

324

interface HttpHandler {

325

method: HttpMethods | RegExp;

326

predicate: HttpRequestPredicate<any>;

327

resolver: HttpResponseResolver<any, any, any>;

328

options: RequestHandlerOptions;

329

}

330

331

enum HttpMethods {

332

GET = 'GET',

333

POST = 'POST',

334

PUT = 'PUT',

335

DELETE = 'DELETE',

336

PATCH = 'PATCH',

337

HEAD = 'HEAD',

338

OPTIONS = 'OPTIONS'

339

}

340

341

// Request types

342

type RequestQuery = Record<string, string | string[]>;

343

344

type DefaultBodyType =

345

| string

346

| number

347

| boolean

348

| null

349

| undefined

350

| ArrayBuffer

351

| Blob

352

| FormData

353

| ReadableStream;

354

355

type DefaultRequestMultipartBody = Record<string, string | File>;

356

357

// Response types

358

type ResponseResolverReturnType<ResponseBodyType> =

359

| Response

360

| HttpResponse<ResponseBodyType>

361

| Promise<Response>

362

| Promise<HttpResponse<ResponseBodyType>>;

363

364

type AsyncResponseResolverReturnType<ResponseBodyType> = Promise<

365

ResponseResolverReturnType<ResponseBodyType>

366

>;

367

```