Skip to main content

EventIngestionProvider

The EventIngestionProvider interface defines the contract for storing and querying authentication events.
interface EventIngestionProvider {
  ingest(event: AuthEvent): Promise<void>;
  ingestBatch?(events: AuthEvent[]): Promise<void>;
  query?(options: EventQueryOptions): Promise<EventQueryResult>;
  count?(): Promise<number>;
  getStats?(): Promise<EventStats>;
  healthCheck?(): Promise<boolean>;
  shutdown?(): Promise<void>;
}

Methods

ingest
(event: AuthEvent) => Promise<void>
required
Ingest a single authentication event. This is the only required method.Parameters:
  • event: The AuthEvent object to store
ingestBatch
(events: AuthEvent[]) => Promise<void>
Ingest multiple events in a single operation for better performance.Parameters:
  • events: Array of AuthEvent objects to store
query
(options: EventQueryOptions) => Promise<EventQueryResult>
Query events with filtering, pagination, and sorting.Parameters:
  • options: Query options including filters, limit, offset, and sort order
Returns:
  • Promise resolving to EventQueryResult with events and pagination info
count
() => Promise<number>
Get the total count of events in storage.Returns:
  • Promise resolving to the total number of events
getStats
() => Promise<EventStats>
Get statistical breakdown of events by severity.Returns:
  • Promise resolving to EventStats with counts by severity level
healthCheck
() => Promise<boolean>
Check if the provider is healthy and can accept events.Returns:
  • Promise resolving to true if healthy, false otherwise
shutdown
() => Promise<void>
Gracefully shutdown the provider, flushing any pending events.

Custom Provider Example

You can implement a custom provider for any storage backend:
import type { EventIngestionProvider, AuthEvent } from "better-auth-studio";

const customProvider: EventIngestionProvider = {
  async ingest(event: AuthEvent) {
    // Store event in your custom backend
    await myDatabase.events.create({
      id: event.id,
      type: event.type,
      timestamp: event.timestamp,
      status: event.status,
      userId: event.userId,
      metadata: event.metadata,
      // ... other fields
    });
  },

  async ingestBatch(events: AuthEvent[]) {
    // Batch insert for better performance
    await myDatabase.events.createMany(events);
  },

  async query(options) {
    const { limit = 20, offset = 0, type, userId, sort = "desc" } = options;
    
    const query = myDatabase.events.find({
      ...(type && { type }),
      ...(userId && { userId }),
    })
      .sort({ timestamp: sort === "desc" ? -1 : 1 })
      .skip(offset)
      .limit(limit + 1);

    const results = await query.exec();
    const hasMore = results.length > limit;
    const events = results.slice(0, limit);

    return {
      events,
      hasMore,
      nextCursor: hasMore ? events[events.length - 1].id : null,
    };
  },

  async healthCheck() {
    try {
      await myDatabase.ping();
      return true;
    } catch {
      return false;
    }
  },
};

export default customProvider;

Using a Custom Provider

import { defineStudioConfig } from "better-auth-studio";
import customProvider from "./custom-provider";

export const studioConfig = defineStudioConfig({
  events: {
    enabled: true,
    provider: customProvider,
  },
});

Build docs developers (and LLMs) love