Meridian Analytics Platform
B2B SaaS · HR technology · Series A
A Series A HR tech company needed a multi-tenant analytics dashboard as a core product feature — not a third-party embed, a native product that their customers would see and evaluate daily. The constraints: 2M+ events/day from enterprise clients, sub-200ms query response across all tenants, strict data isolation, and a 14-week deadline tied to a customer commitment.
2M+
Events per day
Sustained load, no degradation
<200ms
P99 query time
Across all tenant dashboard queries
99.97%
Uptime
12-month post-launch measurement
14wk
Build to production
Including multi-tenant auth and billing
The constraints.
Multi-tenant analytics is a solved problem for large engineering teams — Snowflake, BigQuery, a dedicated data warehouse. For a Series A company with three engineers and a product roadmap full of other commitments, those solutions are operationally expensive.
The real constraint was tenant isolation. Enterprise HR clients cannot have any possibility of cross-tenant data exposure — their contracts explicitly prohibit it and their security reviews test for it. The architecture had to enforce isolation at the database layer, not just the application layer.
The secondary constraint was query performance. Analytics dashboards that take 3–4 seconds to load get abandoned. The target was sub-200ms P99 across all queries, including aggregate queries over 90-day event windows for enterprise tenants.
The architecture decision.
Supabase with row-level security (RLS) as the primary isolation mechanism. Every table has a tenant_id column with an RLS policy that enforces the authenticated tenant's ID at query time. This means isolation is enforced at the Postgres layer — a misconfigured API route cannot return another tenant's data because the database will not return it.
For query performance: the events table is partitioned by month. Queries against recent data (the most common dashboard queries) never scan historical partitions. Composite indexes on (tenant_id, event_timestamp) mean the planner can execute most dashboard queries with a single index scan. Materialized views for the aggregate metrics that recalculate on a schedule rather than on every request.
Stack: Next.js 14 App Router, TypeScript, Supabase (Postgres + RLS + Realtime + Edge Functions), Prisma ORM, tRPC, React Query, Clerk (multi-tenant auth), Stripe (usage billing), Upstash (queue layer for ingestion backpressure), Vercel.
Fourteen weeks, in order.
Week 1–3
Architecture design and data model
The hardest decision: how to isolate tenant data without separate databases per tenant (cost-prohibitive at early ARR) but without shared-table row-level security risks. Answer: Supabase RLS with a tenant_id predicate on every table, enforced at the database layer. This means even a misconfigured API route cannot leak cross-tenant data. Schema designed for the querying patterns the product actually needed, not the ones most common in tutorials.
Week 4–7
Event ingestion pipeline
Real-time event ingestion through a Supabase Edge Function — sub-50ms acknowledgement to the client, async write to the database. The pipeline handles backpressure through an Upstash queue layer between ingestion and storage. Partitioning on event_timestamp means queries against recent data never touch historical partitions.
Week 8–11
Dashboard and query layer
React Query with optimistic updates for the dashboard UI. tRPC for end-to-end type safety — a database schema change propagates as a compile error, not a runtime 500. Custom aggregation layer for the analytics queries that Supabase's default PostgREST layer couldn't express efficiently.
Week 12–14
Multi-tenant auth, billing, and launch
Clerk for authentication with organisation-level tenant isolation. Stripe for billing with usage-based pricing tied to event volume. Posthog for product analytics (product team needed to see feature adoption). Sentry for error monitoring with tenant context attached to every error.
The result.
The platform launched on schedule and passed three enterprise security reviews in the first month — all of which specifically tested for cross-tenant data isolation. No failures.
12 months post-launch: 2M+ events per day sustained, P99 query time consistently under 200ms, 99.97% uptime. The team of three engineers has made multiple schema changes since launch — none required my involvement, which was the design goal.
The system runs itself. That's the deliverable.
“The architecture passed every security review. More importantly, our engineers can extend it without needing to understand the whole thing. That's rare.”
— CTO, HR Technology Company (name withheld on request)
What this engagement taught.
Data isolation architecture is a one-way door — get it right before launch.
Row-level security at the database layer is the correct choice for multi-tenant SaaS at this scale, but it has to be designed in from the beginning. Retrofitting it post-launch would have required a full schema migration. The schema decisions made in week one were still in production 12 months later, unchanged.
Type safety is ops leverage, not developer comfort.
The tRPC + Prisma stack meant schema changes propagated as compile errors. The team of three made multiple schema changes post-launch without my involvement — because the toolchain enforced the constraints at compile time. Every runtime type error that didn't happen is a support ticket that never existed.
Observability before users, not after incidents.
Posthog, Sentry, and distributed tracing were wired before the first user. This sounds obvious and rarely happens. The first three enterprise security reviews specifically tested for monitoring and logging — they passed because the work was already done, not patched in response to the review.
If you have a system that needs to be built right — not just built — the application is the next step. You leave with a clear architectural recommendation whether or not we work together.
Related: Web Architecture insights →