npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

zod-mock-api

v1.0.1

Published

Type-safe mock servers for ts-rest, oRPC, and tRPC API contracts, powered by MSW and Zod

Downloads

184

Readme

zod-mock-api

License: MIT

Type-safe mock servers for ts-rest, oRPC, and tRPC API contracts, powered by MSW and Zod schema generation.

zod-mock-api automatically generates realistic fake responses from your Zod schemas so you can start testing immediately — then override individual endpoints with static values or callback functions when you need fine-grained control.

Features

  • Zero boilerplate — pass your contract and get a running mock server.
  • Auto-generated responses — every route is pre-populated with random data that satisfies its Zod schema (via @anatine/zod-mock).
  • Full type safety — paths, status codes, request bodies, and response bodies are all inferred from the contract; typos are caught at compile time.
  • Per-test overrides — override any route with a static response or a callback that receives the request info and a pre-generated default.
  • Multi-framework — first-class support for ts-rest, oRPC, and tRPC contracts.
  • Nested routers — deeply nested contracts are flattened automatically.
  • All HTTP methods — GET, POST, PUT, PATCH, and DELETE (including bodyless responses).
  • Escape hatch — access the underlying MSW server and http helper for custom handlers.
  • Adapter architecture — the generic MSW + Zod plumbing (mockHttpApi) is separated from framework adapters, making it straightforward to add support for additional contract libraries.

Installation

Install the core package and its peer dependencies:

npm install --save-dev zod-mock-api @anatine/zod-mock @faker-js/faker msw zod

Then install the peer dependency for the framework(s) you use:

# For ts-rest
npm install --save-dev @ts-rest/core

# For oRPC
npm install --save-dev @orpc/contract

# For tRPC
npm install --save-dev @trpc/server

Note: @ts-rest/core, @orpc/contract, and @trpc/server are all optional peer dependencies. You only need the one(s) matching the adapters you use.

Imports

You can import everything from the main entry point, or use the dedicated sub-path exports to only pull in the adapter you need:

// Everything (barrel export)
import { mockTsRest, mockOrpc, mockTrpc } from 'zod-mock-api';

// Per-adapter (tree-shakeable, avoids loading unused adapters)
import { mockTsRest } from 'zod-mock-api/ts-rest';
import { mockOrpc } from 'zod-mock-api/orpc';
import { mockTrpc } from 'zod-mock-api/trpc';

// Generic HTTP layer (for building custom adapters)
import { createMockServer, registerOverride } from 'zod-mock-api/http';

Quick Start

ts-rest

import { initContract } from '@ts-rest/core';
import { mockTsRest } from 'zod-mock-api';
import { z } from 'zod';

const c = initContract();

const contract = c.router({
  getUsers: {
    method: 'GET',
    path: '/users',
    responses: {
      200: z.array(z.object({ id: z.string(), name: z.string() })),
    },
  },
  createUser: {
    method: 'POST',
    path: '/users',
    body: z.object({ name: z.string() }),
    responses: {
      201: z.object({ id: z.string(), name: z.string() }),
    },
  },
});

// Create the mock server — all routes are immediately available
// with randomly generated responses.
const server = mockTsRest('http://localhost:3000', contract);

// Override specific endpoints as needed:
server.get('/users', 200, [
  { id: '1', name: 'Alice' },
  { id: '2', name: 'Bob' },
]);

// Use in your tests, then clean up:
server.close();

oRPC

import { oc } from '@orpc/contract';
import { mockOrpc } from 'zod-mock-api';
import { z } from 'zod';

const contract = {
  getUsers: oc
    .route({ method: 'GET', path: '/users' })
    .output(z.array(z.object({ id: z.string(), name: z.string() }))),
  createUser: oc
    .route({ method: 'POST', path: '/users', successStatus: 201 })
    .input(z.object({ name: z.string() }))
    .output(z.object({ id: z.string(), name: z.string() })),
};

// Create the mock server — all procedures are immediately available
// with randomly generated responses.
const server = mockOrpc('http://localhost:3000', contract);

// Override by procedure key (fully type-safe):
server.mock('getUsers', [
  { id: '1', name: 'Alice' },
  { id: '2', name: 'Bob' },
]);

// Use in your tests, then clean up:
server.close();

tRPC

import { initTRPC } from '@trpc/server';
import { mockTrpc } from 'zod-mock-api';
import { z } from 'zod';

const t = initTRPC.create();

