Skip to main content
Service definitions are YAML files that describe your mock API’s structure, behavior, and data. They’re the foundation of Apicentric and allow you to create realistic API mocks without writing code.

Basic structure

Every service definition contains these top-level fields:
name: Tasks API
version: "1.0"
description: Simple task management API for demonstration
server:
  port: 9001
  base_path: /api/v1
  cors:
    enabled: true
    origins: ["*"]

fixtures:
  # Your test data here

endpoints:
  # Your API endpoints here

scenarios:
  # Optional scenarios for different states

behavior:
  # Optional behavior configuration

Configuration reference

Metadata

Define basic information about your service:
FieldTypeRequiredDescription
namestringYesService name displayed in logs and UI
versionstringNoSemantic version (e.g., “1.0”, “2.1.0”)
descriptionstringNoHuman-readable description of the service
name: User Management API
version: "2.1"
description: REST API for managing user accounts and profiles

Server configuration

The server section controls how your mock API runs:
server:
  port: 9001
  base_path: /api/v1
  cors:
    enabled: true
    origins: ["http://localhost:3000", "https://app.example.com"]
    methods: ["GET", "POST", "PUT", "DELETE"]
    headers: ["Content-Type", "Authorization"]
    credentials: true
FieldTypeDefaultDescription
portintegerRequiredPort number for the HTTP server
base_pathstring/Base path prepended to all endpoints
cors.enabledbooleanfalseEnable Cross-Origin Resource Sharing
cors.originsarray["*"]Allowed origins for CORS requests
cors.methodsarrayAll methodsAllowed HTTP methods
cors.headersarrayAll headersAllowed request headers
cors.credentialsbooleanfalseAllow credentials in CORS requests
If you don’t specify a port, Apicentric will automatically assign one from the configured port range (default: 8000-8999).

Endpoints

