How to Generate and Test API Mock Responses for Development
This guide has a free tool → Open Dummy Data Generator
Why Mock API Responses?
Frontend and backend development rarely happen at the same pace. The UI team needs data to build with, but the API endpoints are not ready. The API is ready, but the test environment is unstable. The real API costs money per call and you do not want to consume your quota during development.
Mock API responses solve all of these problems by providing controlled, predictable data that matches the expected API contract.
Common scenarios where mock responses are essential:
- Frontend development before the backend is ready - build and test UI components without waiting for API completion
- Unit and integration testing - deterministic test data instead of hitting real, stateful APIs that can change or be unavailable
- Prototyping and demos - show realistic data to stakeholders without a working backend or live credentials
- API documentation - include example responses that developers can copy into their code
- Offline development - work on a plane or in an area with no internet
- Edge case simulation - test how your UI handles empty states, null values, very long strings, or error responses - scenarios that are hard to reproduce with real data
- Performance testing - mock a slow API response to test loading states and timeouts
- Security testing - mock malicious payloads to test input validation on the frontend
Dummy Data Generator
Free online dummy data generator - generate realistic fake data for testing in JSON or CSV format
API Mock Response Generator
Free online API mock response generator - generate mock API responses for testing
HTTP Request Builder
Free online HTTP request builder - build and test HTTP requests with custom headers and body
The Economics of Mocking
Relying on real APIs during development is expensive in several ways:
Rate limits: Many APIs limit calls per minute or per day. Running tests against a real API exhausts your quota quickly, and hitting rate limits mid-test causes false failures.
Flaky tests: Real APIs go down, have timeouts, or return slightly different data as their database state changes. Tests that depend on real APIs are inherently unreliable - they fail for reasons unrelated to your code.
Cost: Paid APIs like OpenAI, Stripe, or Twilio charge per request. Running thousands of tests against production APIs adds up.
Latency: Real API calls add network round-trip time to every test. A test suite that takes 2 seconds with mocks might take 30 seconds with real API calls - slowing down your development cycle significantly.
Good mocking eliminates all four problems simultaneously.
What Makes a Good Mock Response?
Realistic Data
Mock data should look like real data. Names should be real names, emails should follow proper format, dates should be valid ISO timestamps. Unrealistic mock data causes bugs because your code may handle it differently than it handles real data.
// Bad mock data - will not reveal real formatting bugs
{
"name": "test",
"email": "test@test.com",
"created_at": "2025-01-01",
"phone": "1234567890"
}
// Good mock data - exposes real formatting and display issues
{
"name": "Sarah Chen",
"email": "sarah.chen@example.com",
"created_at": "2026-02-15T14:30:00Z",
"phone": "+1-555-234-5678"
}The bad mock data might hide a phone number formatting bug that only appears when you have a real phone number with hyphens and country code prefix.
Correct Data Types
If the real API returns numbers, your mock must return numbers - not strings that look like numbers. Type mismatches between mock and real data cause bugs that only surface when your code goes to production.
// Wrong types - will hide type-related bugs
{
"price": "29.99",
"in_stock": "true",
"quantity": "5",
"rating": "4.2"
}
// Correct types - matches what a real API returns
{
"price": 29.99,
"in_stock": true,
"quantity": 5,
"rating": 4.2
}A component that formats price as $${item.price.toFixed(2)} will throw TypeError: item.price.toFixed is not a function when price is a string. With correct mock types, this bug appears immediately during development.
Realistic Variation
Do not use the same data for every record in a list. Realistic variation catches layout bugs that only appear with different content lengths:
{
"users": [
{
"id": 1,
"name": "Al",
"email": "al@example.com",
"bio": null,
"avatar": "https://example.com/avatars/1.jpg"
},
{
"id": 2,
"name": "Maximilian von Richthoven-Strauss",
"email": "maximilian.von.richthoven.strauss@very-long-company-name.example.com",
"bio": "Senior software engineer with 15 years of experience in distributed systems.",
"avatar": null
},
{
"id": 3,
"name": "Zoe K.",
"email": "zoe@example.com",
"bio": "UX designer.",
"avatar": "https://example.com/avatars/3.jpg"
}
]
}This mock reveals: short name rendering, very long name overflow handling, null avatar fallback, null bio handling, long email truncation, and varying bio length.
Edge Cases
Good mocks explicitly test edge cases that your UI needs to handle:
// Empty state
{ "orders": [], "total": 0, "pages": 0 }
// Single result
{ "orders": [{ "id": 1, "total": 19.99 }], "total": 1, "pages": 1 }
// Null optional fields
{ "name": "Alice", "avatar": null, "bio": null, "website": null }
// Very long content
{ "title": "This is an extremely long product title that should be truncated in the card component but wrapped in the full detail view based on your responsive design specifications" }
// Zero values (test zero-state rendering)
{ "revenue": 0, "orders": 0, "conversion_rate": 0.0 }
// Maximum values
{ "quantity": 9999, "price": 99999.99, "reviews": 100000 }
// Unicode and international content
{ "name": "José García-López", "city": "São Paulo", "company": "Müller und Söhne GmbH" }Common Response Patterns
Paginated List
The most common API response pattern. Always include pagination metadata:
{
"data": [
{ "id": 1, "name": "Widget A", "price": 9.99, "in_stock": true },
{ "id": 2, "name": "Widget B", "price": 14.99, "in_stock": false },
{ "id": 3, "name": "Widget C", "price": 24.99, "in_stock": true }
],
"pagination": {
"page": 1,
"per_page": 10,
"total": 47,
"total_pages": 5,
"has_next": true,
"has_prev": false,
"next_cursor": "cursor_abc123"
},
"meta": {
"request_id": "req_a1b2c3d4",
"timestamp": "2026-03-02T10:00:00Z"
}
}Validation Error Response
Frontend code needs to handle validation errors and display them next to the right fields:
{
"error": {
"type": "validation_error",
"code": "VALIDATION_FAILED",
"message": "The request body contains invalid data.",
"details": [
{
"field": "email",
"code": "INVALID_FORMAT",
"message": "Must be a valid email address"
},
{
"field": "age",
"code": "OUT_OF_RANGE",
"message": "Must be between 13 and 120"
},
{
"field": "username",
"code": "ALREADY_EXISTS",
"message": "This username is already taken"
}
]
}
}Authentication Error
{
"error": {
"type": "authentication_error",
"code": "TOKEN_EXPIRED",
"message": "Your access token has expired. Please sign in again.",
"retry_after": null
}
}Rate Limit Error
{
"error": {
"type": "rate_limit_error",
"code": "RATE_LIMIT_EXCEEDED",
"message": "Too many requests. Please wait before retrying.",
"retry_after": 60,
"rate_limit": {
"limit": 100,
"remaining": 0,
"reset_at": "2026-03-02T10:05:00Z"
}
}
}Authenticated User Profile
{
"user": {
"id": "usr_a1b2c3d4",
"name": "Alex Rivera",
"email": "alex@example.com",
"avatar_url": "https://example.com/avatars/alex.jpg",
"role": "admin",
"permissions": ["read", "write", "delete", "manage_users"],
"subscription": {
"plan": "pro",
"status": "active",
"current_period_end": "2026-04-02T00:00:00Z"
},
"preferences": {
"theme": "dark",
"timezone": "America/New_York",
"notifications_email": true,
"notifications_push": false
},
"created_at": "2025-06-15T09:00:00Z",
"last_login": "2026-03-02T08:42:00Z"
}
}Nested Resource
{
"order": {
"id": "ord_x9y8z7",
"status": "shipped",
"total": 89.97,
"currency": "USD",
"items": [
{
"id": "item_001",
"product": {
"id": "prod_1",
"name": "Wireless Mouse",
"sku": "WM-2026-BLK",
"image_url": "https://example.com/products/mouse.jpg"
},
"quantity": 1,
"unit_price": 29.99,
"subtotal": 29.99
},
{
"id": "item_002",
"product": {
"id": "prod_2",
"name": "USB-C Hub",
"sku": "HUB-7PORT-GRY",
"image_url": "https://example.com/products/hub.jpg"
},
"quantity": 2,
"unit_price": 29.99,
"subtotal": 59.98
}
],
"shipping": {
"method": "express",
"carrier": "UPS",
"tracking_number": "1Z999AA10123456784",
"estimated_delivery": "2026-03-04",
"address": {
"line1": "123 Main St",
"line2": "Apt 4B",
"city": "New York",
"state": "NY",
"postal_code": "10001",
"country": "US"
}
},
"payment": {
"method": "card",
"last4": "4242",
"brand": "visa"
},
"created_at": "2026-03-01T14:30:00Z",
"updated_at": "2026-03-02T09:15:00Z"
}
}Analytics / Dashboard Data
{
"period": {
"start": "2026-02-01T00:00:00Z",
"end": "2026-02-28T23:59:59Z",
"granularity": "day"
},
"summary": {
"revenue": 48750.00,
"orders": 342,
"avg_order_value": 142.54,
"new_customers": 89,
"returning_customers": 253
},
"series": [
{ "date": "2026-02-01", "revenue": 1820.00, "orders": 12 },
{ "date": "2026-02-02", "revenue": 2150.00, "orders": 15 },
{ "date": "2026-02-03", "revenue": 1640.00, "orders": 11 }
],
"top_products": [
{ "id": "prod_1", "name": "Wireless Mouse", "revenue": 8997.00, "units": 300 },
{ "id": "prod_2", "name": "USB-C Hub", "revenue": 6747.75, "units": 225 }
]
}Strategies for Using Mock Responses in Development
Strategy 1: Inline Mock Data
The simplest approach - define constants in your code and swap them with real API calls using an environment variable:
// mocks/users.js
export const MOCK_USERS = [
{ id: 1, name: "Alice Chen", email: "alice@example.com", role: "admin" },
{ id: 2, name: "Bob Martinez", email: "bob@example.com", role: "editor" },
{ id: 3, name: "Carol Wu", email: "carol@example.com", role: "viewer" },
];
export const MOCK_EMPTY_USERS = { users: [], total: 0, page: 1 };
// services/users.js
import { MOCK_USERS } from '../mocks/users';
export async function getUsers() {
if (process.env.USE_MOCKS === 'true') {
// Simulate realistic network delay
await new Promise(r => setTimeout(r, 300));
return { data: MOCK_USERS, pagination: { total: 3, page: 1, pages: 1 } };
}
const res = await fetch('/api/users');
if (!res.ok) throw new Error('Failed to fetch users');
return res.json();
}Strategy 2: Mock API Routes (Next.js)
Create dedicated API routes that return mock data:
// app/api/mock/users/route.ts
export async function GET(request: Request) {
// Simulate API latency
await new Promise(r => setTimeout(r, 200));
return Response.json({
data: [
{ id: 1, name: "Alice Chen", email: "alice@example.com", role: "admin" },
{ id: 2, name: "Bob Martinez", email: "bob@example.com", role: "editor" },
],
pagination: {
page: 1,
per_page: 10,
total: 2,
total_pages: 1,
},
});
}
// app/api/mock/users/[id]/route.ts
export async function GET(
request: Request,
{ params }: { params: { id: string } }
) {
// Simulate 404 for unknown IDs
if (params.id === '999') {
return Response.json(
{ error: { code: 'NOT_FOUND', message: 'User not found' } },
{ status: 404 }
);
}
return Response.json({
user: {
id: parseInt(params.id),
name: "Alice Chen",
email: "alice@example.com",
}
});
}Your frontend code calls /api/mock/users during development and swaps to the real endpoint in production.
Strategy 3: Mock Service Worker (MSW)
MSW intercepts actual network requests at the service worker level - meaning your application code makes real fetch calls, but MSW intercepts them before they hit the network and returns mock responses instead. No changes to application code needed.
npm install msw --save-dev
npx msw init public/ --saveDefine handlers:
// src/mocks/handlers.js
import { http, HttpResponse } from 'msw';
export const handlers = [
// GET /api/users
http.get('/api/users', ({ request }) => {
const url = new URL(request.url);
const page = parseInt(url.searchParams.get('page') ?? '1');
const limit = parseInt(url.searchParams.get('limit') ?? '10');
return HttpResponse.json({
data: [
{ id: 1, name: "Alice Chen", email: "alice@example.com" },
{ id: 2, name: "Bob Martinez", email: "bob@example.com" },
],
pagination: { page, per_page: limit, total: 2 },
});
}),
// POST /api/users
http.post('/api/users', async ({ request }) => {
const body = await request.json();
// Simulate validation
if (!body.email || !body.email.includes('@')) {
return HttpResponse.json(
{
error: {
code: 'VALIDATION_FAILED',
details: [{ field: 'email', message: 'Must be a valid email' }],
},
},
{ status: 422 }
);
}
return HttpResponse.json(
{ user: { id: 99, ...body, created_at: new Date().toISOString() } },
{ status: 201 }
);
}),
// Simulate rate limiting
http.get('/api/expensive-endpoint', () => {
return HttpResponse.json(
{
error: { code: 'RATE_LIMIT_EXCEEDED', message: 'Too many requests' },
},
{ status: 429, headers: { 'Retry-After': '60' } }
);
}),
];Browser setup:
// src/mocks/browser.js
import { setupWorker } from 'msw/browser';
import { handlers } from './handlers';
export const worker = setupWorker(...handlers);
// src/main.jsx
async function enableMocking() {
if (process.env.NODE_ENV !== 'development') return;
const { worker } = await import('./mocks/browser');
await worker.start({
onUnhandledRequest: 'bypass', // Let unhandled requests through
});
}
enableMocking().then(() => {
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
});MSW also works in Node.js for testing with Jest or Vitest:
// src/mocks/node.js
import { setupServer } from 'msw/node';
import { handlers } from './handlers';
export const server = setupServer(...handlers);
// In your test setup file
beforeAll(() => server.listen({ onUnhandledRequest: 'error' }));
afterEach(() => server.resetHandlers());
afterAll(() => server.close());
// Override handlers in individual tests
test('handles server errors gracefully', () => {
server.use(
http.get('/api/users', () => {
return HttpResponse.json(
{ error: { message: 'Internal server error' } },
{ status: 500 }
);
})
);
// ... test that your UI shows an error state
});Strategy 4: JSON Server for a Full REST Mock API
json-server creates a full REST API from a JSON file with zero configuration:
npm install -g json-serverCreate db.json:
{
"users": [
{ "id": 1, "name": "Alice Chen", "email": "alice@example.com", "role": "admin" },
{ "id": 2, "name": "Bob Martinez", "email": "bob@example.com", "role": "editor" }
],
"products": [
{ "id": 1, "name": "Wireless Mouse", "price": 29.99, "category_id": 1 },
{ "id": 2, "name": "USB-C Hub", "price": 45.00, "category_id": 1 }
],
"orders": [
{ "id": 1, "user_id": 1, "total": 74.99, "status": "completed" }
]
}Start the server:
json-server --watch db.json --port 3001You now have a full REST API at http://localhost:3001 with:
GET /users- list all users (with filtering, sorting, pagination)GET /users/1- get user by IDPOST /users- create userPUT /users/1- replace userPATCH /users/1- update userDELETE /users/1- delete user
JSON Server supports filtering (/users?role=admin), sorting (/users?_sort=name), and pagination (/users?_page=1&_limit=10) automatically.
Testing Error States
One major advantage of mocking is the ability to reliably test error states that are rare or hard to trigger with a real API.
Network Failure
// MSW handler that simulates network error
http.get('/api/users', () => {
return HttpResponse.error(); // Simulates network failure
});Timeout Simulation
// Simulate a slow response
http.get('/api/heavy-report', async () => {
// Wait 5 seconds, then respond
await new Promise(r => setTimeout(r, 5000));
return HttpResponse.json({ data: 'slow result' });
});Partial Failure
let callCount = 0;
// Fail the first two times, succeed on the third
http.get('/api/flaky-service', () => {
callCount++;
if (callCount <= 2) {
return HttpResponse.json(
{ error: { message: 'Service temporarily unavailable' } },
{ status: 503 }
);
}
return HttpResponse.json({ data: 'success' });
});Generating Mock Data Programmatically
For testing components that display lists, you need multiple records. Generating them manually is tedious. Use Faker.js or similar libraries:
import { faker } from '@faker-js/faker';
function generateUser() {
return {
id: faker.string.uuid(),
name: faker.person.fullName(),
email: faker.internet.email(),
avatar: faker.image.avatar(),
role: faker.helpers.arrayElement(['admin', 'editor', 'viewer']),
created_at: faker.date.past({ years: 2 }).toISOString(),
last_login: faker.date.recent({ days: 30 }).toISOString(),
bio: faker.datatype.boolean(0.7) ? faker.lorem.sentence() : null,
};
}
function generateUsers(count = 10) {
return Array.from({ length: count }, generateUser);
}
// Generate 50 users for pagination testing
const mockUsers = generateUsers(50);For generating mock data without writing code, the Dummy Data Generator creates realistic records with names, emails, addresses, phone numbers, dates, and custom fields.
Validating Mock Responses Against a Schema
Mocks drift from the real API over time. The real API adds a field - the mock does not. You ship code that handles that new field - and it silently breaks when hitting the mock because the field is missing.
JSON Schema validation catches this automatically:
import Ajv from 'ajv';
import addFormats from 'ajv-formats';
const ajv = new Ajv();
addFormats(ajv);
const userSchema = {
type: 'object',
required: ['id', 'name', 'email', 'role', 'created_at'],
properties: {
id: { type: 'string', format: 'uuid' },
name: { type: 'string', minLength: 1 },
email: { type: 'string', format: 'email' },
role: { type: 'string', enum: ['admin', 'editor', 'viewer'] },
avatar: { type: ['string', 'null'], format: 'uri' },
created_at: { type: 'string', format: 'date-time' },
},
additionalProperties: false,
};
const validate = ajv.compile(userSchema);
// Validate your mock data
const mockUser = generateUser();
const valid = validate(mockUser);
if (!valid) {
console.error('Mock user does not match schema:', validate.errors);
}Keeping Mocks Synchronized With the API
The biggest risk of mocking is divergence - your mocks stop reflecting reality and your tests pass even though the real integration is broken.
Strategies to prevent mock drift:
Contract testing with Pact: Pact records the interactions between consumer (frontend) and provider (backend) and verifies that the real API still satisfies those contracts. If the backend changes an API response in a way that breaks the frontend contract, Pact catches it.
OpenAPI-driven mocks: Generate mocks automatically from your OpenAPI specification. When the spec changes, the mocks regenerate:
# Generate a mock server from OpenAPI spec
npx @stoplight/prism-cli mock openapi.yamlShared contract files: Store your response schemas in a shared location (a separate package or a shared repo directory) that both the frontend and backend reference. When the backend changes the response format, it updates the schema, which immediately surfaces failures in the frontend tests.
Periodic smoke tests against real API: Run a subset of your test suite against the real API on a schedule (nightly, or on every release). This catches divergence before it causes production issues.
Generate Mock Responses With ToolBox
The API Mock Response Generator helps you create realistic mock responses quickly without writing mock data by hand:
- Define your response structure or describe what you need
- Get a realistic mock response with correct types and realistic values
- Copy the JSON directly into your mock files, test fixtures, or API documentation
For bulk mock data - hundreds of user records, products, orders - the Dummy Data Generator creates realistic datasets with names, emails, addresses, phone numbers, and custom fields. Both tools run entirely in your browser - no data is sent to any server.
Other tools that pair well with API development:
- HTTP Request Builder - test your real API endpoints directly in the browser
- JSON Formatter - validate and format JSON request and response payloads
- JWT Decoder - inspect the claims and expiry of authentication tokens
- Webhook Tester - receive and inspect incoming webhook payloads
Try It Free
Generate API mock responses or generate realistic dummy data instantly in your browser. Free, private, no signup required.
Building a full API integration? Use the HTTP Request Builder to test your real endpoints. Need to validate JSON response structure? The JSON Formatter catches malformed JSON before it reaches your code.
Related Tools
Free, private, no signup required
Webhook Tester
Webhook tester online - generate temporary webhook URLs and inspect incoming HTTP requests for free
Regex Tester
Free online regex tester - test and debug regular expressions with live matching and highlights
Text Diff Checker
Free online text diff checker - compare two texts and see the differences highlighted line by line
Code Formatter
Free online code formatter - beautify and format JavaScript, CSS, HTML, and more
You might also like
Want higher limits, batch processing, and AI tools?