← Back to list

api-mock-server
by patricio0312rev
Comprehensive library of +100 production-ready development skills covering every aspect of modern software engineering. From project setup to production deployment, from security hardening to performance optimization.
⭐ 6🍴 0📅 Jan 19, 2026
SKILL.md
name: api-mock-server description: Creates mock API servers for testing and development with MSW, json-server, or custom handlers. Use when users request "API mocking", "mock server", "MSW setup", "test fixtures", or "mock API responses".
API Mock Server
Create realistic mock APIs for testing and development.
Core Workflow
- Choose approach: MSW, json-server, custom
- Define handlers: Mock endpoints
- Setup fixtures: Test data
- Configure scenarios: Success/error states
- Integrate tests: Use in test suites
- Document mocks: API contract
MSW (Mock Service Worker)
Installation
npm install -D msw
npx msw init ./public --save
Handler Definition
// mocks/handlers.ts
import { http, HttpResponse, delay } from 'msw';
// Types
interface User {
id: string;
name: string;
email: string;
role: 'admin' | 'user';
}
interface CreateUserInput {
name: string;
email: string;
}
// Fixtures
const users: User[] = [
{ id: '1', name: 'John Doe', email: 'john@example.com', role: 'admin' },
{ id: '2', name: 'Jane Smith', email: 'jane@example.com', role: 'user' },
];
// Handlers
export const handlers = [
// GET /api/users
http.get('/api/users', async () => {
await delay(100);
return HttpResponse.json(users);
}),
// GET /api/users/:id
http.get('/api/users/:id', async ({ params }) => {
await delay(50);
const user = users.find((u) => u.id === params.id);
if (!user) {
return new HttpResponse(null, { status: 404 });
}
return HttpResponse.json(user);
}),
// POST /api/users
http.post('/api/users', async ({ request }) => {
await delay(100);
const body = (await request.json()) as CreateUserInput;
const newUser: User = {
id: String(users.length + 1),
name: body.name,
email: body.email,
role: 'user',
};
users.push(newUser);
return HttpResponse.json(newUser, { status: 201 });
}),
// PUT /api/users/:id
http.put('/api/users/:id', async ({ params, request }) => {
await delay(50);
const body = (await request.json()) as Partial<User>;
const index = users.findIndex((u) => u.id === params.id);
if (index === -1) {
return new HttpResponse(null, { status: 404 });
}
users[index] = { ...users[index], ...body };
return HttpResponse.json(users[index]);
}),
// DELETE /api/users/:id
http.delete('/api/users/:id', async ({ params }) => {
await delay(50);
const index = users.findIndex((u) => u.id === params.id);
if (index === -1) {
return new HttpResponse(null, { status: 404 });
}
users.splice(index, 1);
return new HttpResponse(null, { status: 204 });
}),
];
Setup for Browser
// mocks/browser.ts
import { setupWorker } from 'msw/browser';
import { handlers } from './handlers';
export const worker = setupWorker(...handlers);
// main.tsx or app entry
async function enableMocking() {
if (process.env.NODE_ENV === 'development') {
const { worker } = await import('./mocks/browser');
return worker.start({
onUnhandledRequest: 'bypass',
});
}
}
enableMocking().then(() => {
// Render app
});
Setup for Node (Tests)
// mocks/server.ts
import { setupServer } from 'msw/node';
import { handlers } from './handlers';
export const server = setupServer(...handlers);
// tests/setup.ts (Jest or Vitest)
import { beforeAll, afterEach, afterAll } from 'vitest';
import { server } from '../mocks/server';
beforeAll(() => server.listen({ onUnhandledRequest: 'error' }));
afterEach(() => server.resetHandlers());
afterAll(() => server.close());
Test-Specific Handlers
// tests/users.test.ts
import { http, HttpResponse } from 'msw';
import { server } from '../mocks/server';
import { render, screen, waitFor } from '@testing-library/react';
import { UserList } from '../components/UserList';
describe('UserList', () => {
it('displays users', async () => {
render(<UserList />);
await waitFor(() => {
expect(screen.getByText('John Doe')).toBeInTheDocument();
});
});
it('handles empty state', async () => {
server.use(
http.get('/api/users', () => {
return HttpResponse.json([]);
})
);
render(<UserList />);
await waitFor(() => {
expect(screen.getByText('No users found')).toBeInTheDocument();
});
});
it('handles server error', async () => {
server.use(
http.get('/api/users', () => {
return new HttpResponse(null, { status: 500 });
})
);
render(<UserList />);
await waitFor(() => {
expect(screen.getByText('Error loading users')).toBeInTheDocument();
});
});
it('handles network error', async () => {
server.use(
http.get('/api/users', () => {
return HttpResponse.error();
})
);
render(<UserList />);
await waitFor(() => {
expect(screen.getByText('Network error')).toBeInTheDocument();
});
});
});
GraphQL Mocking
// mocks/graphql-handlers.ts
import { graphql, HttpResponse } from 'msw';
export const graphqlHandlers = [
graphql.query('GetUsers', () => {
return HttpResponse.json({
data: {
users: [
{ id: '1', name: 'John', email: 'john@example.com' },
{ id: '2', name: 'Jane', email: 'jane@example.com' },
],
},
});
}),
graphql.mutation('CreateUser', ({ variables }) => {
return HttpResponse.json({
data: {
createUser: {
id: '3',
name: variables.input.name,
email: variables.input.email,
},
},
});
}),
graphql.query('GetUser', ({ variables }) => {
if (variables.id === 'not-found') {
return HttpResponse.json({
data: { user: null },
errors: [{ message: 'User not found' }],
});
}
return HttpResponse.json({
data: {
user: { id: variables.id, name: 'John', email: 'john@example.com' },
},
});
}),
];
Json-Server
Configuration
// db.json
{
"users": [
{ "id": "1", "name": "John Doe", "email": "john@example.com" },
{ "id": "2", "name": "Jane Smith", "email": "jane@example.com" }
],
"posts": [
{ "id": "1", "title": "Hello World", "authorId": "1" },
{ "id": "2", "title": "Another Post", "authorId": "2" }
],
"comments": [
{ "id": "1", "text": "Great post!", "postId": "1" }
]
}
// json-server.config.js
module.exports = {
port: 3001,
watch: true,
delay: 100,
routes: 'routes.json',
};
// routes.json
{
"/api/*": "/$1",
"/users/:id/posts": "/posts?authorId=:id"
}
Custom Routes
// server.js
const jsonServer = require('json-server');
const server = jsonServer.create();
const router = jsonServer.router('db.json');
const middlewares = jsonServer.defaults();
// Custom middleware for auth
server.use((req, res, next) => {
if (req.headers.authorization !== 'Bearer valid-token') {
if (req.method !== 'GET') {
return res.status(401).json({ error: 'Unauthorized' });
}
}
next();
});
server.use(middlewares);
// Custom routes
server.get('/api/me', (req, res) => {
res.json({ id: '1', name: 'Current User', email: 'me@example.com' });
});
server.post('/api/login', (req, res) => {
const { email, password } = req.body;
if (email === 'test@example.com' && password === 'password') {
res.json({ token: 'valid-token', user: { id: '1', email } });
} else {
res.status(401).json({ error: 'Invalid credentials' });
}
});
server.use('/api', router);
server.listen(3001, () => {
console.log('Mock API running on http://localhost:3001');
});
Fixture Factories
// mocks/factories/user.ts
import { faker } from '@faker-js/faker';
interface User {
id: string;
name: string;
email: string;
role: 'admin' | 'user';
createdAt: string;
}
export function createUser(overrides: Partial<User> = {}): User {
return {
id: faker.string.uuid(),
name: faker.person.fullName(),
email: faker.internet.email(),
role: 'user',
createdAt: faker.date.past().toISOString(),
...overrides,
};
}
export function createUsers(count: number, overrides: Partial<User> = {}): User[] {
return Array.from({ length: count }, () => createUser(overrides));
}
// mocks/factories/index.ts
export * from './user';
export * from './post';
export * from './comment';
Using Factories in Tests
// tests/components/UserProfile.test.tsx
import { createUser } from '../mocks/factories';
import { server } from '../mocks/server';
import { http, HttpResponse } from 'msw';
describe('UserProfile', () => {
it('displays admin badge for admin users', async () => {
const adminUser = createUser({ role: 'admin', name: 'Admin User' });
server.use(
http.get('/api/users/:id', () => {
return HttpResponse.json(adminUser);
})
);
render(<UserProfile userId={adminUser.id} />);
await waitFor(() => {
expect(screen.getByText('Admin User')).toBeInTheDocument();
expect(screen.getByTestId('admin-badge')).toBeInTheDocument();
});
});
});
Scenario-Based Mocking
// mocks/scenarios.ts
import { http, HttpResponse, delay } from 'msw';
import { createUser, createUsers } from './factories';
export const scenarios = {
default: [],
emptyState: [
http.get('/api/users', () => HttpResponse.json([])),
http.get('/api/posts', () => HttpResponse.json([])),
],
loadingState: [
http.get('/api/users', async () => {
await delay('infinite');
return HttpResponse.json([]);
}),
],
errorState: [
http.get('/api/users', () => {
return new HttpResponse(null, { status: 500 });
}),
],
largeDataset: [
http.get('/api/users', () => {
return HttpResponse.json(createUsers(1000));
}),
],
slowNetwork: [
http.get('/api/*', async ({ request }) => {
await delay(2000);
// Continue to default handler
return undefined;
}),
],
};
// Usage in tests
describe('UserList scenarios', () => {
it('empty state', async () => {
server.use(...scenarios.emptyState);
// Test empty state
});
it('error state', async () => {
server.use(...scenarios.errorState);
// Test error handling
});
});
// Usage in Storybook
export const EmptyState: Story = {
parameters: {
msw: {
handlers: scenarios.emptyState,
},
},
};
Playwright Integration
// tests/e2e/api-mocking.spec.ts
import { test, expect } from '@playwright/test';
test.describe('API Mocking', () => {
test('mock successful response', async ({ page }) => {
await page.route('/api/users', async (route) => {
await route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify([
{ id: '1', name: 'Mocked User', email: 'mock@example.com' },
]),
});
});
await page.goto('/users');
await expect(page.getByText('Mocked User')).toBeVisible();
});
test('mock error response', async ({ page }) => {
await page.route('/api/users', async (route) => {
await route.fulfill({
status: 500,
contentType: 'application/json',
body: JSON.stringify({ error: 'Server Error' }),
});
});
await page.goto('/users');
await expect(page.getByText('Error loading users')).toBeVisible();
});
test('delay response', async ({ page }) => {
await page.route('/api/users', async (route) => {
await new Promise((resolve) => setTimeout(resolve, 3000));
await route.fulfill({
status: 200,
body: JSON.stringify([]),
});
});
await page.goto('/users');
await expect(page.getByTestId('loading-spinner')).toBeVisible();
});
});
Best Practices
- Use factories: Generate realistic data
- Define scenarios: Reusable mock configurations
- Test edge cases: Errors, empty states, loading
- Keep handlers simple: One responsibility each
- Match production API: Same contracts
- Delay appropriately: Realistic timing
- Document mocks: API contracts
- Reset between tests: Clean state
Output Checklist
Every API mock setup should include:
- MSW/json-server configuration
- Handler definitions
- Test setup integration
- Fixture factories
- Error scenarios
- Loading states
- GraphQL support (if needed)
- Browser integration
- E2E test mocking
- Documentation
Score
Total Score
70/100
Based on repository quality metrics
✓SKILL.md
SKILL.mdファイルが含まれている
+20
✓LICENSE
ライセンスが設定されている
+10
✓説明文
100文字以上の説明がある
+10
○人気
GitHub Stars 100以上
0/15
✓最近の活動
1ヶ月以内に更新
+10
○フォーク
10回以上フォークされている
0/5
✓Issue管理
オープンIssueが50未満
+5
○言語
プログラミング言語が設定されている
0/5
✓タグ
1つ以上のタグが設定されている
+5
Reviews
💬
Reviews coming soon


