Skip to content

Output Adapters ​

Adapters receive events one at a time during generation and route them to their destination. Synode ships 7 built-in adapters.

Adapter Summary ​

AdapterUse CaseConstructor
ConsoleAdapterDebugging, quick inspectionnew ConsoleAdapter()
InMemoryAdapterTesting, assertions, dry runsnew InMemoryAdapter()
FileAdapterLocal file output (JSONL, JSON, CSV)new FileAdapter({ path, format })
HttpAdapterWebhook/API ingestion with batchingnew HttpAdapter({ url, batchSize })
CallbackAdapterCustom per-event logicnew CallbackAdapter(fn)
CompositeAdapterFan-out to multiple adaptersnew CompositeAdapter([...adapters])
StreamAdapterNode.js Writable streamsnew StreamAdapter(stream)

ConsoleAdapter ​

Prints each event as pretty-printed JSON to stdout. Default adapter when none is specified.

ts
import { generate, ConsoleAdapter } from '@synode/core';

await generate(journey, {
  users: 10,
  adapter: new ConsoleAdapter(),
});

InMemoryAdapter ​

Stores events in an array. Access via adapter.events. Call adapter.clear() to reset.

ts
import { generate, InMemoryAdapter } from '@synode/core';

const adapter = new InMemoryAdapter();
await generate(journey, { users: 50, adapter });

console.log(adapter.events.length); // total event count
console.log(adapter.events[0].name); // first event name
adapter.clear(); // reset for next run

FileAdapter ​

Writes events to disk. Supports three formats and optional daily partitioning.

ts
import { generate } from '@synode/core';
import { FileAdapter } from '@synode/adapter-file';

// JSONL: one JSON object per line, appended per event
const jsonl = new FileAdapter({ path: './out/events.jsonl', format: 'jsonl' });

// JSON: buffered array, written on close
const json = new FileAdapter({ path: './out/events.json', format: 'json' });

// CSV: header + rows, appended per event
const csv = new FileAdapter({ path: './out/events.csv', format: 'csv' });

await generate(journey, { users: 1000, adapter: jsonl });

Daily Partitioning ​

Split output into date-based files using the event timestamp.

ts
const adapter = new FileAdapter({
  path: './out', // base directory for partitioned files
  format: 'csv',
  partition: 'daily',
  filePattern: 'events-{date}.{ext}', // default pattern
});
// Creates: ./out/events-2026-01-15.csv, ./out/events-2026-01-16.csv, etc.

Options: path (required), format ('jsonl'|'json'|'csv', required), partition ('daily'|'none', default 'none'), filePattern (template with {date} and {ext}, default 'events-{date}.{ext}').

HttpAdapter ​

Sends events to an HTTP endpoint with batching, flush intervals, and retry with exponential backoff.

ts
import { generate } from '@synode/core';
import { HttpAdapter } from '@synode/adapter-http';

const adapter = new HttpAdapter({
  url: 'https://api.example.com/ingest',
  batchSize: 50,
  flushInterval: 3000,
  maxRetries: 3,
  headers: { Authorization: 'Bearer token-123' },
});

await generate(journey, { users: 5000, adapter });
await adapter.close(); // flush remaining buffer

Options: method ('POST'|'PUT', default 'POST'), headers, batchSize (default 1), flushInterval (ms, default 5000), maxRetries (default 3), transform (custom body shape function). Retries with exponential backoff on 5xx/429.

CallbackAdapter ​

Invokes a function for each event. Supports sync and async callbacks.

ts
import { generate, CallbackAdapter } from '@synode/core';

const adapter = new CallbackAdapter(async (event) => {
  await db.insert('events', { name: event.name, userId: event.userId, payload: event.payload });
});
await generate(journey, { users: 100, adapter });

CompositeAdapter ​

Fans out each event to multiple child adapters in parallel. Calls close() on all children.

ts
import { generate, ConsoleAdapter } from '@synode/core';
import { FileAdapter } from '@synode/adapter-file';
import { HttpAdapter } from '@synode/adapter-http';
import { CompositeAdapter } from '@synode/adapter-composite';

const adapter = new CompositeAdapter([
  new ConsoleAdapter(),
  new FileAdapter({ path: './out/events.jsonl', format: 'jsonl' }),
  new HttpAdapter({ url: 'https://api.example.com/ingest', batchSize: 50 }),
]);

await generate(journey, { users: 1000, adapter });
await adapter.close(); // closes all children

StreamAdapter ​

Writes events to any Node.js Writable stream. Default format jsonl streams per event; json buffers and writes on close.

ts
import { createWriteStream } from 'node:fs';
import { generate } from '@synode/core';
import { StreamAdapter } from '@synode/adapter-stream';

const stream = createWriteStream('./events.jsonl');
const adapter = new StreamAdapter(stream);
await generate(journey, { users: 500, adapter });
await adapter.close();

close() Lifecycle ​

Adapters with buffered state (FileAdapter JSON mode, HttpAdapter, StreamAdapter) require close() to flush. generate calls close() automatically after all users are processed. If using an adapter outside generate, call close() manually.