🚧 under development, wait for v1 for usage
Schema-driven mock API generator with direct callable API and plugin pipeline
Schmock is a powerful mock API generator that allows you to quickly create predictable, schema-driven mock endpoints for frontend development. With its direct callable API, you can define mocks with minimal boilerplate and maximum expressiveness.
- 🚀 Quick Setup: Get a mock API running in under 30 seconds
- ✨ Direct API: Callable instances with zero boilerplate
- 📋 Schema-Driven: Use JSON Schema to define your data structures
- 🎯 Type-Safe: Full TypeScript support with ambient types
- 🔄 Stateful Mocks: Maintain state between requests
- 🔧 Plugin Pipeline: Extensible
.pipe()architecture
# Using bun (recommended)
bun add @schmock/core
# Using npm
npm install @schmock/core
# Using yarn
yarn add @schmock/coreimport { schmock } from '@schmock/core'
// Create a mock API with global configuration
const mock = schmock({ debug: true, namespace: '/api' })
// Define routes directly - no build() needed!
mock('GET /users', () => [
{ id: 1, name: 'John Doe', email: '[email protected]' },
{ id: 2, name: 'Jane Smith', email: '[email protected]' }
], { contentType: 'application/json' })
mock('GET /users/:id', ({ params }) => {
const users = [
{ id: 1, name: 'John Doe', email: '[email protected]' },
{ id: 2, name: 'Jane Smith', email: '[email protected]' }
]
return users.find(u => u.id === Number(params.id)) || [404, { error: 'User not found' }]
}, { contentType: 'application/json' })
// Make requests immediately
const response = await mock.handle('GET', '/api/users')
console.log(response.status) // 200
console.log(response.body) // [{ id: 1, name: 'John Doe', ... }, ...]
// With parameters
const userResponse = await mock.handle('GET', '/api/users/1')
console.log(userResponse.body) // { id: 1, name: 'John Doe', ... }import { schmock } from '@schmock/core'
import { schemaPlugin } from '@schmock/schema'
import { validationPlugin } from '@schmock/validation'
const mock = schmock({ debug: true })
// Chain plugins with .pipe() - clean and expressive
mock('GET /users', userSchema, { contentType: 'application/json' })
.pipe(schemaPlugin())
.pipe(validationPlugin())
.pipe(loggingPlugin())
mock('POST /users', createUserGenerator, { contentType: 'application/json' })
.pipe(validationPlugin())
.pipe(persistencePlugin())// Initialize mock with global state
const mock = schmock({
state: { users: [] },
debug: true
})
mock('GET /users', ({ state }) => state.users, {
contentType: 'application/json'
})
mock('POST /users', ({ body, state }) => {
const newUser = {
id: Date.now(),
...body,
createdAt: new Date().toISOString()
}
state.users.push(newUser)
return [201, newUser]
}, { contentType: 'application/json' })
mock('DELETE /users/:id', ({ params, state }) => {
const index = state.users.findIndex(u => u.id === Number(params.id))
if (index === -1) return [404, { error: 'User not found' }]
state.users.splice(index, 1)
return [204, null]
}, { contentType: 'application/json' })
// Use immediately
const created = await mock.handle('POST', '/users', {
body: { name: 'Alice', email: '[email protected]' }
})
console.log(created.status) // 201const mock = schmock()
// Generator function - called on each request
mock('GET /time', () => ({
timestamp: new Date().toISOString()
}), { contentType: 'application/json' })
// Static JSON data - returned as-is
mock('GET /config', {
version: '1.0.0',
features: ['auth', 'api', 'websockets']
}, { contentType: 'application/json' })
// Schmock automatically detects the difference based on contentType validationconst mock = schmock()
mock('POST /upload', ({ body }) => [
201,
{ id: 123, filename: body.name },
{ 'Location': '/api/files/123' }
], { contentType: 'application/json' })
mock('GET /protected', ({ headers }) => {
if (!headers.authorization) {
return [401, { error: 'Unauthorized' }]
}
return { data: 'secret' }
}, { contentType: 'application/json' })const mock = schmock()
mock('GET /search', ({ query }) => ({
results: [],
query: query.q,
page: Number(query.page) || 1
}), { contentType: 'application/json' })
mock('GET /me', ({ headers }) => ({
authenticated: !!headers.authorization,
user: headers.authorization ? { id: 1, name: 'John' } : null
}), { contentType: 'application/json' })
// With query parameters
const search = await mock.handle('GET', '/search', {
query: { q: 'typescript', page: '2' }
})
console.log(search.body) // { results: [], query: 'typescript', page: 2 }
// With headers
const me = await mock.handle('GET', '/me', {
headers: { authorization: 'Bearer token123' }
})
console.log(me.body.authenticated) // trueimport { schmock } from '@schmock/core'
import { schemaPlugin } from '@schmock/schema'
const mock = schmock()
// Define a route with JSON Schema instead of a generator
mock('GET /users', {
type: 'array',
items: {
type: 'object',
properties: {
id: { type: 'integer' },
name: { type: 'string', faker: 'person.fullName' },
email: { type: 'string', format: 'email' }
}
}
}, { contentType: 'application/json' })
.pipe(schemaPlugin())
// Generates realistic data automatically
const response = await mock.handle('GET', '/users')
// [{ id: 1, name: "John Doe", email: "john@example.com" }, ...]import { schmock } from '@schmock/core'
import { schemaPlugin } from '@schmock/schema'
import { validationPlugin } from '@schmock/validation'
import { cachingPlugin } from '@schmock/caching'
const mock = schmock({
debug: true,
namespace: '/api/v1'
})
// Complex pipeline: validation → schema generation → caching → response
mock('GET /users', userListSchema, { contentType: 'application/json' })
.pipe(validationPlugin({ strict: true }))
.pipe(schemaPlugin({ count: 10 }))
.pipe(cachingPlugin({ ttl: 60000 }))
mock('POST /users', createUserHandler, { contentType: 'application/json' })
.pipe(validationPlugin({ validateBody: true }))
.pipe(persistencePlugin())
.pipe(notificationPlugin())import express from 'express'
import { toExpress } from '@schmock/express'
const app = express()
const mock = schmock()
mock('GET /users', () => [{ id: 1, name: 'John' }], {
contentType: 'application/json'
})
// Convert to Express middleware
app.use('/api', toExpress(mock))
app.listen(3000)
// Now responds at http://localhost:3000/api/usersfunction schmock(config?: GlobalConfig): CallableMockInstanceGlobal Configuration:
interface GlobalConfig {
debug?: boolean; // Enable debug logging
namespace?: string; // URL prefix for all routes
state?: any; // Initial shared state
delay?: number | [number, number]; // Response delay (ms)
}Define routes by calling the mock instance directly:
const mock = schmock()
// Basic route definition
mock('GET /users', generatorFunction, routeConfig)
mock('POST /users', staticData, routeConfig)
mock('PUT /users/:id', schemaObject, routeConfig)
mock('DELETE /users/:id', generatorFunction, routeConfig)Route Configuration:
interface RouteConfig {
contentType: string; // 'application/json', 'text/plain', etc.
// Additional route-specific options...
}Generator functions can return:
- Direct value: Returns as 200 OK
[status, body]: Custom status code[status, body, headers]: Custom status, body, and headers
Generator functions receive a context with:
state: Shared mutable stateparams: Path parameters (e.g.,:id)query: Query string parametersbody: Request bodyheaders: Request headersmethod: HTTP methodpath: Request path
Chain plugins using .pipe():
mock('GET /users', generator, config)
.pipe(plugin1())
.pipe(plugin2())
.pipe(plugin3())This project uses a monorepo structure with Bun workspaces and automated Git hooks for quality assurance.
# Install dependencies
bun install
# Configure Git hooks (recommended for contributors)
bun run setup
# Build packages
bun run build# Run all tests (262 total: 101 unit + 161 BDD)
bun test
# Run comprehensive test suite with typecheck (recommended before commits)
bun test:all
# Run unit tests only (all packages)
bun test:unit
# Run BDD tests only
bun test:bdd
# Type checking
bun run typecheck
# Linting
bun run lint
bun run lint:fix # Auto-fix issuesAfter running bun run setup, Git hooks will automatically:
- Pre-commit: Run linting and comprehensive tests before allowing commits
- Commit-msg: Enforce conventional commit message format
# Bypass hooks if needed (not recommended)
git commit --no-verifyschmock/
├── packages/
│ ├── core/ # Core Schmock functionality with callable API
│ ├── schema/ # Schema plugin for JSON Schema generation
│ ├── express/ # Express middleware adapter
│ └── angular/ # Angular HTTP interceptor adapter
├── features/ # BDD feature files
├── types/ # Shared TypeScript types
├── docs/ # API documentation
└── examples/ # Usage examples
We use GitHub Flow with automated quality checks:
-
Clone and setup:
git clone <repo> cd schmock bun install bun run setup # Configure Git hooks
-
Create feature branch:
git checkout develop git pull origin develop git checkout -b feature/your-feature-name
-
Development workflow:
- Make your changes with tests
- Git hooks automatically run linting and tests on commit
- BDD tests may fail during development (expected for TDD)
-
Create PR:
- Push feature branch to GitHub
- Create PR from feature → develop
- CI runs comprehensive checks
- All tests must pass for main branch PRs
-
After review: Merge to develop, then periodically develop → main
- Automatic: Git hooks enforce linting and test standards
- Manual override: Use
git commit --no-verifyonly when necessary - Comprehensive testing: 262 tests (101 unit + 161 BDD) with TypeScript checking
See CLAUDE.md for detailed development guidelines and project architecture.
- Basic static mocking with GET requests
- Support for all HTTP methods (POST, PUT, DELETE, PATCH)
- Dynamic route patterns (e.g.,
/api/users/:id) - State management between requests
- Direct callable API with zero boilerplate
- Custom status codes and headers
- Plugin pipeline with
.pipe()chaining - Schema-based data generation
- Express middleware adapter
- Angular HTTP interceptor adapter
- Runtime content-type validation
- Request/response validation plugins
- Response delays and error simulation
- Caching plugin
- Persistence plugin
- GraphQL support
- WebSocket support
MIT