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.