skills/curiouslearner/mock-server/SKILL.md
Create and manage mock API servers for development and testing.
npx skillsauth add aiskillstore/marketplace mock-serverInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Create and manage mock API servers for development and testing.
You are a mock API server expert. When invoked:
Create Mock Servers:
Configure Behavior:
Advanced Scenarios:
Integration:
@mock-server
@mock-server --from-openapi
@mock-server --port 3000
@mock-server --with-delays
@mock-server --graphql
# Install
npm install -g json-server
# Create db.json
cat > db.json << EOF
{
"users": [
{ "id": 1, "name": "John Doe", "email": "[email protected]" },
{ "id": 2, "name": "Jane Smith", "email": "[email protected]" }
],
"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
# 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": "[email protected]"}'
# PUT update user
curl -X PUT http://localhost:3000/users/1 \
-H "Content-Type: application/json" \
-d '{"id": 1, "name": "John Updated", "email": "[email protected]"}'
# 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"
// routes.json
{
"/api/*": "/$1",
"/users/:id/posts": "/posts?userId=:id",
"/auth/login": "/login"
}
// Start with custom routes
json-server db.json --routes routes.json
npm install --save-dev msw
// 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: '[email protected]' },
{ id: '2', name: 'Jane Smith', email: '[email protected]' }
]);
}),
// 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: '[email protected]'
});
}),
// 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 === '[email protected]' && 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));
}
// 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();
}
// 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());
# 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
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: "[email protected]"
/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: "[email protected]"
'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
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: '[email protected]' },
{ id: '2', name: 'Jane Smith', email: '[email protected]' }
];
// 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}`);
});
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: () => '[email protected]',
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}`);
});
const resolvers = {
Query: {
users: () => [
{ id: '1', name: 'John Doe', email: '[email protected]' },
{ id: '2', name: 'Jane Smith', email: '[email protected]' }
],
user: (_, { id }) => {
const users = [
{ id: '1', name: 'John Doe', email: '[email protected]' },
{ id: '2', name: 'Jane Smith', email: '[email protected]' }
];
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
});
# 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": "[email protected]" }
]
}
}
EOF
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()
};
}
// 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' }]);
});
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 });
});
http.get('/api/network-error', () => {
return HttpResponse.error();
});
// Timeout simulation
http.get('/api/timeout', async () => {
await delay(30000); // 30 second timeout
return HttpResponse.json({});
});
| 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 |
development
Apple Human Interface Guidelines for content display components. Use this skill when the user asks about charts component, collection view, image view, web view, color well, image well, activity view, lockup, data visualization, content display, displaying images, rendering web content, color pickers, or presenting collections of items in Apple apps. Also use when the user says how should I display charts, what's the best way to show images, should I use a web view, how do I build a grid of items, what component shows media, or how do I present a share sheet. Cross-references: hig-foundations for color/typography/accessibility, hig-patterns for data visualization patterns, hig-components-layout for structural containers, hig-platforms for platform-specific component behavior.
tools
Automate HelpDesk tasks via Rube MCP (Composio): list tickets, manage views, use canned responses, and configure custom fields. Always search tools first for current schemas.
testing
Expert Haskell engineer specializing in advanced type systems, pure functional design, and high-reliability software. Use PROACTIVELY for type-level programming, concurrency, and architecture guidance.
tools
GraphQL gives clients exactly the data they need - no more, no less. One endpoint, typed schema, introspection. But the flexibility that makes it powerful also makes it dangerous. Without proper controls, clients can craft queries that bring down your server. This skill covers schema design, resolvers, DataLoader for N+1 prevention, federation for microservices, and client integration with Apollo/urql. Key insight: GraphQL is a contract. The schema is the API documentation. Design it carefully.