Skip to content

Repository files navigation

API Mocker

A lightweight TypeScript library for mocking API endpoints during frontend development. Perfect for developing frontend applications before the backend is ready, or for testing different API scenarios.

Features

  • Easy Integration: Simple API that works with any HTTP client (fetch, axios, etc.)
  • TypeScript Support: Full TypeScript support with type definitions
  • Flexible Matching: Support for exact paths and dynamic parameters (:id)
  • Advanced Matching: Match requests by query parameters, headers, or custom logic
  • Dynamic Responses: Create responses with custom logic
  • Stateful Mocks: Built-in state management for realistic CRUD operations
  • Network Error Simulation: Simulate timeouts, connection errors, and aborts
  • Priority System: Control which mock takes precedence with priority levels
  • Delay Simulation: Simulate network delays for realistic testing
  • Request Logging: Optional request logging for debugging
  • Easy Switching: Simple enable/disable for switching to real APIs
  • No Dependencies: Zero runtime dependencies

Requirements

  • Node.js 18 or later for local development and testing (earlier versions must polyfill the Fetch API).
  • Browsers or runtimes that expose fetch, Request, and Response globals. When targeting older environments, install a fetch polyfill before creating an ApiMocker instance.

Installation

npm install @onamfc/api-mocker

Quick Start Guide

Step 1: Create Your Mock Setup File

Create a dedicated file for your API mocks. This keeps your mock configuration organized and separate from your main application code.

Create src/mocks/api-mocks.ts

import{createMocker}from'@onamfc/api-mocker';// Create the mocker instanceexportconstapiMocker=createMocker({baseUrl: 'https://api.yourapp.com',// Your API base URLlogRequests: true,// Enable logging to see which requests are being mockedglobalDelay: 500// Add 500ms delay to all requests for realism});// Define your mock endpointsexportfunctionsetupMocks(){// Simple GET endpointapiMocker.get('/users',[{id: 1,name: 'John Doe',email: 'john@example.com',role: 'admin'},{id: 2,name: 'Jane Smith',email: 'jane@example.com',role: 'user'},{id: 3,name: 'Bob Johnson',email: 'bob@example.com',role: 'user'}]);// Dynamic endpoint with parametersapiMocker.get('/users/:id',{id: 1,name: 'John Doe',email: 'john@example.com',role: 'admin',createdAt: '2023-01-15T10:30:00Z',lastLogin: '2024-01-20T14:22:00Z'});// POST endpoint with custom statusapiMocker.post('/users',{id: 4,name: 'New User',email: 'newuser@example.com',role: 'user',createdAt: newDate().toISOString()},{status: 201,delay: 1000// Longer delay for POST requests});// PUT endpointapiMocker.put('/users/:id',{id: 1,name: 'Updated User',email: 'updated@example.com',role: 'admin',updatedAt: newDate().toISOString()});// DELETE endpointapiMocker.delete('/users/:id',{success: true,message: 'User deleted successfully'});}

Step 2: Initialize Mocks in Your Application

For React Applications:

💡 Environment variable tips: Create React App (and other Webpack-based builds) exposes values through process.env, while Vite exposes them via import.meta.env. Use the snippet that matches your toolchain so the browser build never tries to access an undefined process object.

Create src/mocks/index.ts

For Create React App / Webpack projects:

import{setupMocks,apiMocker}from'./api-mocks';constenv=typeofprocess!=='undefined'&&process?.env ? process.env : {};// Only enable mocks in developmentconstisDevelopment=env.NODE_ENV==='development';constuseMocks=env.REACT_APP_USE_MOCKS==='true'||isDevelopment;if(useMocks){console.log('API Mocking enabled');setupMocks();}else{console.log('Using real API');apiMocker.disable();}export{apiMocker};

Or for Vite-powered React projects:

import{setupMocks,apiMocker}from'./api-mocks';constenv=import.meta.env;// Only enable mocks in developmentconstisDevelopment=env.MODE==='development'||env.DEV;constuseMocks=env.VITE_USE_MOCKS==='true'||isDevelopment;if(useMocks){console.log('API Mocking enabled');setupMocks();}else{console.log('Using real API');apiMocker.disable();}export{apiMocker};

