Eight worked openmock.yml documents, from a minimal REST mock to a two-server gRPC topology. Each one runs with the openmock-go reference server. Copy any of them and adapt.
01-minimal.yml
Minimal REST mock
A single health-check endpoint.
openmock: 0.2.0
# The smallest useful OpenMock document: one operation, one default scenario.
# Any GET /health request returns 200 with a native-YAML body.
info:
title: Minimal REST mock
version: 1.0.0
description: A single health-check endpoint.
servers:
- name: main
type: http
operations:
- method: GET
path: /health
scenarios:
- name: healthy
response:
status: 200
headers:
Content-Type: application/json
body:
status: ok
02-scenarios-when.yml
Users API mock
Demonstrates when conditions and a default fallback.
openmock: 0.2.0
# Multiple scenarios evaluated first-match-wins. The most specific `when`
# scenarios come first; the conditionless default comes last.
info:
title: Users API mock
version: 1.0.0
description: Demonstrates when conditions and a default fallback.
servers:
- name: main
type: http
operations:
- method: GET
path: /users/{id}
scenarios:
# Matches only when the request carries an admin role header.
- name: admin-user
summary: Admin view — richer payload for callers with the admin role.
when:
headers:
X-Role: admin
response:
status: 200
headers:
Content-Type: application/json
body:
id: "{{params.id}}"
name: Ada Lovelace
role: admin
# Matches a specific user id, regardless of headers.
- name: known-user-1
when:
params:
id: "1"
response:
status: 200
headers:
Content-Type: application/json
body:
id: "1"
name: Grace Hopper
role: user
# Default fallback — always matches, so it must be last.
- name: default-user
response:
status: 200
headers:
Content-Type: application/json
body:
id: "{{params.id}}"
name: Anonymous
role: user
- method: POST
path: /users
scenarios:
# Reject when the body is missing a required field.
- name: missing-name
when:
body:
name: ""
response:
status: 422
headers:
Content-Type: application/json
body:
error: name is required
- name: created
response:
status: 201
headers:
Content-Type: application/json
body:
id: "100"
name: "{{request.body.name}}"
role: user
03-templating-faker.yml
Templating and faker demo
Shows every placeholder namespace defined in v0.1.
openmock: 0.2.0
# Templating placeholders and the faker namespace. Values are substituted from
# the incoming request (params/body/headers/query) and generated by faker.
info:
title: Templating and faker demo
version: 1.0.0
description: Shows every placeholder namespace defined in v0.1.
servers:
- name: main
type: http
operations:
- method: GET
path: /users/{id}
scenarios:
- name: rendered-user
response:
status: 200
headers:
Content-Type: application/json
X-Request-Id: "{{request.headers.X-Request-Id}}"
body:
# From the path parameter.
id: "{{params.id}}"
# From a query parameter, e.g. /users/42?fields=full
fields: "{{request.query.fields}}"
# Generated by faker.
name: "{{faker.person.fullName}}"
email: "{{faker.internet.email}}"
token: "{{faker.string.uuid}}"
createdAt: "{{faker.date.recent}}"
- method: POST
path: /echo
scenarios:
- name: echo-body
response:
status: 200
headers:
Content-Type: application/json
body:
# Reach into nested request body values with dotted paths.
receivedName: "{{request.body.user.name}}"
receivedRole: "{{request.body.user.role}}"
04-delays-errors.yml
Delays and errors
Latency simulation and error responses.
openmock: 0.2.0
# Response delays and author-declared error responses. Note the difference
# between a declared error (a scenario with a 4xx/5xx status) and OpenMock's
# own synthetic fallbacks (501 unmatched / 404 unrouted).
info:
title: Delays and errors
version: 1.0.0
description: Latency simulation and error responses.
servers:
- name: main
type: http
operations:
- method: GET
path: /orders/{id}
scenarios:
# Simulate a slow backend for a specific order.
- name: slow-order
when:
params:
id: "slow"
response:
status: 200
delay: 1500
headers:
Content-Type: application/json
body:
id: slow
status: processing
# Simulate an upstream failure for a specific order.
- name: failing-order
when:
params:
id: "boom"
response:
status: 503
delay: 250
headers:
Content-Type: application/json
Retry-After: "5"
body:
error: upstream unavailable
code: SERVICE_UNAVAILABLE
# Not-found error for anything else.
- name: not-found
response:
status: 404
headers:
Content-Type: application/json
body:
error: order not found
id: "{{params.id}}"
05-polling-calls.yml
File-analysis polling mock
Simulates an async analyze-then-poll API.
openmock: 0.2.0
# Stateful polling with the `calls` facet. The engine keeps a call counter per
# operation and concrete path, so /v1/file/abc and /v1/file/def advance
# independently. `calls: 1` is shorthand for {min: 1, max: 1}; bounds are
# inclusive and either may be omitted ({min: 5} = the 5th call onward).
#
# Manual flow this mocks:
# 1. GET /v1/file/{hash} -> 404 (file unknown, 1st call)
# 2. POST /v1/file/upload -> 202 (accepted for analysis)
# 3. GET /v1/file/{hash} -> 200 pending (calls 2-3)
# 4. GET /v1/file/{hash} -> 200 done (4th call onward, default scenario)
info:
title: File-analysis polling mock
version: 1.0.0
description: Simulates an async analyze-then-poll API.
servers:
- name: main
type: http
operations:
- method: POST
path: /v1/file/upload
scenarios:
- name: accepted
response:
status: 202
headers:
Content-Type: application/json
body:
status: queued
- method: GET
path: /v1/file/{hash}
scenarios:
- name: not-uploaded-yet
when:
calls: 1
response:
status: 404
headers:
Content-Type: application/json
body:
error: not_found
- name: analyzing
when:
calls:
min: 2
max: 3
response:
status: 200
headers:
Content-Type: application/json
body:
hash: "{{params.hash}}"
status: pending
# Default scenario: catches the 4th call and every one after it.
# This is how "repeat the final response forever" is written.
- name: done
response:
status: 200
headers:
Content-Type: application/json
body:
hash: "{{params.hash}}"
status: done
verdict: clean
06-grpc.yml
Order service gRPC mock
Unary calls, a streaming watch, errors, and calls-based polling.
openmock: 0.2.0
# gRPC mocks. Operations are addressed by service + rpc, declare their call
# type explicitly (unary | server-streaming), and match on metadata / message
# facets. Messages are native YAML following the proto3 JSON mapping (spec
# §4.2) — the mock is serveable without any .proto file.
info:
title: Order service gRPC mock
version: 1.0.0
description: Unary calls, a streaming watch, errors, and calls-based polling.
# The schema behind this mock lives in 06-grpc.proto; 06-grpc.binpb is the
# descriptor set compiled from it (see the .proto header for the protoc/buf
# commands). Attaching it here is OPTIONAL and never changes what the document
# means — engines MAY ignore it, and MAY use it to serve binary protobuf,
# server reflection, or message validation (spec §3.4).
servers:
- name: main
type: grpc
descriptorSet: ./06-grpc.binpb
# Extension keys are permitted on server objects too (spec §11);
# an engine that doesn't recognise this ignores it.
x-acme-reflection: false
operations:
# Unary RPC with metadata matching and an error scenario.
- service: shop.v1.OrderService
rpc: GetOrder
type: unary
scenarios:
- name: admin-view
when:
metadata:
x-role: admin
response:
status: OK
metadata:
x-cache: MISS
message:
id: "{{request.message.orderId}}"
state: SHIPPED
internalNotes: fragile — handle with care
- name: missing-order
when:
message:
orderId: "0"
response:
status: NOT_FOUND
error: order not found
- name: default-view
response:
status: OK
message:
id: "{{request.message.orderId}}"
state: SHIPPED
# Server-streaming RPC: messages are emitted in order.
- service: shop.v1.OrderService
rpc: WatchOrder
type: server-streaming
scenarios:
- name: lifecycle
response:
status: OK
messages:
- event: CREATED
orderId: "{{request.message.orderId}}"
- event: PAID
orderId: "{{request.message.orderId}}"
- event: SHIPPED
orderId: "{{request.message.orderId}}"
# A stream that fails mid-way: two events, then the stream terminates
# with a non-OK status.
- service: shop.v1.OrderService
rpc: WatchInventory
type: server-streaming
scenarios:
- name: interrupted
response:
status: UNAVAILABLE
error: inventory feed lost upstream connection
messages:
- level: "42"
- level: "41"
# The calls facet works on gRPC exactly as on HTTP; the counter is keyed by
# service + rpc (no path parameters), i.e. per operation.
- service: shop.v1.ReportService
rpc: GetReport
type: unary
scenarios:
- name: generating
when:
calls:
max: 2
response:
status: OK
message:
state: GENERATING
- name: ready
response:
status: OK
message:
state: READY
url: https://example.com/report.pdf
Generate the descriptor set (06-grpc.binpb)
# The gRPC server's `descriptorSet` (spec §3.4) is the compiled
# FileDescriptorSet the transport uses to decode/encode protobuf.
# Generate it from the .proto with protoc:
protoc \
--descriptor_set_out=06-grpc.binpb \
--include_imports \
06-grpc.proto
Protobuf schema (06-grpc.proto)
// The protobuf schema behind examples/06-grpc.yml.
//
// OpenMock documents never require a .proto file — messages are written as
// native YAML following the proto3 JSON mapping (spec §4.2). This file shows
// the schema that mock implements, and is the source for the descriptor set
// the document attaches via `grpc.descriptorSet` (spec §3.4):
//
// protoc --include_imports --descriptor_set_out=06-grpc.binpb 06-grpc.proto
// # or, with buf:
// buf build 06-grpc.proto -o 06-grpc.binpb
//
// Note how the proto3 JSON mapping shapes the YAML in 06-grpc.yml:
// - field names appear as their lowerCamelCase JSON names
// (order_id here → orderId in the document),
// - enum values appear by name (state: SHIPPED),
// - 64-bit integers appear as decimal strings (level: "42").
syntax = "proto3";
package shop.v1;
service OrderService {
rpc GetOrder(GetOrderRequest) returns (Order);
rpc WatchOrder(WatchOrderRequest) returns (stream OrderEvent);
rpc WatchInventory(WatchInventoryRequest) returns (stream InventoryLevel);
}
service ReportService {
rpc GetReport(GetReportRequest) returns (Report);
}
enum OrderState {
ORDER_STATE_UNSPECIFIED = 0;
CREATED = 1;
PAID = 2;
SHIPPED = 3;
}
message GetOrderRequest {
string order_id = 1;
}
message Order {
string id = 1;
OrderState state = 2;
string internal_notes = 3;
}
message WatchOrderRequest {
string order_id = 1;
}
message OrderEvent {
OrderState event = 1;
string order_id = 2;
}
message WatchInventoryRequest {}
message InventoryLevel {
int64 level = 1;
}
enum ReportState {
REPORT_STATE_UNSPECIFIED = 0;
GENERATING = 1;
READY = 2;
}
message GetReportRequest {}
message Report {
ReportState state = 1;
string url = 2;
}
07-graphql.yml
Order service GraphQL mock
Queries, a mutation, declared errors, and calls-based polling.
openmock: 0.2.0
# GraphQL mocks. Operations are addressed by operationType (query | mutation,
# query is the default) + operationName — the name a client gives its request
# document — and match on variables / headers facets. Responses carry `data`,
# `errors`, or both (a partial response); there is no status field, because
# GraphQL signals failure in-band (spec §7.4). The mock is serveable without
# any GraphQL schema.
info:
title: Order service GraphQL mock
version: 1.0.0
description: Queries, a mutation, declared errors, and calls-based polling.
# The type system behind this mock lives in 07-graphql.graphql (SDL).
# Attaching it here is OPTIONAL and never changes what the document means —
# engines MAY ignore it, and MAY use it to serve introspection, validate
# incoming query documents, or check response shapes (spec §3.5).
servers:
- name: main
type: graphql
schema: ./07-graphql.graphql
# Extension keys are permitted on server objects too (spec §11);
# an engine that doesn't recognise this ignores it.
x-acme-introspection: false
operations:
# A query with header matching and a declared error scenario.
# operationType defaults to query, but writing it is clearer.
- operationType: query
operationName: GetOrder
scenarios:
- name: admin-view
when:
headers:
x-role: admin
response:
data:
order:
id: "{{request.variables.orderId}}"
state: SHIPPED
internalNotes: fragile — handle with care
# A partial response: `data` holds what resolved (a null order) and
# `errors` explains what did not.
- name: missing-order
when:
variables:
orderId: "0"
response:
data:
order: null
errors:
- message: order not found
path: [order]
extensions:
code: NOT_FOUND
- name: default-view
response:
data:
order:
id: "{{request.variables.orderId}}"
state: SHIPPED
# A mutation. Routing considers operationType, so a mutation named like a
# query would still resolve to the right operation.
- operationType: mutation
operationName: CreateOrder
scenarios:
- name: created
response:
data:
createOrder:
id: "{{faker.string.uuid}}"
state: CREATED
note: "{{request.variables.input.note}}"
# The calls facet works on GraphQL exactly as on HTTP and gRPC; the counter
# is keyed by operationType + operationName, i.e. per operation.
- operationType: query
operationName: GetReport
scenarios:
- name: generating
when:
calls:
max: 2
response:
data:
report:
state: GENERATING
- name: ready
response:
data:
report:
state: READY
url: https://example.com/report.pdf
GraphQL schema (07-graphql.graphql)
# The GraphQL type system behind examples/07-graphql.yml.
#
# OpenMock documents never require a GraphQL schema — data, errors, and
# variables are written as native YAML (spec §4.3, §7.4). This file shows the
# schema that mock implements, and is what the document attaches via
# `graphql.schema` (spec §3.5). Engines MAY ignore it, and MAY use it to serve
# introspection, validate incoming query documents, or check response shapes.
#
# Note that the operationName each mock operation declares (GetOrder,
# CreateOrder, GetReport) is the name a *client* gives its request document —
# it does not appear in this schema. The root fields the clients select
# (order, createOrder, report) do.
type Query {
order(id: ID!): Order
report: Report!
}
type Mutation {
createOrder(input: CreateOrderInput!): Order!
}
enum OrderState {
CREATED
PAID
SHIPPED
}
type Order {
id: ID!
state: OrderState!
internalNotes: String
note: String
}
input CreateOrderInput {
note: String
}
enum ReportState {
GENERATING
READY
}
type Report {
state: ReportState!
url: String
}
08-websocket.yml
Order feed WebSocket mock
A scripted feed — subscribe/ack, heartbeats, a close, and a per-connection welcome.
openmock: 0.2.0
# WebSocket mocks. Operations are addressed by the connection path (with
# {param} segments, as in HTTP); every message the client sends resolves
# against the operation's scenarios to a reply of zero or more messages,
# optionally followed by a close (spec §5.8, §7.5). v0.1 covers
# client-initiated exchanges only — the mock speaks only in reply.
info:
title: Order feed WebSocket mock
version: 1.0.0
description: A scripted feed — subscribe/ack, heartbeats, a close, and a per-connection welcome.
servers:
- name: main
type: websocket
operations:
# Clients connect to /ws/orders/{id} and exchange JSON messages. The
# connection-scoped facets (params, query, headers) match the values fixed
# at establishment; message matches each inbound message.
- path: /ws/orders/{id}
scenarios:
# The calls counter is keyed per (operation, connection), so every
# connection's first message — whatever it is — is answered with a
# welcome. Placed first so nothing below can consume call 1.
- name: welcome
when:
calls: 1
response:
messages:
- type: welcome
orderId: "{{params.id}}"
# Heartbeats are deliberately ignored: an empty messages list replies
# with nothing and keeps the connection open.
- name: heartbeat
when:
message:
type: ping
response:
messages: []
# Admin connections (matched on the upgrade request's headers) get the
# internal view.
- name: admin-status
when:
headers:
x-role: admin
message:
type: status
response:
messages:
- type: status
orderId: "{{params.id}}"
state: SHIPPED
internalNotes: fragile — handle with care
- name: status
when:
message:
type: status
response:
messages:
- type: status
orderId: "{{params.id}}"
state: SHIPPED
# Reply, then close the connection normally.
- name: bye
when:
message:
type: bye
response:
messages:
- type: goodbye
close:
code: 1000
reason: client requested close
# Default: an application-shaped error reply. WebSocket has no
# per-message status; failures are in-band messages or a close with an
# application code (4000–4999).
- name: unknown-command
response:
messages:
- type: error
error: unknown command
received: "{{request.message.type}}"