# Behaviors and Dispatch — Developers

**Audience:** Engineers defining endpoint behaviors and calling `dispatch`.  
**Related:** [Overview](../00-overview/developers/BOOK.md) · [Adapters](../02-adapters/developers/BOOK.md)

---

## 1. Fixed

Use fixed behavior for health endpoints, stable metadata, constants, and simple fixtures.

```ts
{
  id: 'capabilities',
  method: 'GET',
  path: '/capabilities',
  behavior: {
    type: 'fixed',
    response: {
      status: 200,
      headers: { 'x-simulator': 'true' },
      body: { features: ['read', 'search'] }
    }
  }
}
```

---

## 2. Relative

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}}`
- Array indexes, such as `{{data.users[0].id}}`

```ts
{
  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:

```ts
{ 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:

```ts
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.

---

## 3. Simulation function

Use a simulation function when output needs conditions, calculations, validation, generated values, branching, or error responses.

```ts
{
  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
        }
      };
    }
  }
}
```

---

## 4. Dispatch

```ts
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.

---

## 5. Endpoint contract

```ts
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.

---

## 6. Artificial latency

```ts
{
  id: 'slow-search',
  method: 'GET',
  path: '/search',
  delayMs: 750,
  behavior: {
    type: 'fixed',
    response: { body: { items: [] } }
  }
}
```

---

## 7. Raw responses

```ts
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`.

---

## 8. Path patterns

Supports `:id`, `:id(\\d+)`, and optional `:slug?` segments. Static routes are matched before parameter routes, so `/users/me` wins over `/users/:id`.
