CASE FILE · SIM-01
CASE FILE · SIM-01
Use fixed behavior for health endpoints, stable metadata, constants, and simple fixtures.
{
id: 'capabilities',
method: 'GET',
path: '/capabilities',
behavior: {
type: 'fixed',
response: {
status: 200,
headers: { 'x-simulator': 'true' },
body: { features: ['read', 'search'] }
}
}
}
CASE FILE · SIM-01
Relative behavior starts with configured JSON, then resolves tokens against the matched request and the API's internal data object.
Supported token roots:
{{request.params.parentId}}{{request.query.page}}{{request.headers.x-tenant-id}}{{request.body.customer.id}}{{data.children}}{{data.users[0].id}}{
id: 'children',
method: 'GET',
path: '/parents/:parentId/children',
behavior: {
type: 'relative',
response: {
body: {
parentId: '{{request.params.parentId}}',
items: '{{data.children}}'
}
}
}
}
A token occupying the entire string preserves its original type. Therefore, {{request.body.limit}} can produce a number and {{data.children}} can produce an array. A token embedded inside text is converted to a string:
{ message: 'Children for parent {{request.params.parentId}}' }
Tokens inside API-local data are rendered too. This lets a reusable internal fixture inherit an identifier from the incoming request:
data: {
children: [
{ id: 'child-1', parentId: '{{request.params.parentId}}' }
]
}
Relative behavior is strict by default. A missing token throws TemplateResolutionError. Set strict: false to leave unresolved tokens unchanged.
CASE FILE · SIM-01
Use a simulation function when output needs conditions, calculations, validation, generated values, branching, or error responses.
{
id: 'quote',
method: 'POST',
path: '/quote',
behavior: {
type: 'simulation',
handler: async ({ request, data, apiId, endpointId }) => {
const quantity = Number((request.body as { quantity: number }).quantity);
const unitPrice = Number(data.unitPrice);
return {
status: 201,
body: {
apiId,
endpointId,
quantity,
unitPrice,
total: quantity * unitPrice
}
};
}
}
}
CASE FILE · SIM-01
const result = await simulator.dispatch({
method: 'GET',
path: '/api/v1/parents/parent-42/children'
});
console.log(result.response.body);
dispatch() throws EndpointNotFoundError when nothing matches. tryDispatch() returns undefined instead.
CASE FILE · SIM-01
type EndpointDefinition = {
id: string;
method: HttpMethod;
path: string;
enabled?: boolean;
delayMs?: number;
behavior:
| { type: 'fixed'; response: SimulatorResponse }
| { type: 'relative'; response: SimulatorResponse; strict?: boolean }
| { type: 'simulation'; handler: SimulationFunction };
};
The discriminated behavior union is the core guarantee: an endpoint cannot validly have two behaviors, and it cannot omit its behavior.
CASE FILE · SIM-01
{
id: 'slow-search',
method: 'GET',
path: '/search',
delayMs: 750,
behavior: {
type: 'fixed',
response: { body: { items: [] } }
}
}
CASE FILE · SIM-01
import { RawResponseBody, createApiSimulator } from '@x12i/api-simulator';
// In a simulation handler:
return {
body: new RawResponseBody('<h1>hello</h1>', 'text/html; charset=utf-8')
};
Transport adapters write RawResponseBody verbatim instead of JSON.stringify.
CASE FILE · SIM-01
Supports :id, :id(\\d+), and optional :slug? segments. Static routes are matched before parameter routes, so /users/me wins over /users/:id.