Overview
Better Uptime uses Turborepo to manage a monorepo containing multiple applications and shared packages. This structure enables code sharing, consistent tooling, and efficient builds.Repository Layout
Applications
Client (apps/client)
Next.js web application
The user-facing dashboard for monitoring websites and managing configurations.
Key features:
- Server-side rendering
- tRPC client integration
- Real-time WebSocket updates
- Responsive design with Tailwind CSS
@repo/config: Environment variables@repo/store: Database accessserver: Type definitions for tRPC
Server (apps/server)
tRPC API server
Central API layer exposing type-safe endpoints for the client.
Responsibilities:
- User authentication
- Website CRUD operations
- Status page management
- Real-time subscriptions
- Query orchestration across databases
@repo/api: Router implementations@repo/store: PostgreSQL access@repo/clickhouse: Time-series queries@repo/validators: Request validation
apps/server/src/index.ts:9
Worker (apps/worker)
Uptime check worker
Consumes website check tasks from Redis and executes HTTP health checks.
Responsibilities:
- Consume tasks from Redis Stream consumer group
- Execute HTTP requests to monitored websites
- Record metrics to ClickHouse
- Handle message acknowledgment and PEL management
- Self-monitoring and auto-recovery
- Consumer group support for horizontal scaling
- Automatic stale message reclaim
- Worker watchdog for freeze detection
- Timeout protection on all operations
@repo/clickhouse: Metrics storage@repo/streams: Redis Stream consumer@repo/store: Website validation@repo/config: Environment configuration
apps/worker/src/index.ts:123
Publisher (apps/publisher)
Task publishing service
Periodically queries active websites and publishes check tasks to Redis.
Responsibilities:
- Query PostgreSQL for active websites (every 3 minutes)
- Bulk publish to Redis Stream
- Prevent overlapping publish cycles
- Trim stream to prevent memory exhaustion
@repo/store: Website queries@repo/streams: Redis Stream publisher
apps/publisher/src/index.ts:6
Shared Packages
API (packages/api)
tRPC router definitions
Centralized API logic shared between server and client.
Exports:
userRouter: Authentication and user managementwebsiteRouter: Website monitoring CRUDstatusPageRouter: Public status pagesstatusDomainRouter: Custom domain configuration
- Single source of truth for API logic
- Type definitions flow to client automatically
- Easy to test in isolation
Store (packages/store)
Prisma client and database access
Responsibilities:
- Database schema (Prisma schema)
- Type-safe query client
- Database migrations
ClickHouse (packages/clickhouse)
Time-series metrics storage
Responsibilities:
- ClickHouse client initialization
- Schema management
- Batch insert operations
- Query helpers for metrics retrieval
recordUptimeEvents(events): Batch insert metricsgetRecentStatusEvents(websiteIds, limit): Query recent checksgetStatusEventsForLookbackHours(websiteIds, hours): Historical data
packages/clickhouse/src/index.ts:154
Streams (packages/streams)
Redis Streams abstraction
Responsibilities:
- Redis client configuration
- Stream publishing (XADD)
- Consumer group management (XREADGROUP)
- Automatic message acknowledgment (XACK)
- PEL monitoring and reclaim (XAUTOCLAIM)
xAddBulk(websites): Publish batch of tasksxReadGroup(options): Consume tasksxAckBulk(options): Acknowledge processed tasksxAutoClaimStale(options): Reclaim stale messagesxPendingInfo(consumerGroup): Monitor PEL health
packages/streams/src/index.ts:206
Validators (packages/validators)
Zod schemas for runtime validation
Exports:
- Input validators for tRPC mutations
- Response validators for API contracts
- Shared type definitions
- Single source of truth for validation logic
- Automatic TypeScript type inference
- Reusable across server and client
Config (packages/config)
Environment variables and configuration
Responsibilities:
- Centralized environment variable access
- Type-safe configuration
- Default values and validation
apps/worker/src/index.ts:6
UI (packages/ui)
Shared React components
Responsibilities:
- Reusable UI components
- Common hooks and utilities
- Design system primitives
- Consistent UI across applications
- Single place to update shared components
- Easier to maintain design system
Tooling Packages
ESLint Config (tooling/eslint-config)
Shared ESLint configuration
- Base rules for TypeScript
- React-specific rules
- Next.js-specific rules
- Consistent across all packages
TypeScript Config (tooling/typescript-config)
Shared TypeScript configuration
- Base
tsconfig.json - Strict type checking
- Module resolution settings
- Extended by individual packages
Turborepo Configuration
Build Pipeline
turbo.json defines the build pipeline:
turbo.json
Task Dependencies
^build notation:
- Turbo builds all dependencies first
- Example:
serverdepends on@repo/api, soapibuilds first
- Independent tasks run concurrently
- Dramatically faster than sequential builds
Caching Strategy
Cached tasks:build: Cached based on inputs and dependencieslint: Cached for unchanged filestest:coverage: Cached with coverage outputs
dev: Always runs (persistent)test: Live runs ensure fresh results
Workspace Dependencies
Internal Dependencies
Using workspace protocol:workspace:*: Any version in workspaceworkspace:^: Compatible version in workspace
Dependency Graph
Package Manager
pnpm (version 9.0.0) Benefits:- Efficient disk space usage (content-addressable storage)
- Faster than npm and Yarn
- Strict dependency resolution
- Native workspace support
Development Workflow
Starting Development
Adding a New Package
Using a Workspace Package
Benefits of This Structure
Code Sharing
- Shared logic in packages, not duplicated
- Type definitions shared automatically
- Utilities accessible everywhere
Independent Deployment
- Each app can be deployed separately
- Worker can scale independently of server
- Publisher runs as isolated service
Build Efficiency
- Only rebuild changed packages
- Parallel builds across CPU cores
- Shared build cache
Type Safety
- TypeScript types flow through dependencies
- Changes in packages detected at build time
- Refactoring is safer
Testing
- Test packages in isolation
- Shared test utilities
- Fast feedback loop
Next Steps
System Architecture
Understand component interactions
Data Flow
Learn how data moves through the system