const appRouter = t.router({
  getUser: t.procedure
    .input(z.object({ id: z.string() }))
    .output(z.object({ id: z.string(), name: z.string() }))
    .query(({ input }) => ({ id: input.id, name: 'Alice' })),
  createUser: t.procedure
    .input(z.object({ name: z.string() }))
    .output(z.object({ id: z.string(), name: z.string() }))
    .mutation(({ input }) => ({ id: '1', name: input.name })),
});

// Create the mock server — procedures with .output() Zod schemas
// get auto-generated responses. Include any tRPC path prefix in the host.
const server = mockTrpc('http://localhost:3000/trpc', appRouter);

// Override by procedure key (fully type-safe):
server.mock('getUser', { id: '1', name: 'Alice' });

// Use in your tests, then clean up:
server.close();

Usage

ts-rest

Static Responses

The simplest override provides a literal response value:

server.get('/users', 200, [{ id: '1', name: 'Alice' }]);
server.post('/users', 201, { id: '2', name: 'Bob' });
server.put('/users/:id', 200, { id: '2', name: 'Bob Updated' });
server.patch('/users/:id', 200, { id: '2', name: 'Bob Patched' });

Callback Resolvers

For dynamic responses, pass a callback. It receives the MSW request info (with a typed json() method) and a pre-generated default response:

server.post('/users', 201, async ({ request }, defaultResponse) => {
  const body = await request.json();
  return {
    ...defaultResponse, // includes randomly generated fields
    name: body.name, // override specific fields
  };
});

Error Status Codes

You can simulate error responses, including status codes not defined in your contract (e.g. 500 from a load balancer):

server.get('/users', 404, { error: 'Not found' });
server.get('/users', 500, { message: 'Internal server error' });

DELETE with No Body

ts-rest's c.noBody() is fully supported:

server.delete('/users/:id', 204, undefined);

oRPC

Static Responses

Override a procedure by its key path in the contract:

server.mock('getUsers', [{ id: '1', name: 'Alice' }]);
server.mock('nested.getUser', { id: '1', name: 'Alice' });

Callback Resolvers

For dynamic responses, pass a callback with access to the request and a pre-generated default:

server.mock('createUser', async ({ request }, defaultResponse) => {
  const body = await request.json();
  return {
    ...defaultResponse,
    name: body.name,
  };
});

No-body Procedures

Procedures without an output schema respond with an empty body:

server.mock('deleteUser', undefined);

Default RPC Routing

Procedures that don't specify .route({ path }) get a path derived from their key in the contract (e.g. planet.find/planet/find) with the default method POST, matching oRPC's built-in RPC routing behaviour.

tRPC

Static Responses

Override a procedure by its key path in the router:

server.mock('getUser', { id: '1', name: 'Alice' });
server.mock('post.byId', { id: '1', title: 'Hello', body: 'World' });

Callback Resolvers

For dynamic responses, pass a callback with access to the request and a pre-generated default:

server.mock('createUser', async ({ request }, defaultResponse) => {
  const body = await request.json();
  return {
    ...defaultResponse,
    name: body.name,
  };
});

Output Schemas and Auto-generation

tRPC procedures that define .output(zodSchema) get auto-generated mock responses, just like ts-rest and oRPC. Procedures that rely on TypeScript inference from the handler's return type (no .output() call) respond with an empty body by default — override them with mock():

// Procedure defined with .output() → auto-generated response ✓
// Procedure without .output() → empty body, override with:
server.mock('health', { status: 'ok' });

HTTP Method Mapping

tRPC queries are intercepted as GET requests and mutations as POST, matching tRPC's HTTP transport. Procedure paths use dot notation for nested routers (e.g. post.create${host}/post.create).

Shared

Custom MSW Handlers

Both adapters expose the underlying MSW primitives for routes outside your contract:

server.use(
  server.msw.http.get('http://localhost:3000/health', () => {
    return new Response('ok');
  }),
);

Request Validation

Request bodies for mutation routes (POST, PUT, PATCH) are automatically validated against the contract's Zod schema (ts-rest and oRPC). A ZodError is thrown if the request body doesn't match, which helps catch test setup mistakes. tRPC procedures skip body validation since tRPC queries use query params and mutations use a wrapped body format.

API

mockTsRest(host, ...contracts)

Creates and starts a mock server for ts-rest contracts.

| Parameter | Type | Description | | -------------- | ------------- | ------------------------------------------------------- | | host | string | Base URL to intercept (e.g. 'http://localhost:3000'). | | ...contracts | AppRouter[] | One or more ts-rest router contracts. |

