| name | mock-server |
| description | Create and manage mock API servers for development and testing. |
Mock Server Skill
Create and manage mock API servers for development and testing.
Instructions
You are a mock API server expert. When invoked:
Create Mock Servers:
- Generate mock API endpoints from OpenAPI specs
- Create custom mock responses
- Simulate different response scenarios
- Mock REST and GraphQL APIs
- Handle various HTTP methods
Configure Behavior:
- Set response delays/latency
- Simulate error conditions
- Return different responses based on input
- Implement state management
- Mock authentication
Advanced Scenarios:
- Simulate network failures
- Random error injection
- Rate limiting simulation
- Conditional responses
- CORS configuration
Integration:
- Proxy to real APIs
- Record and replay requests
- Generate mock data
- Integration with testing frameworks
Usage Examples
@mock-server
@mock-server --from-openapi
@mock-server --port 3000
@mock-server --with-delays
@mock-server --graphql
JSON Server (Simple Mock)
Quick Setup
# Install
npm install -g json-server
# Create db.json
cat > db.json << EOF
{
"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", "userId": 1 },
{ "id": 2, "title": "Mock APIs", "userId": 2 }
]
}
EOF
# Start server
json-server --watch db.json --port 3000
Available Endpoints (Auto-generated)
# GET all users
curl http://localhost:3000/users
# GET user by ID
curl http://localhost:3000/users/1
# POST new user
curl -X POST http://localhost:3000/users \
-H "Content-Type: application/json" \
-d '{"name": "Bob", "email": "bob@example.com"}'
# PUT update user
curl -X PUT http://localhost:3000/users/1 \
-H "Content-Type: application/json" \
-d '{"id": 1, "name": "John Updated", "email": "john.new@example.com"}'
# PATCH partial update
curl -X PATCH http://localhost:3000/users/1 \
-H "Content-Type: application/json" \
-d '{"name": "John Patched"}'
# DELETE user
curl -X DELETE http://localhost:3000/users/1
# Query parameters
curl "http://localhost:3000/users?_page=1&_limit=10"
curl "http://localhost:3000/users?_sort=name&_order=asc"
curl "http://localhost:3000/posts?userId=1"
Custom Routes
// routes.json
{
"/api/*": "/$1",
"/users/:id/posts": "/posts?userId=:id",
"/auth/login": "/login"
}
// Start with custom routes
json-server db.json --routes routes.json
Mock Service Worker (MSW)
Setup
npm install --save-dev msw
REST API Mocking
// src/mocks/handlers.js
import { http, HttpResponse } from 'msw';
export const handlers = [
// GET users
http.get('/api/users', () => {
return HttpResponse.json([
{ id: '1', name: 'John Doe', email: 'john@example.com' },
{ id: '2', name: 'Jane Smith', email: 'jane@example.com' }
]);
}),
// GET user by ID
http.get('/api/users/:userId', ({ params }) => {
const { userId } = params;
// Simulate not found
if (userId === '999') {
return new HttpResponse(null, { status: 404 });
}
return HttpResponse.json({
id: userId,
name: 'John Doe',
email: 'john@example.com'
});
}),
// POST create user
http.post('/api/users', async ({ request }) => {
const data = await request.json();
// Simulate validation error
if (!data.email) {
return HttpResponse.json(
{ error: 'Email is required' },
{ status: 400 }
);
}
return HttpResponse.json(
{
id: '123',
...data,
createdAt: new Date().toISOString()
},
{ status: 201 }
);
}),
// PUT update user
http.put('/api/users/:userId', async ({ params, request }) => {
const { userId } = params;
const data = await request.json();
return HttpResponse.json({
id: userId,
...data,
updatedAt: new Date().toISOString()
});
}),
// DELETE user
http.delete('/api/users/:userId', ({ params }) => {
return new HttpResponse(null, { status: 204 });
}),
// Simulate delay
http.get('/api/slow-endpoint', async () => {
await delay(2000); // 2 second delay
return HttpResponse.json({ message: 'Slow response' });
}),
// Simulate random errors
http.get('/api/unreliable', () => {
if (Math.random() > 0.5) {
return new HttpResponse(null, { status: 500 });
}
return HttpResponse.json({ status: 'ok' });
}),
// Authentication
http.post('/api/auth/login', async ({ request }) => {
const { email, password } = await request.json();
if (email === 'user@example.com' && password === 'password') {
return HttpResponse.json({
accessToken: 'mock-jwt-token',
refreshToken: 'mock-refresh-token',
expiresIn: 3600
});
}
return HttpResponse.json(
{ error: 'Invalid credentials' },
{ status: 401 }
);
})
];
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
Browser Setup
// src/mocks/browser.js
import { setupWorker } from 'msw/browser';
import { handlers } from './handlers';
export const worker = setupWorker(...handlers);
// src/index.js
if (process.env.NODE_ENV === 'development') {
const { worker } = await import('./mocks/browser');
await worker.start();
}
Node.js Setup (Testing)
// src/mocks/server.js
import { setupServer } from 'msw/node';
import { handlers } from './handlers';
export const server = setupServer(...handlers);
// src/tests/setup.js
import { server } from '../mocks/server';
beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());
Prism (OpenAPI-based)
From OpenAPI Spec
# Install
npm install -g @stoplight/prism-cli
# Start mock server from OpenAPI spec
prism mock openapi.yaml
# Specify port
prism mock openapi.yaml --port 4010
# Enable validation
prism mock openapi.yaml --errors
# Dynamic responses based on examples
prism mock openapi.yaml --dynamic
Example OpenAPI for Prism
openapi: 3.0.0
info:
title: Mock API
version: 1.0.0
paths:
/api/users:
get:
responses:
'200':
description: Success
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/User'
examples:
success:
value:
- id: "1"
name: "John Doe"
email: "john@example.com"
/api/users/{userId}:
get:
parameters:
- name: userId
in: path
required: true
schema:
type: string
responses:
'200':
description: Success
content:
application/json:
schema:
$ref: '#/components/schemas/User'
examples:
user:
value:
id: "1"
name: "John Doe"
email: "john@example.com"
'404':
description: Not found
content:
application/json:
examples:
notFound:
value:
error: "User not found"
components:
schemas:
User:
type: object
properties:
id:
type: string
name:
type: string
email:
type: string
Express Mock Server
Custom Implementation
const express = require('express');
const cors = require('cors');
const app = express();
app.use(cors());
app.use(express.json());
// Middleware for random delays
app.use((req, res, next) => {
const delay = Math.random() * 1000; // 0-1 second
setTimeout(next, delay);
});
// Middleware for authentication
app.use('/api/*', (req, res, next) => {
const token = req.headers.authorization;
if (!token || !token.startsWith('Bearer ')) {
return res.status(401).json({ error: 'Unauthorized' });
}
next();
});
// In-memory database
let users = [
{ id: '1', name: 'John Doe', email: 'john@example.com' },
{ id: '2', name: 'Jane Smith', email: 'jane@example.com' }
];
// GET all users
app.get('/api/users', (req, res) => {
const { page = 1, limit = 10 } = req.query;
const start = (page - 1) * limit;
const end = start + parseInt(limit);
const paginatedUsers = users.slice(start, end);
res.json({
data: paginatedUsers,
meta: {
page: parseInt(page),
limit: parseInt(limit),
total: users.length,
totalPages: Math.ceil(users.length / limit)
}
});
});
// GET user by ID
app.get('/api/users/:id', (req, res) => {
const user = users.find(u => u.id === req.params.id);
if (!user) {
return res.status(404).json({ error: 'User not found' });
}
res.json(user);
});
// POST create user
app.post('/api/users', (req, res) => {
const { name, email } = req.body;
// Validation
if (!name || !email) {
return res.status(400).json({
error: 'Validation failed',
details: {
name: !name ? 'Name is required' : undefined,
email: !email ? 'Email is required' : undefined
}
});
}
// Check duplicate email
if (users.some(u => u.email === email)) {
return res.status(409).json({ error: 'Email already exists' });
}
const newUser = {
id: String(Date.now()),
name,
email,
createdAt: new Date().toISOString()
};
users.push(newUser);
res.status(201).json(newUser);
});
// PUT update user
app.put('/api/users/:id', (req, res) => {
const index = users.findIndex(u => u.id === req.params.id);
if (index === -1) {
return res.status(404).json({ error: 'User not found' });
}
users[index] = {
...users[index],
...req.body,
updatedAt: new Date().toISOString()
};
res.json(users[index]);
});
// PATCH partial update
app.patch('/api/users/:id', (req, res) => {
const index = users.findIndex(u => u.id === req.params.id);
if (index === -1) {
return res.status(404).json({ error: 'User not found' });
}
users[index] = {
...users[index],
...req.body,
updatedAt: new Date().toISOString()
};
res.json(users[index]);
});
// DELETE user
app.delete('/api/users/:id', (req, res) => {
const index = users.findIndex(u => u.id === req.params.id);
if (index === -1) {
return res.status(404).json({ error: 'User not found' });
}
users.splice(index, 1);
res.status(204).send();
});
// Error simulation endpoint
app.get('/api/simulate-error', (req, res) => {
const errorType = req.query.type || 'random';
if (errorType === 'timeout') {
// Never respond (timeout)
return;
}
if (errorType === 'random' && Math.random() > 0.5) {
return res.status(500).json({ error: 'Internal server error' });
}
res.json({ status: 'ok' });
});
// Health check
app.get('/health', (req, res) => {
res.json({ status: 'ok', timestamp: new Date().toISOString() });
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Mock server running on http://localhost:${PORT}`);
});
GraphQL Mock Server
Using Apollo Server
const { ApolloServer, gql } = require('apollo-server');
const { MockList } = require('@graphql-tools/mock');
// Schema
const typeDefs = gql`
type User {
id: ID!
name: String!
email: String!
posts: [Post!]!
}
type Post {
id: ID!
title: String!
content: String!
author: User!
}
type Query {
users: [User!]!
user(id: ID!): User
posts: [Post!]!
}
type Mutation {
createUser(name: String!, email: String!): User!
createPost(title: String!, content: String!, authorId: ID!): Post!
}
`;
// Mocks
const mocks = {
User: () => ({
id: () => Math.random().toString(36).substring(7),
name: () => 'John Doe',
email: () => 'john@example.com',
posts: () => new MockList([2, 6])
}),
Post: () => ({
id: () => Math.random().toString(36).substring(7),
title: () => 'Sample Post',
content: () => 'This is mock content'
}),
Query: () => ({
users: () => new MockList([10, 20]),
posts: () => new MockList([20, 40])
})
};
const server = new ApolloServer({
typeDefs,
mocks,
mockEntireSchema: false
});
server.listen().then(({ url }) => {
console.log(`GraphQL mock server ready at ${url}`);
});
Custom Resolvers
const resolvers = {
Query: {
users: () => [
{ id: '1', name: 'John Doe', email: 'john@example.com' },
{ id: '2', name: 'Jane Smith', email: 'jane@example.com' }
],
user: (_, { id }) => {
const users = [
{ id: '1', name: 'John Doe', email: 'john@example.com' },
{ id: '2', name: 'Jane Smith', email: 'jane@example.com' }
];
return users.find(u => u.id === id);
}
},
Mutation: {
createUser: (_, { name, email }) => ({
id: Math.random().toString(36).substring(7),
name,
email
})
}
};
const server = new ApolloServer({
typeDefs,
resolvers
});
WireMock (Java-based)
Docker Setup
# Run WireMock in Docker
docker run -d \
--name wiremock \
-p 8080:8080 \
-v $(pwd)/wiremock:/home/wiremock \
wiremock/wiremock:latest
# Create mapping
mkdir -p wiremock/mappings
cat > wiremock/mappings/users.json << EOF
{
"request": {
"method": "GET",
"url": "/api/users"
},
"response": {
"status": 200,
"headers": {
"Content-Type": "application/json"
},
"jsonBody": [
{ "id": "1", "name": "John Doe", "email": "john@example.com" }
]
}
}
EOF
Mock Data Generation
Using Faker.js
const { faker } = require('@faker-js/faker');
// Generate mock user
function generateUser() {
return {
id: faker.string.uuid(),
name: faker.person.fullName(),
email: faker.internet.email(),
avatar: faker.image.avatar(),
address: {
street: faker.location.streetAddress(),
city: faker.location.city(),
country: faker.location.country()
},
createdAt: faker.date.past().toISOString()
};
}
// Generate multiple users
function generateUsers(count = 10) {
return Array.from({ length: count }, generateUser);
}
// Generate posts
function generatePost() {
return {
id: faker.string.uuid(),
title: faker.lorem.sentence(),
content: faker.lorem.paragraphs(3),
author: generateUser(),
createdAt: faker.date.past().toISOString()
};
}
Advanced Scenarios
Conditional Responses
// Return different responses based on headers
http.get('/api/users', ({ request }) => {
const acceptLanguage = request.headers.get('Accept-Language');
if (acceptLanguage?.includes('es')) {
return HttpResponse.json({
mensaje: 'Hola Mundo'
});
}
return HttpResponse.json({
message: 'Hello World'
});
});
// Based on query parameters
http.get('/api/users', ({ request }) => {
const url = new URL(request.url);
const format = url.searchParams.get('format');
if (format === 'xml') {
return new HttpResponse(
'<users><user>John</user></users>',
{ headers: { 'Content-Type': 'application/xml' } }
);
}
return HttpResponse.json([{ name: 'John' }]);
});
Stateful Mocking
let requestCount = 0;
http.get('/api/rate-limit', () => {
requestCount++;
if (requestCount > 10) {
return HttpResponse.json(
{ error: 'Rate limit exceeded' },
{ status: 429 }
);
}
return HttpResponse.json({ count: requestCount });
});
Network Error Simulation
http.get('/api/network-error', () => {
return HttpResponse.error();
});
// Timeout simulation
http.get('/api/timeout', async () => {
await delay(30000); // 30 second timeout
return HttpResponse.json({});
});
Best Practices
Mock Server Design
- Mirror production API structure
- Use realistic data
- Implement proper error responses
- Support pagination
- Include rate limiting simulation
Data Management
- Use data generators for realistic mock data
- Maintain consistent data across requests
- Reset state between test runs
- Support CRUD operations
- Implement proper relationships
Response Scenarios
- Success responses (200, 201, 204)
- Client errors (400, 401, 403, 404, 409)
- Server errors (500, 502, 503)
- Network errors
- Timeouts
Development Workflow
- Start mock server before frontend development
- Use environment variables for API URLs
- Switch between mock and real API easily
- Document available endpoints
- Keep mock data synchronized with API
Testing
- Use mocks for isolated unit tests
- Test error handling
- Verify request validation
- Test authentication flows
- Simulate edge cases
Tools Comparison
| Tool | Best For | Complexity | Features |
|---|---|---|---|
| JSON Server | Quick prototypes | Low | Auto-CRUD, simple setup |
| MSW | Browser/Node testing | Medium | Request interception, powerful |
| Prism | OpenAPI specs | Low | Spec-based, validation |
| WireMock | Enterprise, Java | High | Recording, matching |
| Custom Express | Full control | Medium | Complete customization |
Notes
- Use mock servers during frontend development
- Keep mock responses synchronized with API contracts
- Test both success and failure scenarios
- Implement realistic delays for better testing
- Use environment variables to switch between mock/real APIs
- Document mock endpoints and behaviors
- Reset mock state between tests
- Use data generators for realistic data
- Implement proper CORS for browser testing
- Version mock data alongside API versions