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
Define basic information about your service:
Field Type Required Description namestring Yes Service name displayed in logs and UI versionstring No Semantic version (e.g., “1.0”, “2.1.0”) descriptionstring No Human-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
Server configuration options
Field Type Default Description portinteger Required Port number for the HTTP server base_pathstring /Base path prepended to all endpoints cors.enabledboolean falseEnable Cross-Origin Resource Sharing cors.originsarray ["*"]Allowed origins for CORS requests cors.methodsarray All methods Allowed HTTP methods cors.headersarray All headers Allowed request headers cors.credentialsboolean falseAllow 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
Complete endpoint configuration reference
Field Type Required Description methodstring Yes HTTP method: GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS pathstring Yes URL path, must start with /. Supports parameters: /users/{id} descriptionstring No Human-readable description of what this endpoint does parametersarray No Query, path, or header parameters request_bodyobject No Request body requirements and schema responsesobject Yes Map of status codes to response definitions kindstring No Endpoint type: http, websocket, or sse (default: http) header_matchobject No Headers that must match for this endpoint to trigger streamobject No Streaming 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
Field Type Required Description content_typestring Yes MIME type (e.g., application/json, text/html) bodystring Yes Response body template (supports Handlebars) conditionstring No Template expression that must evaluate to true headersobject No Additional response headers schemastring No Reference to a model for validation side_effectsarray No Actions 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.
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
Behavior configuration options
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:
Use descriptive names : Choose clear, meaningful names for services and endpoints
Add descriptions : Document what each endpoint does
Organize fixtures logically : Group related data together
Use semantic versioning : Version your service definitions
Keep it DRY : Use fixtures and models to avoid repetition
Test your templates : Ensure Handlebars expressions work as expected
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