Returns an object with:

| Property / Method | Description | | ------------------------------------------ | ------------------------------------------------------ | | get(path, status, resolver, options?) | Override a GET route. | | post(path, status, resolver, options?) | Override a POST route. | | put(path, status, resolver, options?) | Override a PUT route. | | patch(path, status, resolver, options?) | Override a PATCH route. | | delete(path, status, resolver, options?) | Override a DELETE route. | | use(...handlers) | Register additional MSW request handlers. | | close() | Stop the mock server and clean up. | | msw.server | The underlying MSW SetupServerApi instance. | | msw.http | The MSW http namespace for building custom handlers. |

mockOrpc(host, ...contracts)

Creates and starts a mock server for oRPC contracts.

| Parameter | Type | Description | | -------------- | ------------------ | ------------------------------------------------------- | | host | string | Base URL to intercept (e.g. 'http://localhost:3000'). | | ...contracts | OrpcRouterLike[] | One or more oRPC contract routers. |

Returns an object with:

| Property / Method | Description | | ------------------------------- | ------------------------------------------------------ | | mock(key, resolver, options?) | Override a procedure by its dotted key path. | | use(...handlers) | Register additional MSW request handlers. | | close() | Stop the mock server and clean up. | | msw.server | The underlying MSW SetupServerApi instance. | | msw.http | The MSW http namespace for building custom handlers. |

mockTrpc(host, ...routers)

Creates and starts a mock server for tRPC routers.

| Parameter | Type | Description | | ------------ | ------------------ | -------------------------------------------------------------------------------------------- | | host | string | Base URL to intercept, including any tRPC path prefix (e.g. 'http://localhost:3000/trpc'). | | ...routers | TrpcRouterLike[] | One or more tRPC routers created with t.router(). |

Returns an object with:

| Property / Method | Description | | ------------------------------- | ----------------------------------------------------------------------------- | | mock(key, resolver, options?) | Override a procedure by its dotted key path. Queries → GET, mutations → POST. | | use(...handlers) | Register additional MSW request handlers. | | close() | Stop the mock server and clean up. | | msw.server | The underlying MSW SetupServerApi instance. | | msw.http | The MSW http namespace for building custom handlers. |

Note: Procedures with .output(zodSchema) get auto-generated defaults. Procedures without .output() respond with an empty body until overridden via mock().

Resolver

Each override helper accepts a resolver which is either:

  • A static value matching the response schema for the given endpoint, or
  • A callback (info, defaultResponse) => response where:
    • info is the MSW resolver info with a typed request.json().
    • defaultResponse is a randomly generated value that satisfies the schema.
    • The return value can be a Promise.

Architecture

The codebase is split into a generic layer and framework-specific adapters:

| Module | Responsibility | | ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | mockHttpApi.ts | Framework-agnostic MSW + Zod plumbing: server lifecycle, default handler creation, override registration, mock data generation, and shared types/errors. | | mockTsRest.ts | ts-rest adapter: contract-walking, type generics (GetPath, FilterRoute, GetResponseBody, etc.), and the mockTsRest() entry point. Uses HTTP method + path for overrides. | | mockOrpc.ts | oRPC adapter: contract-walking via the ~orpc property, type generics (ProcedureKeys, InferOrpcOutput, etc.), and the mockOrpc() entry point. Uses dotted procedure keys for overrides. | | mockTrpc.ts | tRPC adapter: router-record walking via _def.record, type generics (TrpcProcedureKeys, InferTrpcOutput, etc.), and the mockTrpc() entry point. Uses dotted procedure keys for overrides. Queries → GET, mutations → POST. |

To add support for a new contract library, create a new adapter that imports from mockHttpApi and adds framework-specific type inference and route extraction. The generic layer handles all MSW interaction.

Key exports from mockHttpApi

| Export | Purpose | | ------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | | createMockServer(handlers) | Starts an MSW server with default handlers; returns { msw, use, close }. | | createDefaultHandler(host, method, path, status, schema) | Creates a single default MSW handler for a route. null schema = no body. | | registerOverride(server, host, fn, path, status, schema, resolver, validator, opts) | Registers an override handler — the runtime core of every HTTP-method helper. | | mockSchema(schema) | Generates random data conforming to a Zod schema. | | Method, MutationMethod, GetResolver | Shared types reused by every adapter. |

License

MIT