# Adapters and Helpers — Developers

**Audience:** Engineers exposing simulators over HTTP and composing helpers.  
**Related:** [Behaviors](../01-behaviors/developers/BOOK.md) · [Package simulators](../03-package-simulators/developers/BOOK.md)

---

## 1. Node HTTP adapter

```ts
import { createServer } from 'node:http';
import { createNodeHttpHandler } from '@x12i/api-simulator/node';

createServer(createNodeHttpHandler(simulator)).listen(5520);
```

The adapter:

- parses URL path and query values;
- normalizes request headers to lowercase keys (values may be `string | string[]`);
- parses JSON, urlencoded, and text bodies by default; returns `Buffer` for binary;
- returns `404` when no simulated endpoint matches;
- returns `500` when rendering or simulation fails;
- limits request bodies to 1 MiB by default;
- writes `RawResponseBody` verbatim (HTML/XML/CSV/binary).

The `./node` adapter is **transport-only**. It does not bind ports, serve `/_live`, or print a compliance banner. Processes that **listen on HTTP** should wrap it with `@x12i/core-service` (next section).

---

## 2. HTTP process compliance (local services)

Any example or package simulator that **binds an HTTP port** should use [`@x12i/core-service`](https://www.npmjs.com/package/@x12i/core-service) (pulls `@x12i/api-live-view`; uses `@x12i/ports-manager`).

Product contract (current **`@x12i/memorix-docs`**, not old memorix-ebooks):

- Use case **`http-process-compliance`**
- Building Services (developers) → **HTTP process compliance (local services)**
- Deep reference: package READMEs for `core-service`, `ports-manager`, `api-live-view`

This repo applies that contract in zone **`api-simulator` (5520–5539)** — not Memorix’s 5100–5119.

| Surface | Role |
|--------|------|
| Port from zone map | `scripts/api-simulator-ports.mjs` / `createSimulatorHttp` |
| `GET /health` | Process liveness |
| `/_live` | In-process request rings (api-live-view) |
| `x-correlation-id` | Request/response correlation |
| Startup banner | origin · health · live · docs |

Shared helper:

```js
import { createSimulatorHttp } from '../../scripts/create-simulator-http.mjs';

const { core, announce } = createSimulatorHttp({
  id: 'my-simulator',
  title: 'My package simulator',
  portKey: 'api', // or flowstate / libraryDemo
  portEnv: 'API_SIMULATOR_API_PORT'
});

const server = createServer(async (req, res) => {
  if (await core.tryHandleCore(req, res)) return;
  await createNodeHttpHandler(simulator)(req, res);
});

await announce(server);
```

Published `@x12i/api-simulator` stays **zero runtime dependencies**. Remote *clients* do not install these kits; *servers that bind* do.

---

## 3. Multiple APIs

A single simulator can expose many APIs. Each API has its own `id`, optional `basePath`, internal `data`, and endpoints.

```ts
const simulator = createApiSimulator({
  apis: [crmApi, billingApi, identityApi]
});
```

Duplicate `METHOD + full path` routes are rejected during startup. Structurally ambiguous routes such as `/users/:id` and `/users/:name` are also rejected. Static routes are matched before parameter routes.

---

## 4. In-memory store

```ts
import { createStore } from '@x12i/api-simulator/store';

const users = createStore([{ id: '1', name: 'Ada' }]);
users.create({ name: 'Grace' });
users.list({ name: 'Ada' });
users.reset(); // restore seed — useful between tests
```

---

## 5. OpenAPI skeletons

```ts
import { openapiToApiDefinition } from '@x12i/api-simulator/openapi';

const api = openapiToApiDefinition(parsedOpenApiObject, { id: 'pets-api', basePath: '/v1' });
```

Pass an already-parsed JS object (parse YAML yourself if needed).

---

## 6. Fetch adapter

```ts
import { createFetchHandler } from '@x12i/api-simulator/fetch';

export default {
  fetch: createFetchHandler(simulator)
};
```

---

## 7. Express and Fastify

```ts
// Express
app.use(async (req, res) => {
  const match = await simulator.tryDispatch({
    method: req.method,
    path: req.path,
    headers: req.headers,
    query: req.query,
    body: req.body
  });
  if (!match) return res.status(404).json({ error: 'NOT_FOUND' });
  res.status(match.response.status ?? 200).set(match.response.headers ?? {}).send(match.response.body);
});
```

---

## 8. Auth and rate limits

```ts
import { requireBearerToken, rateLimit } from '@x12i/api-simulator/helpers';

handler: rateLimit({ windowMs: 60_000, max: 30 })(
  requireBearerToken((token) => token === 'secret')(
    ({ request }) => ({ body: { ok: true, path: request.path } })
  )
)
```

---

## 9. CORS host and validators

```ts
{
  id: 'api',
  host: 'tenant.example.com',
  cors: { origins: '*', methods: ['GET', 'POST', 'OPTIONS'] },
  endpoints: [{
    id: 'create',
    method: 'POST',
    path: '/items',
    validate: ({ request }) => {
      if (!request.body) return { status: 400, body: { error: 'EMPTY' } };
    },
    behavior: { type: 'fixed', response: { status: 201, body: { ok: true } } }
  }]
}
```

---

## 10. Record and replay

```bash
node scripts/record-replay.mjs --base https://httpbin.org --paths /get,/uuid --out captured.json
```

---

## 11. Node body parsers

Built-in parsers: JSON, `application/x-www-form-urlencoded`, `text/plain`. Binary bodies are returned as `Buffer`. Plug in multipart yourself:

```ts
createNodeHttpHandler(simulator, {
  bodyParsers: {
    'multipart/form-data': async (raw, contentType) => {
      // use busboy / your parser of choice
      return { rawLength: raw.length, contentType };
    }
  }
});
```

---

## 12. Simulator options

```ts
const simulator = createApiSimulator(definition, {
  recordHistory: { limit: 100 },
  validateTemplates: true // checks {{data.*}} at construction
});

simulator.getHistory();
simulator.clearHistory();
```
