Skip to main content

Events

Overview

The Events module handles platform event logging and alerting for streaming events such as followers, subscribers, cheers, raids, channel point redemptions, and tips. Events are stored in a TimescaleDB hypertable with automatic compression for efficient time-series queries. Events are broadcast in real time via Redis pub/sub to WebSocket clients for overlay alerts and dashboard panels. The module supports paginated queries with filters by type, platform, date range, and full-text search.

Architecture

Backend

  • GraphQL (apps/api/src/graphql/events.rs) -- Queries for listing/fetching events with filters. Mutation for emitting test/manual events.
  • Crate (crates/lo-events/src/) -- Core event logic including EventFilter, EventInput, EventRow types, list_events, get_event, insert_event, and broadcast_event functions.
  • Database -- Events are stored in TimescaleDB (platform_events hypertable) with time-based partitioning and compression.
  • Real-time -- Events are broadcast via RedisPubSub using the GqlPubSub wrapper to all WebSocket subscribers for the account.

Frontend

  • Next.js API proxy routes call GraphQL internally.
  • Event panel displays events with filters by type and platform.
  • Overlay alerts consume events via WebSocket for real-time display.

API

GraphQL Queries

QueryPermissionDescription
events(filter: EventFilterInput)events:readPaginated event list with filters for types, platform, search, date range
event(id: UUID)events:readGet a single event by ID (account-scoped)

GraphQL Mutations

MutationPermissionDescription
emitEvent(input: EmitEventInput)events:createEmit a test/manual event, insert into TimescaleDB and broadcast via pub/sub

REST Endpoints

All paths live under /v1. Bodies are snake_case.

MethodPathPermissionDescription
GET/v1/eventsevents:readPaginated event list (accepts the same filters as EventFilterInput)
GET/v1/events/{id}events:readFetch a single event by ID (account-scoped)
POST/v1/events/emitevents:createEmit a manual event (insert + broadcast)
POST/v1/events/testevents:createEmit a test event (broadcast only, not persisted in all paths)

Filter Options (EventFilterInput)

FieldTypeDescription
types[String]Filter by event types (e.g., ["twitch:follower", "twitch:subscriber"])
platformStringFilter by platform (comma-separated for multiple)
searchStringCase-insensitive text search in raw JSON data
fromStringISO 8601 timestamp -- only events after this time
toStringISO 8601 timestamp -- only events before this time
pageu32Page number (1-based, default 1)
limitu32Items per page (default 25, max 100)

Permissions

PermissionDescription
events:readRead/list events
events:createCreate events (test events, emit)
events:deleteDelete events
events:userinfoView user cards/profiles from event entries

Database

TableDatabaseDescription
platform_eventsTimescaleDBHypertable with compression. Fields: id (UUID), account_id, event_type (platform:action format), platform, external_id, raw (JSONB), created_at

Event Types

Events follow the platform:action naming convention:

  • twitch:follower, twitch:subscriber, twitch:cheer, twitch:raid, twitch:redemption
  • youtube:subscriber, youtube:member, youtube:superchat
  • spotify:track, spotify:play, spotify:pause, spotify:skip, spotify:volume, spotify:queue_add, spotify:device
  • streamelements:tip

Data Flow

  1. Platform adapter detects an event (e.g., Twitch EventSub webhook receives a follow).
  2. Event is inserted into TimescaleDB via lo_events::insert_event().
  3. Event is broadcast via lo_events::broadcast_event() through Redis pub/sub.
  4. WebSocket server forwards the event to all connected clients for the account.
  5. Overlay alerts render the event. Dashboard event panel updates in real time.
  6. Users can query historical events with filters and pagination via events query.

Key Files

PathDescription
apps/api/src/graphql/events.rsGraphQL queries and mutations
crates/lo-events/src/Core events crate (insert, list, broadcast, filter types)
crates/lo-events-db/TimescaleDB event storage layer