Next.js + ClickHouse + Prisma (Analytics & Observability)
Architecture guidelines for high-volume event ingestion and analytical queries using ClickHouse alongside a PostgreSQL/Prisma control plane.
- Formats
- 4 files
- AGENTS.md
- 37 lines
- CLAUDE.md
- 15 lines
- Languages
- TypeScript
- Updated
- Oct 2026
- Used by
- 4 projects
Writes .claude/skills/nextjs-clickhouse-analytics/SKILL.md
$ curl -s --create-dirs -o .claude/skills/nextjs-clickhouse-analytics/SKILL.md https://stackitfast.com/rules/nextjs-clickhouse-analytics/SKILL.mdRule files
Works with Cursor · Claude Code · Windsurf · AGY
Architecture notes
Architecture Overview
Standardized production guidelines for Next.js, ClickHouse, and Prisma/PostgreSQL — the two-database split used by analytics and observability platforms that need both transactional entity storage and high-volume event aggregation.
Verified Real-World Adoption
Umami (privacy-focused web analytics) and Langfuse (LLM observability) both run this exact split: Next.js + Prisma over PostgreSQL for the control plane, ClickHouse for the raw event/trace volume. The same architectural pattern, with different primary languages, powers PostHog, Sentry, and Plausible Analytics.
Key Architectural Nuances
- Append-Only vs. Mutable Storage: ClickHouse rows are treated as immutable once written; anything that changes over time (user plan, project name) stays in PostgreSQL and is joined logically, not physically, at query time.
- Batched, Async Ingestion: The ingestion path is architected separately from the dashboard read path, since bursty high-volume writes and low-latency authenticated reads have conflicting performance profiles.
Frequently asked questions
Does this AGENTS.md work with Cursor, Claude Code, and Windsurf?
Yes — AGENTS.md is the open, cross-tool standard read by Cursor, Claude Code, Windsurf, and 30+ other agents. A dedicated .mdc file is also included for Cursor's native .cursor/rules format.
Why run two databases instead of just PostgreSQL?
PostgreSQL is a row-oriented database optimized for transactional reads/writes of individual records. ClickHouse is column-oriented and optimized for scanning and aggregating billions of append-only rows in milliseconds. Once event volume gets large enough that dashboard queries on Postgres start timing out, splitting the write-heavy analytical workload into ClickHouse -- while keeping Postgres for everything transactional -- is the standard fix rather than trying to tune Postgres past its design center.
When should a project reach for this pattern instead of just PostgreSQL?
When event volume is in the millions-of-rows-per-day range and dashboard queries need to aggregate over that volume in near-real-time. Below that scale, PostgreSQL with a well-indexed events table (or a service like Tinybird) is simpler to operate and sufficient.
Used in production
Explore all stacksCal.com
1Splitting the tRPC-based web app from an independently versioned NestJS Platform API lets Cal.com serve both its own product and third-party developers from one monorepo, while Turborepo and per-integration app-store packages keep dozens of calendar/payment providers buildable and testable in isolation.
Cosmo (WunderGraph)
Splitting the Go router (runtime query execution) from the TypeScript Controlplane (schema registry and management) lets Cosmo scale and deploy the performance-critical data path independently, while a dedicated ClickHouse-backed metrics service keeps high-volume usage analytics off the primary Postgres database.
Sentry
Pairing Django with Kafka and ClickHouse allows Sentry to ingest billions of events daily while providing sub-second analytical search across production traces.
Taxonomy
Built to demonstrate the App Router, server components, and Next.js 13 data fetching patterns in one realistic, full-featured codebase.