Then in your src/index.js or src/main.tsx:

importReactfrom'react';importReactDOMfrom'react-dom/client';importAppfrom'./App';// Import mocks BEFORE your app componentsimport'./mocks';constroot=ReactDOM.createRoot(document.getElementById('root'));root.render(<App/>);

For Vue Applications:

Create src/mocks/index.ts:

import{setupMocks,apiMocker}from'./api-mocks';exportfunctioninitializeMocks(){constisDevelopment=import.meta.env.DEV;constuseMocks=import.meta.env.VITE_USE_MOCKS==='true'||isDevelopment;if(useMocks){console.log('API Mocking enabled');setupMocks();}else{console.log('Using real API');apiMocker.disable();}}

Then in your src/main.js:

import{createApp}from'vue';importAppfrom'./App.vue';import{initializeMocks}from'./mocks';// Initialize mocks before creating the appinitializeMocks();createApp(App).mount('#app');

Step 3: Use Your Regular HTTP Client

Now your existing API calls will automatically use the mocked responses:

// This will use your mocked data automaticallyasyncfunctionfetchUsers(){try{constresponse=awaitfetch('https://api.yourapp.com/users');constusers=awaitresponse.json();console.log('Users:',users);// Will log your mocked usersreturnusers;}catch(error){console.error('Failed to fetch users:',error);}}// This will also work with dynamic parametersasyncfunctionfetchUser(id){constresponse=awaitfetch(`https://api.yourapp.com/users/${id}`);returnresponse.json();}// POST requests work tooasyncfunctioncreateUser(userData){constresponse=awaitfetch('https://api.yourapp.com/users',{method: 'POST',headers: {'Content-Type': 'application/json'},body: JSON.stringify(userData)});returnresponse.json();}

API Reference

createMocker(config?: MockConfig)

Factory helper that returns a new ApiMocker instance. Use this in your application bootstrap so you can easily share a configured mocker across modules.

ApiMocker

The main class responsible for intercepting fetch calls.

  • mock(endpoint) – Register a static endpoint. The response can be plain data, a MockResponse object ({ data, status?, headers? }), a native Response, or a function returning any of those (sync or async).
  • mockDynamic(endpoint) – Register a dynamic endpoint whose handler receives a RequestContext with params, query, headers, parsed body, and shared state.
  • get, post, put, patch, delete – Convenience wrappers around mock for common HTTP verbs.
  • enable() / disable() – Toggle interception without removing registered mocks.
  • clear() / remove(method, path) – Remove all mocks or a specific endpoint.
  • setState(key, value) / getState() / clearState() – Manage shared in-memory state used by dynamic handlers.
  • updateConfig(partialConfig) – Merge configuration updates at runtime (e.g., change baseUrl, add headers, adjust logging).
  • restore() – Restore the original fetch implementation. Call this when you no longer need mocking (for example in test teardown).

Endpoint Options

  • status, delay, headers, priority – Control the returned HTTP status, simulated latency, custom headers, and selection priority between overlapping mocks.
  • queryParams, requestHeaders, match(request) – Advanced matching rules to scope mocks to specific query strings, request headers, or arbitrary logic.
  • networkError – Simulate network failures ('timeout', 'connection_refused', or 'abort').
  • MockResponse support lets you fine-tune status and headers on a per-response basis without mutating the endpoint definition.

Advanced Usage Examples

1. Dynamic Responses with Custom Logic

// Create a more realistic user endpoint that responds based on the IDapiMocker.mockDynamic({path: '/users/:id',method: 'GET',handler: (context)=>{constuserId=parseInt(context.params.id);// Simulate user not foundif(userId>100){thrownewError('User not found');}return{id: userId,name: `User ${userId}`,email: `user${userId}@example.com`,role: userId===1 ? 'admin' : 'user',avatar: `https://api.dicebear.com/7.x/avataaars/svg?seed=${userId}`,createdAt: newDate(2023,0,userId).toISOString(),isOnline: Math.random()>0.5};}});

2. Stateful CRUD Operations

Create src/mocks/stateful-mocks.ts:

