Backend Code Structure Guide
This guide explains how Langfuseβs backend is organized and how to write code that follows our established patterns.
Architecture at a Glance
Langfuse uses a monorepo structure with three main packages:
- web - Next.js 15 application (UI + tRPC API + Public REST API)
- worker - Express-based background job processor using BullMQ
- packages/shared - Shared code, types, and utilities used by both web and worker
API Request Flow
ββ Web (NextJs): tRPC API βββββ βββ Web (NextJs): Public API ββ
β β β β
β HTTP Request β β HTTP Request β
β β β β β β
β tRPC Procedure β β withMiddlewares + β
β (protectedProjectProcedure)β β createAuthedProjectAPIRouteβ
β β β β β β
β Service (business logic) β β Service (business logic) β
β β β β β β
β Prisma / ClickHouse β β Prisma / ClickHouse β
β β β β
βββββββββββββββββββββββββββββββ βββββββββββββββββββββββββββββββ
β
[optional]: Publish to Redis BullMQ queue
β
ββ Worker (Express): BullMQ Queue Job βββββββββββββββββββββββββ
β β
β BullMQ Queue Job β
β β β
β Queue Processor (handles job) β
β β β
β Service (business logic) β
β β β
β Prisma / ClickHouse β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββWe follow the layered architecture pattern:
- Router Layer: HTTP Requests or BullMQ Job handlers
- Service Layer: Contains all the business logic
- Repository Layer: Prisma / ClickHouse
Directory Structure
Web Package (/web/src/)
web/src/
βββ features/ # Feature-organized code
β βββ [feature-name]/
β βββ server/ # Backend: tRPC routers, services
β βββ components/ # Frontend: React components
β βββ types/ # TypeScript types
β
βββ server/
β βββ api/
β β βββ routers/ # tRPC routers
β β βββ trpc.ts # tRPC config & middleware
β β βββ root.ts # Root router
β βββ auth.ts # NextAuth configuration
β βββ db.ts # Database client
β
βββ pages/
β βββ api/
β β βββ public/ # Public REST API endpoints
β β βββ trpc/ # tRPC handler
β βββ [routes].tsx # Next.js pages
β
βββ __tests__/ # Jest tests
βββ instrumentation.ts # OpenTelemetry setup
βββ env.mjs # Environment configWorker Package (/worker/src/)
worker/src/
βββ queues/ # BullMQ job processors
β βββ evalQueue.ts
β βββ ingestionQueue.ts
β βββ workerManager.ts
βββ features/ # Business logic
βββ app.ts # Express server + queue setupShared Package (/packages/shared/src/)
shared/src/
βββ server/ # Server-only code
β βββ auth/ # Authentication utilities
β βββ clickhouse/ # ClickHouse client
β βββ repositories/ # Complex query logic
β βββ services/ # Shared business logic
β βββ redis/ # Queue and cache utilities
β βββ instrumentation/ # Observability helpers
β
βββ encryption/ # Encryption utilities
βββ tableDefinitions/ # Database schemas
βββ utils/ # Shared utilities
βββ db.ts # Prisma client
βββ index.ts # Public exportsTypeScript Types
We use TypeScript for all our code and maintain a structured type system with clear conversion boundaries.
Type Hierarchy
Our type system follows a layered architecture with explicit conversions between layers.
Typing through the API layers
Typing through the storage layers
Key Differences:
- Public APIs are versioned - Our SDKs convert returned JSON to TypeScript/Python types. We must always be backwards compatible. Hence, we define dedicated types for the public API and convert domain objects to these types.
- tRPC API is not versioned - We deploy our backend and frontend in sync and force refresh our frontend on new deployments. Therefore we can introduce breaking changes to the tRPC API.
- The domain types can be found in
packages/shared/src/domain/index.ts
Was this page helpful?