msw.ts70 lines · main
1import { http, HttpResponse, HttpResponseResolver } from 'msw'
2import { setupServer } from 'msw/node'
3
4import type { paths } from '../../data/api'
5import { GlobalAPIMocks } from './msw-global-api-mocks'
6import { API_URL } from '@/lib/constants'
7
8export const mswServer = setupServer(...GlobalAPIMocks)
9
10mswServer.events.on('request:start', ({ request }) => {
11 console.log('[MSW] Outgoing:', request.method, request.url)
12})
13
14// Recursively changes params in an endpoint path segment from {param}
15// format to :param format which is expected by Mock Service Worker
16type RemapParams<T extends string> = T extends `${infer Before}{${infer Param}}${infer After}`
17 ? `${Before}:${Param}${RemapParams<After>}`
18 : T
19
20type TrimQueryParams<T extends string> = T extends `${infer Endpoint}?${string}` ? Endpoint : T
21
22// We need to remap all keys from our openapi-typescript generated types
23// so that they follow MSW's parameter formatting
24type RemapPaths = {
25 [K in keyof paths as RemapParams<K>]: paths[K]
26}
27
28type Endpoints = keyof RemapPaths
29type Methods = Exclude<keyof typeof http, 'all'>
30
31// Extract either the 200 or 201 response object
32type SuccessResponse<P extends Endpoints, M extends Methods> = RemapPaths[P][M] extends {
33 responses: { 200: { content: { 'application/json': infer R200 } } }
34}
35 ? R200
36 : RemapPaths[P][M] extends {
37 responses: { 201: { content: { 'application/json': infer R201 } } }
38 }
39 ? R201
40 : never
41
42const isResponseResolver = (val: unknown): val is HttpResponseResolver => typeof val === `function`
43
44export const addAPIMock = <P extends Endpoints | `${Endpoints}?${string}`, M extends Methods>({
45 method,
46 path,
47 response,
48}: SuccessResponse<TrimQueryParams<P>, M> extends never
49 ? // Don't require a mocked response when the API doesn't return one
50 { method: M; path: P; response?: never }
51 : {
52 method: M
53 path: P
54 response: SuccessResponse<TrimQueryParams<P>, M> | HttpResponseResolver
55 }) => {
56 const fullPath = `${API_URL}${path}`
57 console.log('[MSW] Adding mock:', method.toUpperCase(), fullPath)
58
59 mswServer.use(
60 http[method](
61 fullPath,
62 isResponseResolver(response)
63 ? response
64 : ({ request }) => {
65 console.log('[MSW] Handling request:', request.method, request.url, response)
66 return HttpResponse.json(response ?? null)
67 }
68 )
69 )
70}