Ventyd Logo

Database Adapter

Connect to your database

Adapter Interface

Connect Ventyd to any database by implementing a simple adapter interface.

interface Adapter {
  // Load all events for an entity
  getEventsByEntityId(params: {
    entityName: string;
    entityId: string;
  }): Promise<Event[]>;

  // Save new events atomically
  commitEvents(params: {
    events: Event[];
  }): Promise<void>;
}
import type { Adapter } from 'ventyd';

const myAdapter: Adapter = {
  // ...
}

Implementation Requirements

Make sure your adapter follows these guidelines for reliable event sourcing:

getEventsByEntityId()

  • Return events in chronological order (oldest first)
  • Return empty array if entity doesn't exist

commitEvents()

  • Save all events atomically (all or nothing)
  • Handle empty array gracefully
  • Make it idempotent if possible (use unique constraints on eventId)

Example Implementation

Here's a simple adapter using a generic database client:

import { createRepository } from 'ventyd';
import type { Adapter } from 'ventyd';

const adapter: Adapter = {
  async getEventsByEntityId({ entityName, entityId }) {
    // Load events from your database
    return await db.query(
      'SELECT * FROM events WHERE entity_name = ? AND entity_id = ? ORDER BY created_at ASC',
      [entityName, entityId]
    );
  },

  async commitEvents({ events }) {
    if (events.length === 0) return;

    // Save events atomically to your database
    await db.insertMany(events);
  }
};

const repository = createRepository(User, { adapter });

Best Practices

Connection Pooling

Always use connection pooling for production databases to handle concurrent requests efficiently.

Create Indexes

Index on (entityName, entityId) for fast event lookups.

Handle Errors

Catch duplicate key errors to make operations idempotent.

Use Transactions

Use database transactions in commitEvents() to ensure atomicity.


Implement the adapter for your database of choice (MongoDB, PostgreSQL, MySQL, Redis, etc.) and you're ready to go!

In production, commitEvents() often needs to do more than just insert events — updating a read model, saving snapshots, and handling concurrent writes all need to happen atomically. When these operations aren't in one transaction, a crash between steps can leave your read model out of sync with your event log. See Transactions for how to handle this.

On this page