This is a fork.
msw-trpcby Malo Guertin, forked at2.0.1and published to GitHub Packages as@blurb/msw-trpcfor use inside the org. It adds handler fallthrough on top of upstream. See Releasing to cut a new version.
- Create MSW handlers from your tRPC router.
- Get your tRPC typing into your MSW handlers.
- Augments MSW with utils for tRPC.
- Use it like you would use the tRPC client.
- Merged routers supported !
- Use
mswv2
As someone who loves MSW and was already using it I wanted to keep using it instead of mocking tRPC. While it is possible to simply write the Rest handlers it felt like it would be great not to lose the full power of tRPC types in the tests.
1. Point the @blurb scope at GitHub Packages.
GitHub Packages requires authentication even for public packages, so the consuming repo needs an .npmrc:
@blurb:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=${NODE_AUTH_TOKEN}
NODE_AUTH_TOKEN can be a personal access token with read:packages, or — in GitHub Actions — the built-in
secrets.GITHUB_TOKEN, since this package is public.
2. Install @blurb/msw-trpc.
npm i @blurb/msw-trpc --save-dev3. build your trpcMsw with createTRPCMsw.
import { createTRPCMsw } from '@blurb/msw-trpc'
import type { AppRouter } from 'path/to/your/router'
export const trpcMsw = createTRPCMsw<AppRouter>() /* 👈 */4. Start using it.
const server = setupServer(
trpcMsw.userById.query(() => ({ id: '1', name: 'Uncle bob' })),
trpcMsw.createUser.mutation((name) => ({ id: '2', name }))
)You can find examples of how to use it in the test-react package or in the test-node package.
createTRPCMsw returns a Proxy that infers types from your AppRouter
// all queries will expose a query function that accepts a MSW handler
trpcMsw.myQuery.query(() => {})
// all mutations will expose a mutation function that accepts a MSW handler
trpcMsw.myMutation.mutation(() => {})Return the fallthrough sentinel from a mock to decline the request, so MSW keeps looking for the next matching
handler. This is MSW's handler fallthrough, and it lets you mock
a procedure conditionally on its input:
import { createTRPCMsw, fallthrough } from '@blurb/msw-trpc'
server.use(
trpcMsw.userById.query(({ input }) => (input === '1' ? { id: '1', name: 'Uncle bob' } : fallthrough)),
trpcMsw.userById.query(() => ({ id: '99', name: 'Anyone else' }))
)Handlers are tried in the order given within a single use() call, so the declining handler goes first. The next
handler can be another msw-trpc handler or a plain http.* one.
Returning undefined is not a fallthrough — it responds with an empty tRPC result, which is the correct response for
a procedure that returns nothing. Throwing a TRPCError is not a fallthrough either: mocking an error handles the
request.
One MSW behaviour to be aware of: if every matching handler declines, the request goes to the real network.
onUnhandledRequest: 'error' does not catch it, because a declining handler still counts as a match.
You need to pass a httpLink to the createTRPCMsw function like you would do with the tRPC client.
You can pass an optional transformer like superjson to the createTRPCMsw function.
interface TRPCMswConfig {
links: Link[]
transformer?: TRPCCombinedDataTransformer
}Peer dependencies:
Please note:
- Batch is not yet supported
@blurb/msw-trpc is published to GitHub Packages by .github/workflows/npm-publish.yml,
which runs on a published GitHub Release and authenticates with the workflow's built-in GITHUB_TOKEN.
-
Bump
versioninpackages/msw-trpc/package.jsonand merge that tomain. -
Cut a release whose tag matches that version — the workflow fails fast if they disagree:
gh release create v2.1.0 --title v2.1.0 --notes 'Add handler fallthrough support' -
Watch it publish with
gh run watch, then confirm at the package page.
Versions follow upstream's numbering: the fork's base is upstream 2.0.1, and fork-only changes bump from there.