Endpoints define the API routes your service responds to. Each endpoint specifies the HTTP method, path, parameters, and responses.
endpoints:
  - method: GET
    path: /tasks
    description: Get all tasks with optional filtering
    parameters:
      - name: completed
        in: query
        required: false
        type: boolean
    responses:
      200:
        content_type: application/json
        body: |
          {
            "tasks": [
              {{#each fixtures.tasks}}
              {
                "id": {{id}},
                "title": "{{title}}",
                "completed": {{completed}}
              }{{#unless @last}},{{/unless}}
              {{/each}}
            ]
          }

Endpoint fields

FieldTypeRequiredDescription
methodstringYesHTTP method: GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS
pathstringYesURL path, must start with /. Supports parameters: /users/{id}
descriptionstringNoHuman-readable description of what this endpoint does
parametersarrayNoQuery, path, or header parameters
request_bodyobjectNoRequest body requirements and schema
responsesobjectYesMap of status codes to response definitions
kindstringNoEndpoint type: http, websocket, or sse (default: http)
header_matchobjectNoHeaders that must match for this endpoint to trigger
streamobjectNoStreaming configuration for WebSocket/SSE endpoints

Path parameters

Use curly braces to define dynamic path segments:
endpoints:
  - method: GET
    path: /users/{id}
    responses:
      200:
        content_type: application/json
        body: |
          {
            "id": {{params.id}},
            "username": "user_{{params.id}}"
          }
You can access path parameters in templates using {{params.parameterName}}.

Query parameters

Define expected query parameters:
parameters:
  - name: category
    in: query
    required: false
    type: string
    description: Filter products by category
  - name: page
    in: query
    required: false
    type: integer
  - name: limit
    in: query
    required: false
    type: integer
Access query parameters in templates: {{request.query.category}}

Request body

Specify request body requirements:
request_body:
  required: true
  content_type: application/json
  schema: CreateUserRequest  # Reference to a model definition
Access request body in templates: {{request.body.fieldName}}

Responses

Each endpoint must define at least one response. Responses are keyed by HTTP status code:
responses:
  200:
    content_type: application/json
    body: |
      {
        "success": true,
        "data": "{{request.body.title}}"
      }
  400:
    condition: "{{not request.body.title}}"
    content_type: application/json
    body: |
      {
        "error": "Validation failed",
        "message": "Title is required"
      }
  404:
    content_type: application/json
    body: |
      {
        "error": "Not found"
      }

Response fields

FieldTypeRequiredDescription
content_typestringYesMIME type (e.g., application/json, text/html)
bodystringYesResponse body template (supports Handlebars)
conditionstringNoTemplate expression that must evaluate to true
headersobjectNoAdditional response headers
schemastringNoReference to a model for validation
side_effectsarrayNoActions to perform when this response is returned

Conditional responses

Use the condition field to return different responses based on request data:
responses:
  200:
    condition: "{{eq params.id '1'}}"
    content_type: application/json
    body: |
      {"id": 1, "name": "Alice"}
  200:
    condition: "{{eq params.id '2'}}"
    content_type: application/json
    body: |
      {"id": 2, "name": "Bob"}
  404:
    condition: "{{not (or (eq params.id '1') (eq params.id '2'))}}"
    content_type: application/json
    body: |
      {"error": "User not found"}
The simulator evaluates conditions in the order they appear. The first matching response is returned.

Custom headers

Add custom headers to responses:
responses:
  200:
    content_type: application/json
    headers:
      X-RateLimit-Remaining: "100"
      X-Custom-Header: "{{request.headers.x-request-id}}"
    body: |
      {"status": "ok"}

Side effects

Modify service state when a response is returned:
responses:
  201:
    content_type: application/json
    body: |
      {"id": {{faker "datatype.number"}}, "created": true}
    side_effects:
      - action: "set"
        target: "bucket.lastCreated"
        value: "{{request.body.title}}"

Models

Define reusable data schemas using JSON Schema:
models:
  User:
    type: object
    required: ["username", "email"]
    properties:
      id:
        type: integer
      username:
        type: string
        minLength: 3
      email:
        type: string
        format: email
      role:
        type: string
        enum: ["admin", "user", "guest"]
Reference models in request bodies and responses:
request_body:
  schema: User

Behavior

Configure global behavior for your service:
behavior:
  latency:
    min_ms: 100
    max_ms: 300
  error_simulation:
    enabled: false
    rate: 0.05  # 5% error rate
    status_codes: [500, 502, 503]
  rate_limiting:
    enabled: false
    requests_per_minute: 60

Latency simulation

Add realistic delays to responses:
latency:
  min_ms: 50    # Minimum delay in milliseconds
  max_ms: 200   # Maximum delay in milliseconds
The simulator adds a random delay between min and max for each request.

Error simulation

Randomly return error responses:
error_simulation:
  enabled: true
  rate: 0.1              # 10% of requests fail
  status_codes: [500, 503]  # Randomly chosen from this list

Rate limiting

Limit the number of requests:
rate_limiting:
  enabled: true
  requests_per_minute: 100
Requests exceeding the limit receive a 429 status code.

Complete example

Here’s a complete service definition for a task management API:
name: Tasks API
version: "1.0"
description: Simple task management API for demonstration

server:
  port: 9001
  base_path: /api/v1
  cors:
    enabled: true
    origins: ["*"]

fixtures:
  tasks:
    - id: 1
      title: "Setup development environment"
      completed: false
      created_at: "2024-01-15T10:00:00Z"
    - id: 2
      title: "Write unit tests"
      completed: true
      created_at: "2024-01-14T15:30:00Z"

endpoints:
  - method: GET
    path: /tasks
    description: Get all tasks
    responses:
      200:
        content_type: application/json
        body: |
          {
            "tasks": [
              {{#each fixtures.tasks}}
              {
                "id": {{id}},
                "title": "{{title}}",
                "completed": {{completed}},
                "created_at": "{{created_at}}"
              }{{#unless @last}},{{/unless}}
              {{/each}}
            ],
            "total": {{fixtures.tasks.length}}
          }

  - method: GET
    path: /tasks/{id}
    description: Get a specific task by ID
    responses:
      200:
        condition: "{{eq params.id '1'}}"
        content_type: application/json
        body: |
          {
            "id": 1,
            "title": "Setup development environment",
            "completed": false
          }
      404:
        condition: "{{ne params.id '1'}}"
        content_type: application/json
        body: |
          {"error": "Task not found"}

  - method: POST
    path: /tasks
    description: Create a new task
    request_body:
      required: true
      content_type: application/json
    responses:
      201:
        condition: "{{request.body.title}}"
        content_type: application/json
        body: |
          {
            "id": {{faker "datatype.number" min=100 max=999}},
            "title": "{{request.body.title}}",
            "completed": false,
            "created_at": "{{now}}"
          }
      400:
        condition: "{{not request.body.title}}"
        content_type: application/json
        body: |
          {
            "error": "Validation failed",
            "message": "Title is required"
          }

behavior:
  latency:
    min_ms: 100
    max_ms: 300

Validation

Apicentric validates your service definition when loading it. Common validation errors:
Common validation errors:
  • Path must start with /: Ensure all endpoint paths begin with a forward slash
  • Invalid HTTP method: Use standard methods (GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS)
  • Missing responses: Every endpoint must have at least one response definition
  • Invalid status code: Use standard HTTP status codes (200, 201, 400, 404, 500, etc.)
You can validate a service definition without starting the simulator:
apicentric simulator validate --file tasks-api.yaml

Best practices

Follow these best practices for maintainable service definitions:
  1. Use descriptive names: Choose clear, meaningful names for services and endpoints
  2. Add descriptions: Document what each endpoint does
  3. Organize fixtures logically: Group related data together
  4. Use semantic versioning: Version your service definitions
  5. Keep it DRY: Use fixtures and models to avoid repetition
  6. Test your templates: Ensure Handlebars expressions work as expected
  7. Version control: Store service definitions in Git

Next steps

Fixtures and templating

Learn how to create dynamic responses with Handlebars

Scenarios

Simulate different API states and conditions

Build docs developers (and LLMs) love