When mocking a path with a param and requesting with a query param the req.param object is empty.
msw: 0.15.5nodejs: 12.15.0npm: 6.13.4Please also provide your browser version.
Steps to reproduce the behavior:
'/api/v3/registries/:registryId/reg_items''/api/v3/registries/123/reg_items?hello=true'req.param is empty in body of the mocked endpoint.req.param should have registryId with the value of 123 and req.query.get('hello') should be true.
import { composeMocks, rest } from 'msw'
import faker from 'faker'
import { recommendedProduct } from './responders'
const RESPONSE_STATUS = {
SERVER_ERROR: 500,
}
/**
* Patterns for returning different error responses.
*/
const resourceErrorStatus = id => {
switch (id) {
case '!!!':
return RESPONSE_STATUS.SERVER_ERROR
default:
return null
}
}
// Configure mocking routes
const { start } = composeMocks(
rest.post('/api/v3/registries/:registryId/reg_items', (req, res, ctx) => {
// Access request's params
const { registryId } = req.params // 鈿狅笍 is undefined when ?include_recommendation present
const {
reg_item: { product_id },
} = req.body
const includeAddNext = req.query.get('include_recommendation')
const errStatus = resourceErrorStatus(registryId)
if (errStatus) {
return res(ctx.status(errStatus), ctx.json({ error: 'Whoops' }))
}
const resJson = {
regItem: {
...(includeAddNext && {
addNextRecommendation: {
metadata: {
modelVersion: faker.random.uuid(),
registryId: Number(registryId),
sourceProductId: product_id,
},
recommendedProducts: [
recommendedProduct(),
recommendedProduct(),
recommendedProduct(),
],
},
}),
},
}
return res(
// Set custom status
ctx.status(200),
// Set headers
ctx.set({ 'X-Header': 'Mocked value' }),
// Delay the response
ctx.delay(1000),
// send JSON response body
ctx.json(resJson)
)
})
)
/* Start the Service Worker */
start()
Hey, @derekr. Thanks for reporting this. Looks like something is wrong with applying the mask to request's URL. That's where MSW gets the parameters. URL query should not be taken into account during this process.
I'll double check and update here.
The issue is fixed and will be released in the next patch release 0.15.6.
I've discovered that query parameters were not stripped off when passing the actual URL to the path matching function. This resulted into broken matching.
To properly fix it, I strip request URL query parameters and hashes in both predicate for each request, and within the path matching function:
The
matchfunction we use fromnode-match-pathalways performs a strict matching when given a URL string. So in our case we need to pass the "clean" URL for the matching to be precise.
During the debugging I've discovered that when a query parameter follows a trailing path parameter, its being matches as a part of a path parameter. Consider:
match('/user/:userId', '/user/abc-123?hello=true')
// { params: { userId: 'abc-123?hello=true' } }
I've treated this as an issue originally, but when compared to how path-to-regexp (used by Express) works, it also captures that trailing query as a part of a path parameter:

I'd love to maintain a compatibility with Express path matching to grant the best experience for users who are already familiar with Express. I will live this trailing query behavior as-is for now.
Thanks for the speedy fix and thorough walkthrough!
Most helpful comment
Thanks for the speedy fix and thorough walkthrough!