import{apiMocker}from'./api-mocks';letnextId=4;// Initialize some dataapiMocker.setState('users',[{id: 1,name: 'John Doe',email: 'john@example.com'},{id: 2,name: 'Jane Smith',email: 'jane@example.com'},{id: 3,name: 'Bob Johnson',email: 'bob@example.com'}]);exportfunctionsetupStatefulMocks(){// GET all users - returns current stateapiMocker.mockDynamic({path: '/users',method: 'GET',handler: (context)=>{returncontext.state.users||[];}});// GET single userapiMocker.mockDynamic({path: '/users/:id',method: 'GET',handler: (context)=>{constusers=context.state.users||[];constuser=users.find(u=>u.id===parseInt(context.params.id));if(!user){return{error: 'User not found'};}returnuser;},status: 200});// POST new user - adds to stateapiMocker.mockDynamic({path: '/users',method: 'POST',handler: (context)=>{constusers=context.state.users||[];constnewUser={id: nextId++,
...context.body,createdAt: newDate().toISOString()};users.push(newUser);context.state.users=users;returnnewUser;},status: 201});// PUT update user - modifies stateapiMocker.mockDynamic({path: '/users/:id',method: 'PUT',handler: (context)=>{constusers=context.state.users||[];constuserIndex=users.findIndex(u=>u.id===parseInt(context.params.id));if(userIndex===-1){return{error: 'User not found'};}users[userIndex]={
...users[userIndex],
...context.body,updatedAt: newDate().toISOString()};context.state.users=users;returnusers[userIndex];}});// DELETE user - removes from stateapiMocker.mockDynamic({path: '/users/:id',method: 'DELETE',handler: (context)=>{constusers=context.state.users||[];constuserIndex=users.findIndex(u=>u.id===parseInt(context.params.id));if(userIndex===-1){return{error: 'User not found'};}users.splice(userIndex,1);context.state.users=users;return{success: true,message: 'User deleted'};}});}

3. Advanced Request Matching

// Match based on query parametersapiMocker.mock({path: '/users',method: 'GET',response: [{id: 1,name: 'Admin User',role: 'admin'}],queryParams: {role: 'admin'}});apiMocker.mock({path: '/users',method: 'GET',response: [{id: 2,name: 'Regular User 1',role: 'user'},{id: 3,name: 'Regular User 2',role: 'user'}],queryParams: {role: 'user'}});// Match based on request headersapiMocker.mock({path: '/api/data',method: 'GET',response: {format: 'json',data: [1,2,3]},requestHeaders: {'Accept': 'application/json'}});// Custom matching logicapiMocker.mock({path: '/protected',method: 'GET',response: {message: 'Access granted',data: 'secret'},match: (request)=>{constauthHeader=request.headers.get('Authorization');returnauthHeader&&authHeader.startsWith('Bearer ');}});

4. Error Simulation

// HTTP error responsesapiMocker.get('/users/999',{error: 'User not found'},{status: 404,delay: 300});apiMocker.post('/users',{error: 'Validation failed',details: ['Email is required']},{status: 400});// Network error simulationapiMocker.mock({path: '/flaky-endpoint',method: 'GET',response: {},networkError: 'timeout'// Simulates network timeout});// Conditional errorsapiMocker.mockDynamic({path: '/unreliable',method: 'GET',handler: ()=>{// 30% chance of success, 70% chance of errorif(Math.random()>0.3){thrownewError('Service temporarily unavailable');}return{status: 'success',data: 'Hello World'};}});

File Organization

Here's a recommended file structure for organizing your mocks:

src/
├── mocks/
│ ├── index.ts # Main mock initialization
│ ├── api-mocks.ts # Basic mock definitions
│ ├── stateful-mocks.ts # Stateful CRUD operations
│ ├── error-mocks.ts # Error scenarios
│ └── data/
│ ├── users.ts # Mock user data
│ ├── products.ts # Mock product data
│ └── orders.ts # Mock order data
├── services/
│ ├── api.ts # Your API service functions
│ └── userService.ts # User-specific API calls
└── components/
└── ...

Example src/mocks/data/users.ts:

