Free Mock API Generator Guide — Build and Test APIs Without a Backend
Mock APIs let frontend developers build and test UIs without waiting for a real backend. They simulate API responses, handle different scenarios, and speed up development. This guide covers the best free mock API tools, how to use them effectively, and patterns for realistic fake data generation with Faker.js.
Zero backend
required to start frontend development
JSON Server
most popular — full REST API from a JSON file
MSW
best for React testing with service workers
Faker.js
generate realistic fake data for any field
Why Mock APIs?
The core benefit
Mock APIs eliminate the frontend/backend dependency during development. Instead of waiting for the backend team to build endpoints, frontend devs define the expected API shape and mock it immediately. This enables parallel development, faster iteration, and reliable testing without live API dependencies.
Parallel development
Frontend and backend teams work simultaneously. Frontend mocks the agreed API contract; backend implements it. No blocking, faster delivery.
Reliable testing
Tests that hit real APIs are flaky — network issues, rate limits, changing data. Mock APIs return predictable responses every time, making CI reliable.
Edge case simulation
Easily simulate error states (500, 401, timeout), empty states, large datasets, and pagination that are hard to reproduce with real APIs.
Offline development
Work without internet or VPN. Mock APIs run locally and don't depend on network connectivity, API uptime, or authentication.
Cost savings
Avoid burning API rate limits or incurring costs for paid APIs (Stripe, SendGrid, Twilio) during development and CI/CD test runs.
Contract-first development
Define the API shape first as the mock, then both teams implement against that contract. Reduces integration surprises at merge time.
Tool Comparison
| Item | Tool | Best For |
|---|---|---|
| JSON Server | Zero-config REST API from db.json | Quick prototypes, CRUD APIs, no code required — start in 30 seconds |
| MSW (Mock Service Worker) | Intercepts real fetch/axios calls in browser | React/Vue apps, Jest/Vitest testing, Storybook component isolation |
| Mockoon | Desktop GUI mock server application | Teams that prefer GUI over code, sharing mock configs as files |
| Mirage JS | In-browser API mocking with ORM | Ember/React apps with complex relational data needs |
| Postman Mock Server | Cloud-hosted mock from Postman collection | Teams already using Postman, sharing mocks across teams via URL |
| WireMock | Java-based mock server with powerful matching | Java/JVM projects, complex request matching, fault simulation |
| Faker.js | Fake data generation library (not a server) | Seeding JSON Server, generating test fixtures, Storybook stories |
JSON Server — REST API in 30 Seconds
# Install
npm install -g json-server
# Create your mock database
cat > db.json << 'EOF'
{
"users": [
{ "id": 1, "name": "Alice Johnson", "email": "alice@example.com", "role": "admin" },
{ "id": 2, "name": "Bob Smith", "email": "bob@example.com", "role": "user" }
],
"posts": [
{ "id": 1, "title": "Hello World", "userId": 1, "published": true },
{ "id": 2, "title": "Mock APIs Rock", "userId": 2, "published": false }
],
"comments": [
{ "id": 1, "postId": 1, "body": "Great post!", "userId": 2 }
]
}
EOF
# Start server — full REST API available instantly!
json-server --watch db.json --port 3001
# Now you have:
# GET /users → list all users
# GET /users/1 → get user by id
# POST /users → create user (auto-assigns id)
# PUT /users/1 → replace user
# PATCH /users/1 → partial update
# DELETE /users/1 → delete user
# GET /posts?userId=1 → filter by field
# GET /users?_sort=name&_order=asc → sort
# GET /users?_page=1&_limit=10 → pagination
# GET /posts?_expand=user → include related user
# GET /users/1/posts → nested resources// json-server-config.js — custom routes + middleware
const jsonServer = require('json-server');
const server = jsonServer.create();
const router = jsonServer.router('db.json');
const middlewares = jsonServer.defaults();
// Add custom middleware for auth simulation
server.use((req, res, next) => {
if (req.headers.authorization !== 'Bearer mock-token') {
// Allow GET requests without auth
if (req.method !== 'GET') {
return res.status(401).json({ error: 'Unauthorized' });
}
}
next();
});
// Custom route rewrites
server.use(
jsonServer.rewriter({
'/api/*': '/$1', // /api/users → /users
'/v1/users/:id': '/users/:id',
})
);
// Simulate delay for all requests (realistic latency)
server.use((req, res, next) => {
setTimeout(next, 300 + Math.random() * 200); // 300-500ms delay
});
server.use(middlewares);
server.use(router);
server.listen(3001, () => console.log('Mock API running on port 3001'));
// Run with: node json-server-config.jsMSW — Mock Service Worker for React
// src/mocks/handlers.ts
import { http, HttpResponse, delay } from 'msw';
export const handlers = [
// Mock GET /api/users
http.get('/api/users', async () => {
await delay(200); // realistic latency
return HttpResponse.json([
{ id: 1, name: 'Alice', email: 'alice@example.com', role: 'admin' },
{ id: 2, name: 'Bob', email: 'bob@example.com', role: 'user' },
]);
}),
// Mock POST /api/users with request body access
http.post('/api/users', async ({ request }) => {
const body = await request.json() as { name: string; email: string };
return HttpResponse.json(
{ id: Math.floor(Math.random() * 1000), ...body, role: 'user' },
{ status: 201 }
);
}),
// Simulate 404 for specific IDs
http.get('/api/users/:id', ({ params }) => {
if (params.id === '999') {
return HttpResponse.json({ error: 'User not found' }, { status: 404 });
}
return HttpResponse.json({ id: params.id, name: 'Test User', role: 'user' });
}),
// Simulate server error
http.delete('/api/users/:id', ({ params }) => {
if (params.id === '1') {
return HttpResponse.json({ error: 'Cannot delete admin' }, { status: 403 });
}
return new HttpResponse(null, { status: 204 });
}),
];
// src/mocks/browser.ts (for development)
import { setupWorker } from 'msw/browser';
import { handlers } from './handlers';
export const worker = setupWorker(...handlers);
// src/main.tsx — only activate in development
if (process.env.NODE_ENV === 'development') {
const { worker } = await import('./mocks/browser');
await worker.start({ onUnhandledRequest: 'warn' });
}
// In Jest tests (setupTests.ts)
import { setupServer } from 'msw/node';
import { handlers } from './mocks/handlers';
const server = setupServer(...handlers);
beforeAll(() => server.listen({ onUnhandledRequest: 'error' }));
afterEach(() => server.resetHandlers());
afterAll(() => server.close());
// Override handlers per test for specific scenarios:
test('handles 500 error', () => {
server.use(
http.get('/api/users', () =>
HttpResponse.json({ error: 'Server Error' }, { status: 500 })
)
);
// ... test error handling
});Generating Realistic Fake Data with Faker.js
import { faker } from '@faker-js/faker';
// Generate realistic user data
function generateUser() {
return {
id: faker.string.uuid(),
name: faker.person.fullName(),
email: faker.internet.email(),
phone: faker.phone.number(),
avatar: faker.image.avatar(),
username: faker.internet.username(),
address: {
street: faker.location.streetAddress(),
city: faker.location.city(),
state: faker.location.state(),
country: faker.location.country(),
zipCode: faker.location.zipCode(),
},
company: faker.company.name(),
jobTitle: faker.person.jobTitle(),
createdAt: faker.date.past({ years: 2 }).toISOString(),
isActive: faker.datatype.boolean({ probability: 0.85 }),
};
}
// Generate realistic product data
function generateProduct() {
return {
id: faker.string.uuid(),
name: faker.commerce.productName(),
price: parseFloat(faker.commerce.price({ min: 10, max: 500 })),
description: faker.commerce.productDescription(),
category: faker.commerce.department(),
sku: faker.string.alphanumeric(8).toUpperCase(),
inStock: faker.datatype.boolean({ probability: 0.9 }),
rating: parseFloat((Math.random() * 2 + 3).toFixed(1)), // 3.0–5.0
reviewCount: faker.number.int({ min: 0, max: 2000 }),
images: Array.from({ length: 3 }, () => faker.image.url()),
};
}
// Seed for reproducible data (same seed = same data across runs)
faker.seed(12345);
const reproducibleUser = generateUser(); // Always the same user
// Generate bulk data for db.json seeding
const dbSeed = {
users: Array.from({ length: 50 }, generateUser),
products: Array.from({ length: 200 }, generateProduct),
};
// Write to db.json for JSON Server
import fs from 'fs';
fs.writeFileSync('db.json', JSON.stringify(dbSeed, null, 2));
console.log('Seeded db.json with', dbSeed.users.length, 'users and', dbSeed.products.length, 'products');Setting Up a Complete Mock API Workflow
Define your API contract
Before writing any mock code, document the endpoint paths, request/response shapes, and error codes in a shared spec (OpenAPI/Swagger or a simple TypeScript interface file). This becomes the source of truth for both the mock and the real implementation.
Generate seed data with Faker.js
Create a seed script that generates realistic data: 50+ users, 200+ products, with real-looking names, emails, and prices. Run it to populate db.json. Realistic data surfaces UI bugs that placeholder text hides (long names, special characters, edge cases).
Start JSON Server for rapid prototyping
Run json-server --watch db.json --port 3001 during development. Your entire React/Vue/Angular app can immediately CRUD data. No backend required. Add a custom middleware file for auth simulation and artificial delays.
Add MSW for component and integration tests
Set up MSW handlers that mirror your JSON Server routes. In tests, override specific handlers to simulate errors, empty states, and edge cases. This gives you test coverage of every UI state without hitting a real network.
Simulate all error states
Explicitly test: 401 (logged out), 403 (no permission), 404 (not found), 422 (validation error), 500 (server down), network timeout. Use MSW's server.use() in individual tests to inject these states. Your error UI is only as good as your error testing.
Transition to real API
When the real backend is ready, remove the MSW handlers and JSON Server. Since you used real endpoint paths throughout, your app transitions with zero code changes. Run the same test suite against the staging API to catch contract mismatches early.
Use Unblockdevs Mock API Generator