Events Table Query Routing#
Scope: This covers how read queries are routed between the legacy ClickHouse traces/observations tables and the V4 events tables (events_core, events_full), including the useEventsTable flag, environment variable fallback, and implications for V4 events-only deployments.
Overview#
During the V4 data pipeline migration, Langfuse introduced a dual-path query architecture. Legacy queries run against the traces and observations ClickHouse tables; V4 queries aggregate from events_core and events_full via purpose-built repository functions. At runtime, a useEventsTable boolean (derived from a query parameter or environment variable) selects which path to execute.
The write-side counterpart is controlled by LANGFUSE_MIGRATION_V4_WRITE_MODE (legacy | dual | events_only). The default for new deployments is events_only, meaning new installations write only to the events tables and have no legacy table data.
Environment Variables#
Three web/src/env.mjs variables gate events-table routing :
| Variable | Default | Controls |
|---|---|---|
LANGFUSE_ENABLE_EVENTS_TABLE_OBSERVATIONS | "false" | Read routing for public API traces/observations list and by-ID endpoints |
LANGFUSE_ENABLE_EVENTS_TABLE_FLAGS | "false" | Dual-write of bookmarked/public mutations to events tables alongside legacy upserts |
LANGFUSE_ENABLE_EVENTS_TABLE_V2_APIS | "false" | Enables V2 API endpoints that are natively events-table backed |
The useEventsTable Flag#
Schema#
useEventsTableSchema in web/src/features/query/types.ts accepts "true", "false", or a native boolean and transforms to a boolean value. This allows callers to pass the flag as a query string parameter.
Resolution pattern#
Every public API read endpoint resolves useEventsTable with the same pattern — query parameter beats environment variable:
useEventsTable = (query.useEventsTable != null)
? query.useEventsTable === true
: env.LANGFUSE_ENABLE_EVENTS_TABLE_OBSERVATIONS === "true"
This pattern is used in , , and .
Routing by API Layer#
Public REST API — traces list#
web/src/pages/api/public/traces/index.ts :
- Events path →
getTracesFromEventsTableForPublicApi+getTracesCountFromEventsTableForPublicApi - Legacy path →
generateTracesForPublicApi+getTracesCountForPublicApi
Public REST API — observations list and by-ID#
web/src/pages/api/public/observations/index.ts :
- Events path →
getObservationsFromEventsTableForPublicApi+getObservationsCountFromEventsTableForPublicApi - Legacy path →
generateObservationsForPublicApi+getObservationsCountForPublicApi
web/src/pages/api/public/observations/[observationId].ts :
- Events path →
getObservationByIdFromEventsTable - Legacy path →
getObservationById
tRPC observations router (UI)#
web/src/server/api/routers/observations.ts reads LANGFUSE_ENABLE_EVENTS_TABLE_OBSERVATIONS directly — no per-request override :
- Events path →
getObservationByIdFromEventsTable - Legacy path →
getObservationById
tRPC traces router (UI)#
web/src/server/api/routers/traces.ts does not route trace reads to the events table. getTracesTable and getTracesTableCount always query the legacy traces ClickHouse table. This is in contrast to sessions, which have explicit allFromEvents / countAllFromEvents tRPC endpoints with an isBetaEnabled toggle.
Mutations — dual-write via LANGFUSE_ENABLE_EVENTS_TABLE_FLAGS#
The bookmark and publish tRPC mutations apply changes to the legacy traces table first, then conditionally fan out to the events tables when LANGFUSE_ENABLE_EVENTS_TABLE_FLAGS === "true" . Both calls are made in parallel via Promise.all.
V4 Events-Only Instances#
In events_only write mode (the default for new deployments), the legacy traces and observations tables contain no data. For such instances:
- Public API reads must have
LANGFUSE_ENABLE_EVENTS_TABLE_OBSERVATIONS=true(or the caller must passuseEventsTable=true), otherwise queries return empty results. - The tRPC traces table UI (
.all,.countAll) will return empty results because it always queries the legacytracestable — events-table backing for the traces UI list is not yet implemented. - Onboarding state detection (
hasAnyTrace) uses a three-step fallback: PostgreSQL flag → ClickHousetracestable → ClickHouse events tables, ensuring V4-only projects are not incorrectly shown the onboarding UI.
Key Source Files#
| File | Role |
|---|---|
web/src/env.mjs | Env var declarations |
web/src/features/query/types.ts | useEventsTableSchema |
web/src/pages/api/public/traces/index.ts | Traces public API routing |
web/src/pages/api/public/observations/index.ts | Observations list public API routing |
web/src/pages/api/public/observations/[observationId].ts | Observation by-ID public API routing |
web/src/server/api/routers/observations.ts | tRPC observations routing |
web/src/server/api/routers/traces.ts | tRPC dual-write for mutations |
packages/shared/src/server/repositories/events.ts | Events-table query implementations |
packages/shared/src/server/repositories/traces.ts | Legacy traces query implementations |