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

zomoc

v0.2.0

Published

A type-safe API mocking tool for frontend development, powered by axios and Zod.

Readme

English | 한국어


Zomoc: A Type-Safe API Mocking Tool

Zomoc is a tool that decouples your frontend development from unstable backend APIs by automatically generating realistic mock data based on TypeScript interfaces. It can be applied to non-Vite environments like Next.js and Create React App via its CLI, and seamlessly integrates as a plugin in a Vite environment to provide a convenient developer experience.

Zomoc is not a zero-config tool, but a "low-config" one. With just a few lines of configuration, you can create a powerful, type-safe development environment that allows you to work independently of the backend's status.

Features

  • Reliable API Decoupling: Keep developing your UI without interruption, even when the real API is down or changing.
  • Structural Type-Safety: Guarantees your mock data's structure always matches your TypeScript interfaces, preventing data-shape-related bugs.
  • Highly Automated: Scans your code to generate mock data, keeping it in sync with your type definitions.
  • Flexible File Structure: Locate your mock definitions and interface files anywhere in your project using glob patterns.
  • Dynamic Route Matching: Supports dynamic URL patterns like /users/:id out of the box.
  • Pagination Support: Automatically generates paginated responses and intelligently handles both paginated and regular array types.
  • Custom Data Generators: Hook in your own data generation logic (e.g., using @faker-js/faker) for more realistic mock data.

Installation

npm install -D zomoc

zomoc has axios and zod as peer dependencies. npm 7+ will install them automatically if they are not already in your project.

🚀 Getting Started (CLI)

For environments that don't use Vite (like Next.js or Create React App), you can use zomoc via its CLI. This approach works by generating a physical configuration file instead of using a virtual module.

Step 1: Define Mocks and Types

This step is identical to the Vite version. Create a mock.json file and a corresponding interface.ts file.

src/api/mock.json

{
  "GET /users": "IUserListResponse"
}

src/api/interface.ts

export interface IUser {
  id: string
  name: string
  email: string
}

export interface IUserListResponse {
  users: IUser[]
  total: number
}

Step 2: Generate the Registry File

Run the generate command in your terminal to create the mock configuration file.

npx zomoc generate

This command creates a .zomoc/registry.ts file in your project root. You'll need to re-run this command whenever you change your mock.json or type files.

Pro-Tip: To greatly improve your development experience, set up a script in your package.json to run npx zomoc generate --watch alongside your dev command. This will automatically regenerate the file on changes.

Step 3: Activate the Interceptor

Set up the interceptor by importing directly from the file generated by the CLI.

// src/shared/api/index.ts
import axios from "axios"
// Use 'zomoc/cli' instead of 'zomoc'.
import { setupMockingInterceptor } from "zomoc/cli"
// Import directly from the generated file.
import { finalSchemaUrlMap } from "../../.zomoc/registry"

const api = axios.create({ baseURL: "https://api.example.com" })

setupMockingInterceptor(api, {
  enabled: process.env.NODE_ENV === "development",
  registry: finalSchemaUrlMap,
  debug: true, // Optional: log mocked requests
})

export { api }

🚀 Getting Started (Vite Plugin)

Here’s how to get Zomoc up and running in 3 steps in a Vite project.

1. Configure Vite & TypeScript

Add zomoc to your vite.config.ts and update tsconfig.json to recognize the virtual module.

vite.config.ts

// vite.config.ts
import { defineConfig } from "vite"
import zomoc from "zomoc/vite"

export default defineConfig({
  plugins: [
    zomoc(), // Add the plugin
  ],
})

tsconfig.json

{
  "compilerOptions": {
    // ... other options
    "types": ["zomoc/client"]
  }
}

2. Create a Mock and an Interface

Create a mock.json file and a corresponding interface.ts file.

src/api/mock.json

{
  "GET /users": "IUserListResponse"
}

src/api/interface.ts

export interface IUser {
  id: string
  name: string
  email: string
}

export interface IUserListResponse {
  users: IUser[]
  total: number
}

3. Activate the Interceptor

In your central axios configuration, set up the interceptor.

// src/shared/api/index.ts
import axios from "axios"
import { setupMockingInterceptor } from "zomoc"
import { finalSchemaUrlMap } from "virtual:zomoc" // Import from the virtual module

const api = axios.create({ baseURL: "https://api.example.com" })

// Zomoc will now intercept calls made with this instance
setupMockingInterceptor(api, {
  enabled: import.meta.env.DEV, // Active only in development
  registry: finalSchemaUrlMap,
  debug: true, // Optional: log mocked requests
})

export { api }

That's it! Now, when your app calls api.get('/users') in dev mode, Zomoc will serve a type-safe mock response.

Vite Plugin vs. CLI: Which Should I Use?

| Aspect | Vite Plugin | CLI | | :------------------- | :------------------------ | :------------------------------------------- | | Workflow | Fully Automatic (HMR) | Manual (generate) or Semi-Auto (--watch) | | Setup | vite.config.ts | package.json scripts | | Information Flow | Virtual Module (Abstract) | Physical File (Explicit) |

  • If you are using a Vite project, use the Vite plugin. It offers the best developer experience.
  • For Next.js, CRA, or any other environment, use the CLI. Using it with the --watch option is highly recommended.

🧪 Standalone Data Generation (for Storybook & Tests)

In addition to intercepting API requests, Zomoc provides a powerful feature to use its data generator directly in your code. This is extremely useful when you need type-safe mock data without an API call, such as for populating your Storybook stories or writing unit and integration tests.

✨ Important: How Scanning Works

  • Independent of mock.json: This generator does not use the mock.json file at all. Instead, it directly scans the interfacePaths defined in your Zomoc configuration (zomoc.config.ts or Vite plugin options) to register all available interfaces into its registry.

  • "Survivor Principle" Scanning: Zomoc attempts to convert all interfaces from the specified paths into Zod schemas. However, if a conversion is not possible (e.g., due to complex generics not supported by ts-to-zod, or importing types defined outside of interfacePaths), that interface is silently skipped. This prevents the entire process from halting and ensures the registry is built only with successfully converted "surviving" schemas. Therefore, if you find a specific type is missing from the generator, it's worth checking its definition.

The usage is simple: initialize the generator with the finalSchemaTypeMap provided by Zomoc, and then call it with the name of the interface you need.

Step 1: Import createGenerator and the Registry

First, import the createGenerator function from zomoc and the finalSchemaTypeMap from the location appropriate for your environment.

For Vite Users (Virtual Module):

import { createGenerator } from "zomoc"
import { finalSchemaTypeMap } from "virtual:zomoc"

For CLI Users (Generated File):

// The import path for 'zomoc/cli' may vary based on your project structure.
import { createGenerator } from "zomoc/cli"
import { finalSchemaTypeMap } from "../.zomoc/registry" // Adjust the path to your generated file.

Step 2: Create and Use the Generator

Once you have the necessary modules, the usage is identical in any environment.

// 1. Initialize the generator with the registry.
const getMock = createGenerator(finalSchemaTypeMap)

// 2. Generate data by passing the name of a scanned interface.
// The return value is perfectly type-inferred!
const mockUser = getMock("IUser")
const mockProduct = getMock("IProduct")

// Example usage in a Storybook story:
export default {
  title: "Components/UserProfile",
  component: UserProfile,
  args: {
    // Pass the generated mock data as a prop.
    user: getMock("IUser"),
  },
}

Advanced Options: You can control how data is generated by passing an options object as the second argument to the getMock function. This allows you to apply most of the advanced generation strategies available in mock.json, such as creating an array of objects (repeatCount) or simulating a paginated response. For more details on the available options, please refer to the In-Depth Guide.

// Create an array of 10 user objects
const userList = getMock("IUser", { repeatCount: 10 })

// Simulate a paginated response
// (assuming IProductResponse is shaped like { data: IProduct[], total: number })
const paginatedProducts = getMock("IProductResponse", {
  pagination: {
    itemsKey: "data", // The key for the data array
    totalKey: "total", // The key for the total count
  },
})

This approach allows you to flexibly use the same high-quality, type-safe mock data engine that the interceptor uses, anywhere in your project.

📚 In-Depth Guide

This section covers advanced configuration and features.

Mocking Error Responses and Multiple Statuses

While the simple configuration is great for mocking successful "happy path" responses (200 OK), Zomoc also provides a powerful "Response Map Mode" to handle various HTTP status codes and error cases for a single API endpoint. This allows you to robustly test how your application behaves in different scenarios, such as when data is not found (404) or a server error occurs (500).

The core idea is to use a responses object where each key is an HTTP status code, and a top-level status property acts as a "switch" to determine which response is currently active.

1. Configure the responses map in your mock definition:

src/api/mock.json

{
  "GET /api/user/:id": {
    "status": 404,
    "responses": {
      "200": {
        "responseType": "IUserProfile"
      },
      "404": {
        "responseBody": { "message": "User not found" }
      },
      "500": {
        "responseBody": { "error": "Internal Server Error" }
      }
    }
  }
}
  • status: This is the switch. The value here (e.g., 404) determines which of the definitions inside responses will be used. You can change this value to easily switch between testing a success, a not-found error, or a server error.
  • responses: This object maps status codes to their respective response definitions.
    • "200": For a 200 status, Zomoc will generate data based on the IUserProfile interface as usual.
    • "404" and "500": For these statuses, we use responseBody.

2. Using responseBody for Direct Data Injection

The responseBody key allows you to bypass the schema-based data generation and return a specific, predefined JSON object directly. This is perfect for defining fixed error messages or testing edge-case data structures.

Note: responseBody takes precedence. If both responseBody and responseType are defined for the same response, the value in responseBody will always be used. responseBody can only be used inside the responses map.

3. AxiosError-Compliant Responses

When Zomoc serves a response with a status code of 400 or higher, it doesn't just resolve the promise with an error status. Instead, it rejects the promise with an AxiosError-like object. This object includes properties like isAxiosError: true and a response object containing the status, data, etc.

This is a critical feature because it means your application's error handling logic (e.g., try...catch blocks or react-query's onError callback) will work identically for both real API errors and mocked errors, ensuring your tests are realistic and reliable.


Pagination Mocking

Zomoc can intelligently mock paginated API responses. When a pagination configuration is provided for a specific endpoint, Zomoc will read the page and size from the request (query params or body) and generate the exact number of items requested.

1. Configure the pagination object in your mock definition:

In your mock.json, instead of just a type name string, use an object with a responseType and a pagination key.

src/api/mock.json

{
  "GET /users": {
    "responseType": "IUserListResponse",
    "pagination": {
      "itemsKey": "users",
      "totalKey": "total",
      "pageKey": "page",
      "sizeKey": "size"
    }
  }
}
  • itemsKey: The key in the response object that holds the array of items.
  • totalKey: The key for the total number of items.
  • pageKey: The name of the parameter for the page number (defaults to "page").
  • sizeKey: The name of the parameter for the page size (defaults to "size").

If you don't provide these parameters in your API call, Zomoc will use default values (page: 1, size: 10).

2. Make an API call with pagination params:

// e.g., GET /users?page=1&size=10
api.get("/users", { params: { page: 1, size: 10 } })

Zomoc will intercept this call and return a response where the users array contains exactly 10 mock items.

Note: If an endpoint is configured for pagination, zomoc will generate the requested number of items. For regular array responses (like IUser[]) that are not configured for pagination, it will automatically generate a random number of items (1-3) to make the data feel more dynamic.

Controlling Array Length

For non-paginated endpoints that return an array, you can specify the exact number of items to generate using the repeatCount option. If not provided, a random number of items (1-3) will be generated.

Unlike paginated responses which are objects containing an array, this feature is for APIs that directly return an array.

To use this, you must first explicitly define and export a type for your array in your interface file. This is because Zomoc needs to find a Zod schema that corresponds to an array (ZodArray) to apply the repeatCount logic correctly.

1. Define and export the array type:

interface.ts

export interface ITag {
  id: number
  name: string
}

// Explicitly define and export the array type.
export type ITagList = ITag[]

2. Use the array type's name in mock.json:

mock.json

{
  "GET /api/tags": {
    "responseType": "ITagList",
    "repeatCount": 5
  }
}

This configuration will always return an array containing exactly 5 mock ITag items.

Key Difference: repeatCount vs. pagination It's important to understand when to use repeatCount and when to use pagination.

  • Use repeatCount when the API response is an array itself. The responseType must be an array type (e.g., type MyArray = Item[]).
  • Use pagination when the API response is an object that contains an array. The responseType must be an object type.

Applying repeatCount to a responseType that is an object will not affect the length of arrays inside that object. Those arrays will default to a random length of 1-3 items.

Putting It All Together: A Quick Guide

Here’s a summary table to help you decide which configuration to use based on your API's response shape.

| Your API Response... | Your Goal | mock.json Configuration | Result | | :-------------------------------------------------------------- | :------------------------------- | :--------------------------- | :--------------------------------------------------- | | Is an object with an array(e.g., { users: [], ... }) | Control array length via request | Use pagination object | Array length matches the size param in the request | | Is an object with an array(e.g., { users: [], ... }) | Let Zomoc decide the length | Omit pagination object | Inner array gets a random length (1-3) | | Is an array itself(e.g., [{}, {}]) | Set a fixed array length | Use repeatCount number | Array length matches repeatCount | | Is an array itself(e.g., [{}, {}]) | Let Zomoc decide the length | Omit repeatCount number | Array gets a random length (1-3) |

Important: Do not use pagination for a responseType that is an array, and do not expect repeatCount to work on a responseType that is an object. Mixing them will lead to unexpected behavior or errors.

Mocking Strategy: Fixed vs. Random

For union types ('a' | 'b') or enum types in your interfaces, you can control how zomoc generates mock data. This is particularly useful for creating predictable tests.

1. Configure the mockingStrategy in your mock.json:

{
  "GET /api/status": {
    "responseType": "IStatus",
    "mockingStrategy": "fixed"
  }
}
  • mockingStrategy: (Optional) Specifies how to generate data for ZodUnion or ZodEnum types.
    • "random" (default): Randomly selects one of the union/enum options. This is great for discovering edge cases.
    • "fixed": Always selects the first option defined in the union/enum. This is essential for creating predictable and stable tests where you need to rely on a specific value.

Example interface.ts:

export type Status = "Pending" | "Success" | "Failed"

export interface IStatus {
  status: Status
}

With mockingStrategy: "fixed", the status field will always be "Pending". With "random", it could be any of the three values.

URL Matching and Priority

Zomoc uses path-to-regexp for URL matching, which is the same engine used by frameworks like Express.js. This allows you to define dynamic URL paths.

Key Principles:

  1. Dynamic Segments: Use colons (:) to define dynamic parts of a URL (e.g., GET /api/users/:userId).
  2. Query Strings: Query strings (?key=value) are ignored during matching, so you only need to define the URL path.
  3. Matching Order: Rules in mock.json are evaluated from top to bottom. The first rule that matches the incoming request will be used, and the evaluation will stop.

This means the order of your rules is critical. More specific paths must always be placed before more general, dynamic paths.

Example of incorrect order:

{
  "GET /api/users/:userId": "IUserProfile",
  "GET /api/users/me": "IMyProfile"
}

In this case, a request to /api/users/me will be incorrectly matched by the first rule (GET /api/users/:userId), with userId being "me".

Example of correct order:

{
  "GET /api/users/me": "IMyProfile",
  "GET /api/users/:userId": "IUserProfile"
}

With this order, a request to /api/users/me is correctly caught by the first, more specific rule.

Customizing File Paths

By default, Zomoc searches for files matching **/mock.json, **/interface.ts, and **/type.ts. You can override these paths via the CLI or your Vite config.

CLI

You can specify custom paths by passing the --mock-paths and --interface-paths options to the generate command. Each option can accept multiple glob patterns separated by spaces.

# Pass two patterns to --mock-paths and one to --interface-paths
npx zomoc generate \
  --mock-paths "src/mocks/api.json" "src/features/**/mock.json" \
  --interface-paths "src/types/**/*.ts"

Vite Plugin

In your vite.config.ts, you can pass the options directly to the plugin.

// vite.config.ts
import zomoc from "zomoc/vite"

export default {
  plugins: [
    zomoc({
      // Glob patterns for your mock definition files.
      mockPaths: ["src/features/**/mock.json"],
      // Glob patterns for your TypeScript interface files.
      interfacePaths: ["src/features/**/model/*.ts"],
    }),
  ],
}

Custom Data Generators

By default, zomoc generates simple placeholder data. For more realistic mocks, you can provide your own generator functions using the customGenerators option. This is powerful when combined with a library like @faker-js/faker.

1. First, install faker:

npm install -D @faker-js/faker

2. Then, pass your custom generators to the interceptor:

// src/shared/api/index.ts
import { setupMockingInterceptor, type CustomGenerators } from "zomoc"
import { faker } from "@faker-js/faker"

// Define your custom generator functions
const customGenerators: CustomGenerators = {
  string: (key) => {
    if (key.toLowerCase().includes("email")) return faker.internet.email()
    if (key.toLowerCase().includes("name")) return faker.person.fullName()
    return faker.lorem.sentence()
  },
  number: () => faker.number.int({ max: 1000 }),
}

setupMockingInterceptor(api, {
  // ...other options
  customGenerators,
})

With this setup, zomoc will call your functions to generate context-aware data.

Limitations

While Zomoc is a helpful tool, it has some limitations you should be aware of:

  • Generic Types: Zomoc relies on ts-to-zod

🤝 Contributing

Contributions are welcome! If you have a feature request, bug report, or pull request, please feel free to open an issue or PR.

License

This project is licensed under the MIT License.