Spec0docs
Mock serverConcepts

CEL Expressions

**CEL (Common Expression Language)** lets you write dynamic response logic that is evaluated at request time rather than saved as a static body. With CEL you can:

CEL (Common Expression Language) lets you write dynamic response logic that is evaluated at request time rather than saved as a static body. With CEL you can:

  • Return different responses based on path parameters, query parameters, or headers
  • Generate random UUIDs or timestamps in every response
  • Simulate conditional logic (e.g., return 404 for unknown IDs)

CEL is sandboxed and Turing-incomplete, making it safe to execute on the server.

Contents


Creating a CEL variant

From the UI

  1. Go to Endpoints → expand an operation → Add Variant.
  2. Switch to the CEL tab.
  3. Write your expression in the editor.
  4. Save. CEL variants are marked with a sparkle badge in the variant list.

From the API

Set celExpression instead of (or in addition to) responseBody:

curl -X POST http://localhost:8080/mock-server/servers/{mockServerId}/variants \
  -H 'Content-Type: application/json' \
  -d '{
    "operationId": "getUser",
    "responseName": "Dynamic",
    "statusCode": "200",
    "celExpression": "request.path_params.id == '\''99'\'' ? {'\''status'\'': 404, '\''body'\'': {'\''error'\'': '\''not found'\''}} : {'\''status'\'': 200, '\''body'\'': {'\''id'\'': request.path_params.id}}"
  }'

Expression context

Every CEL expression receives a request object and an env map.

request fields

VariableTypeDescriptionExample
request.methodstringHTTP method"GET", "POST"
request.pathstringFull request path"/users/123"
request.path_paramsmap<string, string>Named path parametersrequest.path_params.id
request.query_paramsmap<string, string>Query string parametersrequest.query_params.filter
request.headersmap<string, string>Request headers (keys lowercased)request.headers["x-session-id"]
request.bodymap or nullParsed JSON request bodyrequest.body.name

env variables

Per-server environment variables are accessible as env.<KEY>. Define them in Settings → Environment Variables.

env.REGION          // e.g. "us-east-1"
env.API_VERSION     // e.g. "v2"

Built-in functions

FunctionReturnsDescription
uuid()stringA random UUID v4
now()stringCurrent timestamp as ISO 8601
randomInt(min, max)intRandom integer in [min, max)

Return value

[!IMPORTANT] Your expression must return a map with single-quoted string keys. Unquoted identifiers are treated as variable references and will fail if the variable is not defined.

{
  'status':  <int>,                  // required — HTTP status code
  'body':    <any>,                  // optional — response body
  'headers': <map<string, string>>   // optional — extra response headers
}

Examples

Branch on a path parameter

request.path_params.id == "99"
  ? {'status': 404, 'body': {'error': 'user not found'}}
  : {'status': 200, 'body': {'id': request.path_params.id, 'name': 'Alice'}}

Generated ID and timestamp on every response

{
  'status': 201,
  'body': {
    'id': uuid(),
    'createdAt': now(),
    'name': request.body.name
  }
}

Branch on a query parameter

request.query_params.status == "inactive"
  ? {'status': 200, 'body': {'active': false, 'reason': 'account suspended'}}
  : {'status': 200, 'body': {'active': true}}

Echo back a request header

{
  'status': 200,
  'body': {'sessionId': request.headers["x-session-id"], 'ok': true}
}

Add custom response headers

{
  'status': 200,
  'body': {'result': 'ok'},
  'headers': {'X-Request-Id': uuid(), 'X-Served-By': 'spec0-mock'}
}

Use a server-level environment variable

{
  'status': 200,
  'body': {'region': env.REGION, 'id': request.path_params.id}
}

Common mistakes

[!WARNING] request.body is null for GET requests and other bodyless methods. Guard against it before accessing fields:

request.body != null ? request.body.userId : 'anonymous'

[!WARNING] Path parameters are always strings. Compare with string literals, not integers:

request.path_params.id == "42"   // correct
request.path_params.id == 42     // wrong — type mismatch, will always be false

[!NOTE] CEL variants bypass response-body schema validation at save time. Validation only applies to static variants. The body returned by a CEL expression is not checked against the OpenAPI schema.


See also

On this page