exportconstmockUsers=[{id: 1,name: 'John Doe',email: 'john@example.com',role: 'admin',avatar: 'https://api.dicebear.com/7.x/avataaars/svg?seed=1',createdAt: '2023-01-15T10:30:00Z',isActive: true},{id: 2,name: 'Jane Smith',email: 'jane@example.com',role: 'user',avatar: 'https://api.dicebear.com/7.x/avataaars/svg?seed=2',createdAt: '2023-02-20T14:22:00Z',isActive: true},// ... more users];exportconstmockUserProfiles={1: {bio: 'Software engineer with 10 years of experience',location: 'San Francisco, CA',website: 'https://johndoe.dev',socialLinks: {twitter: '@johndoe',linkedin: 'john-doe-dev'}},// ... more profiles};

Environment Configuration

Using Environment Variables

Create a .env file in your project root:

# Development
REACT_APP_USE_MOCKS=true
REACT_APP_API_URL=https://api.yourapp.com
# For Vue projects, use VITE_ prefix
VITE_USE_MOCKS=true
VITE_API_URL=https://api.yourapp.com

Conditional Mock Setup

// src/mocks/index.ts (Create React App / Webpack)import{setupMocks,setupStatefulMocks,apiMocker}from'./api-mocks';constenv=typeofprocess!=='undefined'&&process?.env ? process.env : {};constconfig={// Enable mocks in development or when explicitly requestedenabled: env.NODE_ENV==='development'||env.REACT_APP_USE_MOCKS==='true',// Use stateful mocks for more realistic testingstateful: env.REACT_APP_STATEFUL_MOCKS==='true',// Add delays to simulate real network conditionsrealistic: env.REACT_APP_REALISTIC_DELAYS==='true'};if(config.enabled){console.log('Initializing API mocks...');// Configure the mockerapiMocker.updateConfig({baseUrl: env.REACT_APP_API_URL,logRequests: true,globalDelay: config.realistic ? 500 : 0});// Setup mocksif(config.stateful){setupStatefulMocks();}else{setupMocks();}console.log('API mocks ready');}else{console.log('Using real API endpoints');apiMocker.disable();}
// src/mocks/index.ts (Vite)import{setupMocks,setupStatefulMocks,apiMocker}from'./api-mocks';constenv=import.meta.env;constconfig={// Enable mocks in development or when explicitly requestedenabled: env.DEV||env.MODE==='development'||env.VITE_USE_MOCKS==='true',// Use stateful mocks for more realistic testingstateful: env.VITE_STATEFUL_MOCKS==='true',// Add delays to simulate real network conditionsrealistic: env.VITE_REALISTIC_DELAYS==='true'};if(config.enabled){console.log('Initializing API mocks...');// Configure the mockerapiMocker.updateConfig({baseUrl: env.VITE_API_URL,logRequests: true,globalDelay: config.realistic ? 500 : 0});// Setup mocksif(config.stateful){setupStatefulMocks();}else{setupMocks();}console.log('API mocks ready');}else{console.log('Using real API endpoints');apiMocker.disable();}

Migration to Real APIs

When you're ready to connect to real APIs, you have several options:

1. Disable All Mocks

// Simply disable all mockingapiMocker.disable();

2. Gradual Migration

// Remove specific endpoints as real ones become availableapiMocker.remove('GET','/users');apiMocker.remove('POST','/users');// Keep other mocks active// apiMocker.get('/products', mockProducts); // Still mocked

3. Environment-based Control

// Use environment variables to control which endpoints are mocked (CRA / Webpack)constenv=typeofprocess!=='undefined'&&process?.env ? process.env : {};constmockConfig={users: env.REACT_APP_MOCK_USERS!=='false',products: env.REACT_APP_MOCK_PRODUCTS!=='false',orders: env.REACT_APP_MOCK_ORDERS!=='false'};if(mockConfig.users){setupUserMocks();}if(mockConfig.products){setupProductMocks();}if(mockConfig.orders){setupOrderMocks();}
// Use environment variables to control which endpoints are mocked (Vite)constenv=import.meta.env;constmockConfig={users: env.VITE_MOCK_USERS!=='false',products: env.VITE_MOCK_PRODUCTS!=='false',orders: env.VITE_MOCK_ORDERS!=='false'};if(mockConfig.users){setupUserMocks();}if(mockConfig.products){setupProductMocks();}if(mockConfig.orders){setupOrderMocks();}

API Reference

Creating a Mocker

import{ApiMocker,createMocker}from'@onamfc/api-mocker';// Using the factory function (recommended)constmocker=createMocker({baseUrl: 'https://api.example.com',globalDelay: 500,logRequests: true,globalHeaders: {'Content-Type': 'application/json','X-Custom-Header': 'value'}});// Or using the class directlyconstmocker=newApiMocker(config);

Configuration Options

interfaceMockConfig{baseUrl?: string;// Base URL to match againstglobalDelay?: number;// Default delay for all requests (ms)globalHeaders?: Record<string,string>;// Default headers for all responseslogRequests?: boolean;// Enable/disable request loggingdefaultPriority?: number;// Default priority for endpoints}

Adding Mock Endpoints

Static Responses

// Using convenience methodsmocker.get('/users',[{id: 1,name: 'John'}]).post('/users',{id: 2,name: 'Jane'}).put('/users/:id',{id: 1,name: 'Updated John'}).delete('/users/:id',{success: true});// Using the generic mock methodmocker.mock({path: '/products',method: 'GET',response: [{id: 1,name: 'Product 1'}],status: 200,delay: 1000,headers: {'X-Total-Count': '1'}});

Dynamic Responses

// Enhanced dynamic response with full contextmocker.mockDynamic({path: '/users/:id',method: 'GET',handler: (context)=>{constuserId=context.params.id;constincludeEmail=context.queryParams.include==='email';constisAdmin=context.headers['x-user-role']==='admin';return{id: userId,name: `User ${userId}`,email: includeEmail ? `user${userId}@example.com` : undefined,role: isAdmin ? 'admin' : 'user',timestamp: newDate().toISOString()};},delay: 500});// Stateful dynamic responsemocker.mockDynamic({path: '/analytics/pageview',method: 'POST',handler: (context)=>{// Initialize analytics if not existsif(!context.state.analytics){context.state.analytics={pageViews: 0,uniqueVisitors: newSet()};}const{ page, userId }=context.body;context.state.analytics.pageViews++;context.state.analytics.uniqueVisitors.add(userId);return{
page,totalViews: context.state.analytics.pageViews,uniqueVisitors: context.state.analytics.uniqueVisitors.size};}});

