mockapi
A single-binary mock API server. YAML config, fake data, stateful CRUD, proxy mode, record & replay, hot-reload.
json-server, but a single binary. YAML config. Custom status codes, delays, templates, fake data, conditional responses, hot-reload. No Node.js required.
mockapi is a single-binary mock API server that spins up a fake REST API from a YAML config file. No runtime, no dependencies, no build step.
Download a prebuilt binary from the latest release:
# Linux (x86_64)
curl -LO https://github.com/purpleneutral/mockapi/releases/latest/download/mockapi-x86_64-unknown-linux-gnu.tar.gz
tar xzf mockapi-x86_64-unknown-linux-gnu.tar.gz
sudo mv mockapi /usr/local/bin/
# macOS (Apple Silicon)
curl -LO https://github.com/purpleneutral/mockapi/releases/latest/download/mockapi-aarch64-apple-darwin.tar.gz
tar xzf mockapi-aarch64-apple-darwin.tar.gz
sudo mv mockapi /usr/local/bin/
# macOS (Intel)
curl -LO https://github.com/purpleneutral/mockapi/releases/latest/download/mockapi-x86_64-apple-darwin.tar.gz
tar xzf mockapi-x86_64-apple-darwin.tar.gz
sudo mv mockapi /usr/local/bin/Or build from source:
cargo install --git https://github.com/purpleneutral/mockapi.git# Generate a starter config
mockapi init
# Start the server
mockapi serveThat's it. You have a mock API running on http://localhost:8080.
curl http://localhost:8080/users
curl http://localhost:8080/users/42
curl -X POST http://localhost:8080/users \
-H "Content-Type: application/json" \
-d '{"name":"Charlie","email":"charlie@test.com"}'Everything lives in one YAML file:
server:
port: 8080
cors: true
routes:
- method: GET
path: /users
status: 200
body:
- id: 1
name: Alice
- id: 2
name: Bob
- method: GET
path: /users/:id
status: 200
body:
id: "{{ params.id }}"
name: "{{ fake.name }}"
email: "{{ fake.email }}"
- method: POST
path: /users
status: 201
body:
id: "{{ fake.uuid }}"
name: "{{ request.body.name }}"
created_at: "{{ now }}"
- method: GET
path: /slow
delay: 2000
status: 200
body:
message: Finally hereEdit the file while the server is running — it hot-reloads automatically.
- Route matching — exact paths, parameters (
:id), wildcards (**) - All HTTP methods — GET, POST, PUT, PATCH, DELETE, OPTIONS, HEAD, ANY
- Custom status codes — per route
- Custom headers — per route and global defaults
- Response delays — per route and global default
- Templates —
{{ params.id }},{{ request.body.name }},{{ query.page }},{{ now }},{{ seq }} - Fake data —
{{ fake.name }},{{ fake.email }},{{ fake.uuid }},{{ fake.int(1, 100) }}, and more - Conditional responses — return different bodies/statuses based on query, headers, or body content
- Stateful mode — in-memory CRUD with auto-ID, filtering, sorting, pagination
- Proxy mode — forward unmatched requests to a real backend
- Record & replay — record real API responses, replay them as mocks
- File responses —
file: responses/data.json - CORS — enabled by default, configurable origins
- Hot-reload — edit config, routes update instantly
- Colored logging — minimal or verbose mode
- Helpful 404s — tells you what to add to your config
mockapi serve # start server (default: ./mockapi.yaml)
mockapi serve -c api.yaml # use a specific config
mockapi serve -p 3001 # custom port
mockapi serve -d 500 # add 500ms delay to all responses
mockapi serve --log verbose # full request/response details
mockapi serve --no-cors # disable CORS
mockapi serve --no-watch # disable hot-reload
mockapi init # generate starter config
mockapi init -o api.yaml # custom output path
mockapi validate # check config for errors
mockapi validate -c api.yaml # validate a specific file
mockapi record -t https://api.example.com # record from a real API
mockapi record -t https://api.example.com -o api.yaml # custom output
mockapi record -t https://api.example.com --filter "/api/**" # filter paths
Generate realistic random data in templates:
body:
id: "{{ fake.uuid }}"
name: "{{ fake.name }}"
first: "{{ fake.firstName }}"
last: "{{ fake.lastName }}"
email: "{{ fake.email }}"
phone: "{{ fake.phone }}"
company: "{{ fake.company }}"
address: "{{ fake.address }}"
url: "{{ fake.url }}"
color: "{{ fake.color }}"
active: "{{ fake.bool }}"
score: "{{ fake.int(1, 100) }}"
price: "{{ fake.float(9.99, 99.99, 2) }}"
bio: "{{ fake.lorem(20) }}"
status: "{{ fake.pick('active', 'inactive', 'pending') }}"Every request returns different random values.
Return different responses based on request properties. First match wins; if no condition matches, the default body/status is used.
routes:
- method: POST
path: /auth/login
status: 200
body:
token: "default-token"
responses:
# Admin credentials
- when:
body:
email: admin@test.com
password: admin123
status: 200
body:
token: "admin-jwt-token"
role: admin
# Wrong password
- when:
body:
password: wrong
status: 401
body:
error: Invalid credentials
# Missing fields
- when:
body_missing:
- email
- password
status: 400
body:
error: Missing required fieldswhen:
query: # match query parameters
role: admin
headers: # match request headers (case-insensitive)
Authorization: "Bearer valid-token"
body: # match request body fields (partial/subset match)
email: admin@test.com
body_has: # require fields to exist in body
- email
- password
body_missing: # require fields to NOT exist in body
- tokenAll conditions in a when clause are ANDed together.
Enable in-memory CRUD — POST creates, GET reads, PUT/PATCH updates, DELETE removes. No code required, just declare your resources:
stateful:
enabled: true
resources:
- path: /users
id_field: id
seed:
- id: 1
name: Alice
- id: 2
name: Bob
- path: /posts
id_field: id
seed: []This auto-generates these endpoints:
| Method | Endpoint | Behavior | Status |
|---|---|---|---|
| GET | /users |
List all | 200 |
| GET | /users/1 |
Get by ID | 200 / 404 |
| POST | /users |
Create (auto-ID) | 201 |
| PUT | /users/1 |
Full replace | 200 / 404 |
| PATCH | /users/1 |
Partial update | 200 / 404 |
| DELETE | /users/1 |
Remove | 204 / 404 |
Query parameters on list endpoints:
# Filter by field value
curl http://localhost:8080/users?name=Alice
# Sort
curl http://localhost:8080/users?_sort=name&_order=desc
# Paginate (adds X-Total-Count header)
curl http://localhost:8080/users?_page=1&_limit=10Explicit routes always override stateful endpoints. State is in-memory only — resets on server restart.
Forward unmatched requests to a real backend. Explicit routes and stateful resources are checked first — only requests that don't match anything get proxied.
server:
port: 8080
proxy:
enabled: true
target: "https://api.example.com"
pass_headers: true # forward request headers (default: true)
routes:
# Override specific endpoints locally
- method: GET
path: /api/feature-flags
status: 200
body:
dark_mode: true
new_dashboard: trueThis lets you:
- Mock specific endpoints while proxying the rest to a real API
- Develop against a real backend with selective overrides
- Test edge cases by intercepting specific routes
Proxy returns 502 Bad Gateway if the upstream is unreachable. Upstream response status, headers, and body are forwarded as-is.
Record responses from a real API and replay them as mocks. No config writing needed.
# Record from a real API
mockapi record -t https://jsonplaceholder.typicode.com -o recorded.yaml
# Make requests through the recorder (they're proxied to the real API)
curl http://localhost:8080/users
curl http://localhost:8080/users/1
curl http://localhost:8080/posts/1
# Press Ctrl+C — routes are saved to recorded.yaml
# Replay the recording
mockapi serve -c recorded.yamlOptions:
-t, --target <url>— target API URL (required)-o, --output <path>— output file (default:./recorded.yaml)-p, --port <port>— local port (default: 8080)--filter <pattern>— only record matching paths (e.g.,"/api/**")
Each unique method+path is recorded once (first response wins). The output is a valid mockapi config file.
# Path parameters (from /users/:id)
"{{ params.id }}"
# Query parameters (from ?page=2)
"{{ query.page }}"
# Request body fields
"{{ request.body.name }}"
"{{ request.body.email }}"
# Request metadata
"{{ request.method }}"
"{{ request.path }}"
"{{ request.headers.authorization }}"
# Built-in
"{{ now }}" # ISO 8601 timestamp
"{{ now_unix }}" # Unix timestamp (seconds)
"{{ now_ms }}" # Unix timestamp (milliseconds)
"{{ seq }}" # Auto-incrementing counterSee the examples/ directory:
examples/basic.yaml— stateful CRUD with fake data and conditional responsesexamples/ecommerce.yaml— products, cart, checkout with fake data and conditional responsesexamples/auth.yaml— login with conditional responses, token validationexamples/proxy.yaml— proxy to a real API with local overrides
mockapi serve -c examples/ecommerce.yamlgit clone https://github.com/purpleneutral/mockapi.git
cd mockapi
cargo build --release
ls -lh target/release/mockapi # ~5.6 MBIf you find mockapi useful, consider supporting the project:
GPL-3.0