name: mockoon
description: >-
Mockoon runs mock REST APIs on your machine from a JSON environment file:
a desktop app designs them and a CLI serves them without a GUI. Use when
the user wants to fake a backend for frontend work, mock an OpenAPI spec,
return dynamic responses with templating and rules, or start a mock server
in CI or Docker. Also use when the user mentions "mockoon," "API mocking,"
"mock server," "mock API," "OpenAPI mock," or "local API simulation." For
programmatic HTTP mocking, see wiremock.
license: Apache-2.0
compatibility: "Node.js 18+ for the CLI (@mockoon/cli 9); desktop app on Windows, macOS and Linux"
metadata:
author: terminal-skills
version: "1.1.0"
category: development
repository: https://github.com/mockoon/mockoon
tags:
- api-mocking
- mock-server
- openapi
- development
Mockoon
Overview
Mockoon is an open-source tool for running mock API servers locally. A mock is an environment: one JSON file holding routes, responses, rules and data. The desktop app edits and runs environments; the CLI (mockoon-cli) serves the same files headlessly in a terminal, CI job or container. Responses can be static, generated with Handlebars templating and Faker, chosen by rules on the request, or backed by an in-memory data bucket that behaves like a small CRUD database.
Instructions
Initial Assessment
- Purpose — Frontend development, integration testing, or demo?
- API spec — Do you have an OpenAPI/Swagger spec to import?
- Complexity — Static responses or dynamic (templating, rules)?
- Environment — Local development, CI, or Docker?
CLI Setup
# Add the CLI to the project (the binary is called mockoon-cli)
npm install --save-dev @mockoon/cli
npx mockoon-cli start --data ./mocks/storefront.json --port 3001
# Or install it once for all projects
npm install -g @mockoon/cli
The desktop app is optional: brew install --cask mockoon (macOS), winget install mockoon (Windows), snap install mockoon (Linux). It stores each environment as a JSON file — right-click an environment and choose "Show in folder" to find it.
Environment Configuration
mocks/storefront.json — a data bucket, a CRUD route on top of it, and a route with conditional responses:
{
"uuid": "f5420a7c-39c1-4441-a915-8b8e992c9aa8",
"lastMigration": 33,
"name": "Storefront API",
"endpointPrefix": "api",
"port": 3001,
"hostname": "127.0.0.1",
"data": [
{
"uuid": "0e938a92-13f3-4763-8728-720e9a7a582b",
"id": "prod",
"name": "products",
"value": "[\n {{#repeat 3}}\n { \"id\": \"{{faker 'string.uuid'}}\", \"name\": \"{{faker 'commerce.productName'}}\", \"priceCents\": {{faker 'number.int' min=500 max=9900}} }\n {{/repeat}}\n]"
}
],
"routes": [
{
"uuid": "5f86de43-cd07-425b-911b-0c9fe72d144e",
"type": "crud",
"endpoint": "products",
"responses": [
{ "uuid": "1493969d-e219-401e-905d-e14c1cb99f86", "bodyType": "DATABUCKET", "databucketID": "prod", "default": true }
]
},
{
"uuid": "18b5ebe3-9ef2-4662-9837-ff33b4cb0280",
"type": "http",
"method": "get",
"endpoint": "orders/:id",
"responses": [
{
"uuid": "2de84593-5bd0-402e-9931-1db212693cb1",
"statusCode": 200,
"label": "Found",
"headers": [{ "key": "Content-Type", "value": "application/json" }],
"body": "{ \"id\": \"{{urlParam 'id'}}\", \"status\": \"shipped\", \"placedAt\": \"{{now 'yyyy-MM-dd'}}\" }",
"default": true
},
{
"uuid": "5614aaa1-1942-4a87-b143-de99ae9fe737",
"statusCode": 401,
"label": "No token",
"headers": [{ "key": "Content-Type", "value": "application/json" }],
"body": "{ \"error\": \"Unauthorized\" }",
"rules": [{ "target": "header", "modifier": "Authorization", "operator": "null", "value": "", "invert": false }]
},
{
"uuid": "c75cf058-59ac-4840-9124-85aea071a2b0",
"statusCode": 404,
"label": "Unknown order",
"headers": [{ "key": "Content-Type", "value": "application/json" }],
"body": "{ \"error\": \"Order not found\" }",
"rules": [{ "target": "params", "modifier": "id", "operator": "equals", "value": "ord_missing", "invert": false }]
}
]
}
]
}
This file is trimmed for reading. mockoon-cli start loads a copy, fills every omitted key with its default and leaves the file on disk untouched; mockoon-cli validate --data mocks/storefront.json is strict and lists each omitted key. Files saved by the desktop app or produced by mockoon-cli import are complete. Keep lastMigration (33 for version 9.9) — without it the CLI stops to ask whether to repair the file unless --repair is passed. Every uuid must be a real, unique UUID (node -e "console.log(crypto.randomUUID())").
A CRUD route serves GET, POST, PUT, PATCH and DELETE on /products and /products/:id from the bucket. Changes live in memory until the server restarts. The list endpoint accepts page, limit, sort, order, search and filters such as priceCents_gt=2000 or name_like=Tote, and returns X-Total-Count and X-Filtered-Count headers.
Templating Helpers
A response body (or bucket value) is a Handlebars template:
{
"id": "{{faker 'string.uuid'}}",
"name": "{{faker 'person.fullName'}}",
"email": "{{faker 'internet.email'}}",
"createdAt": "{{now 'yyyy-MM-dd'}}",
"quantity": {{faker 'number.int' min=1 max=20}},
"request": {
"method": "{{method}}",
"pathId": "{{urlParam 'id'}}",
"search": "{{queryParam 'search'}}",
"customerEmail": "{{body 'customer.email'}}",
"auth": "{{header 'Authorization'}}"
},
"firstProduct": {{data 'products' '0'}},
"productNames": [{{#each (dataRaw 'products')}}"{{name}}"{{#unless @last}}, {{/unless}}{{/each}}],
"region": "{{getEnvVar 'MOCKOON_REGION' 'eu-west-1'}}"
}
data and dataRaw read a bucket by name. getEnvVar only sees variables that start with MOCKOON_ unless --env-vars-prefix changes the prefix.
Response Rules
Each response may carry rules; the first response whose rules match is served, otherwise the one marked "default": true. Within a response, "rulesOperator": "AND" requires all rules (the default is OR).
- target:
body, query, header, cookie, params, path, method, request_number, global_var, data_bucket, templating
- operator:
equals, regex, regex_i, null, empty_array, array_includes, valid_json_schema
- modifier: the property to read, such as a header name, a query parameter or a body path like
customer.email
- invert:
true negates the rule
{ "target": "query", "modifier": "category", "operator": "regex_i", "value": "^(tea|coffee)quot;, "invert": false }
OpenAPI
# Serve a spec directly (Swagger 2 / OpenAPI 3, JSON or YAML)
npx mockoon-cli start --data ./openapi.yaml --port 3001
# Convert it to an environment file to add rules and templating, or go the other way
npx mockoon-cli import --input ./openapi.yaml --output ./mocks/inventory.json --prettify
npx mockoon-cli export --input ./mocks/storefront.json --output ./storefront.openapi.yaml --format yaml
Docker
# The image's entrypoint is "mockoon-cli start", so only flags follow the image name.
docker run -d --name mockoon \
-p 3001:3001 \
-v "$(pwd)/mocks/storefront.json:/data/storefront.json:ro" \
mockoon/cli:latest \
--data /data/storefront.json --port 3001 --hostname 0.0.0.0
# Or generate a Dockerfile that bundles the environment file
npx mockoon-cli dockerize --data ./mocks/storefront.json --port 3001 --output ./docker-build/Dockerfile
Check the generated Dockerfile before docker build: 9.9.0 still writes FROM node:16-alpine, on which CLI 9 fails to start (No such built-in module: node:readline/promises) — change that line to a current image such as node:24-alpine. Its entrypoint passes no --hostname, so the bundled file must say "hostname": "0.0.0.0" to be reachable through the published port.
CI Integration
# .github/workflows/mock-api.yml — integration tests against a Mockoon mock server
name: Integration Tests
on: [push]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v7
with:
node-version: 24
- run: npm ci
- name: Start mock server
uses: mockoon/cli-action@v3
with:
version: "9.9.0"
data-file: "./mocks/storefront.json"
port: 3001
- run: npm test
env:
API_BASE_URL: http://127.0.0.1:3001/api
Without the action, start the CLI in the background (npx mockoon-cli start --data ./mocks/storefront.json --port 3001 &) and wait for the port before running tests.
Examples
Example 1: Mock a product catalog for frontend work
User: "The products API isn't ready. Give me a fake one on port 3001 that I can read from and write to."
Save the environment above as mocks/storefront.json, then:
npx mockoon-cli start --data ./mocks/storefront.json --watch
{"app":"mockoon-server","environmentName":"Storefront API","level":"info","message":"Server started on port 3001", …}
curl -s "http://127.0.0.1:3001/api/products?limit=2&sort=priceCents&order=desc"
# [{"id":"68e6f816-…","name":"Fresh Ceramic Shoes","priceCents":9532},{"id":"5370b85f-…","name":"Luxurious Ceramic Fish","priceCents":6775}]
curl -s -X POST -H 'Content-Type: application/json' \
-d '{"name":"Canvas Tote","priceCents":2400}' http://127.0.0.1:3001/api/products
# {"name":"Canvas Tote","priceCents":2400,"id":"863298e9-9005-4676-8773-db2b7f05330d"}
The new product is returned by later GET calls until the server restarts. --watch reloads the file when it changes.
Example 2: Test error handling with rules
User: "I need the order endpoint to return 401 without a token and 404 for a missing order."
The orders/:id route above already encodes both cases:
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:3001/api/orders/ord_1042
# 401
curl -s -H "Authorization: Bearer $ORDERS_API_TOKEN" http://127.0.0.1:3001/api/orders/ord_1042
# { "id": "ord_1042", "status": "shipped", "placedAt": "2026-10-01" }
curl -s -H "Authorization: Bearer $ORDERS_API_TOKEN" http://127.0.0.1:3001/api/orders/ord_missing
# { "error": "Order not found" } (HTTP 404)
Guidelines
- Environment files are plain JSON — no comments. An unparseable file fails with "This file is not a valid OpenAPI specification (JSON or YAML v2.0.0 and v3.0.0) or Mockoon environment".
- Mind the braces in templates. A helper followed directly by a JSON brace (
max=9900}}}) is read by Handlebars as a triple-stash and fails with "Expecting 'CLOSE', got 'CLOSE_UNESCAPED'". Put a space before the closing brace.
- Set
Content-Type on every response; without the header Mockoon answers text/html.
- Bind to localhost. An empty
hostname listens on all interfaces. Use 127.0.0.1 on a workstation (or --hostname 127.0.0.1) and 0.0.0.0 only inside a container.
- Admin API. Every running mock exposes
/mockoon-admin/ (logs, state, environment variables) protected by a bearer token that is auto-generated and printed at startup. Pass --admin-api-token "$MOCKOON_ADMIN_API_TOKEN" to fix it, or --disable-admin-api when it is not needed.
- Logs. The CLI also writes
~/.mockoon-cli/logs/{mock-name}.log; use --disable-log-to-file in CI and --log-transaction to log full requests and responses (credential headers are redacted).
- Reproducible data.
--faker-seed 42 makes Faker output a predictable sequence; --faker-locale en_GB changes the locale.
- Keep the CLI as new as the app. A file saved by a newer desktop version than the installed CLI is refused; upgrade
@mockoon/cli. Older files are migrated in memory.
- OpenAPI is lossy. Rules and templating have no OpenAPI equivalent, so keep the Mockoon JSON as the source of truth once you add them. Use
--disable-external-refs for untrusted specs so remote $ref URLs are not fetched.
- When not to use it. For mocks defined inside test code use MSW or WireMock; Mockoon fits a shared, long-running fake backend.