Managing Mocks

// Enable/disable mockingmocker.enable();mocker.disable();// Clear all mocksmocker.clear();// Remove specific mockmocker.remove('GET','/users');// State managementmocker.setState('key','value');conststate=mocker.getState();mocker.clearState();// Restore original fetchmocker.restore();// Update configurationmocker.updateConfig({logRequests: false,globalDelay: 1000});

Best Practices

  1. Organize Your Mocks: Keep mock definitions in separate files organized by feature or endpoint
  2. Use Environment Variables: Control mock behavior through environment variables
  3. Realistic Data: Use realistic mock data that matches your actual API responses
  4. Test Error Scenarios: Mock both success and error responses to test error handling
  5. Gradual Migration: Disable mocks endpoint by endpoint as real APIs become available
  6. Type Safety: Use TypeScript interfaces for your API responses
interfaceUser{id: number;name: string;email: string;}mocker.get('/users',[]asUser[]);
  1. State Management: Use stateful mocks for realistic CRUD operations
  2. Network Conditions: Use delays and network errors to test under realistic conditions

Troubleshooting

Common Issues

Mocks not working:

  • Check that mocks are initialized before your API calls
  • Verify the baseUrl matches your API calls
  • Ensure mocking is enabled (mocker.enable())

TypeScript errors:

  • Make sure you have proper type definitions for your mock data
  • Use as assertions when needed for complex types

State not persisting:

  • Remember that state is only maintained within the same session
  • Use mocker.setState() to initialize state if needed

Priority conflicts:

  • Use explicit priorities when you have overlapping endpoints
  • More specific paths automatically get higher priority

Development

npm install
npm test
npm run build

These commands run the Jest test suite (using the Node 18 runtime) and build the distributable bundles under dist/.

Contributing

Issues and pull requests are welcome! Please visit the GitHub repository.

About

A lightweight TypeScript library for mocking API endpoints during frontend development. Perfect for developing frontend applications before the backend is ready, or for testing different API scenarios.

Topics

Resources

Stars

7 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages