Modern sports dashboards are often plagued by identical problems: slow page loads, stale stats, high API costs, and visual clutter. When trying to process live-game data, aggregate predicting models, and render actionable NBA research tools, a naive architectural approach will crash under rate limits and slow database lookups.
With ParlAi, we set out to build a Next.js sports prediction prototype designed to solve these constraints. By combining Clerk authentication, a Neon-backed snapshot caching strategy, unified data streams from ESPN and Kalshi, and a custom dark-forest design language called Notio, we created a highly performant, data-dense NBA intelligence hub.
Here is the technical blueprint of how we designed, optimized, and built it.
The Architecture: Live Data without the Latency
In sports prediction applications, the temptation is to pull data live from external APIs on every single user request. However, external sports APIs are notorious for slow response times (often exceeding 1.5 seconds) and restrictive rate limits.
To overcome this, ParlAi implements a decoupled Snapshot Cache design.
graph TD
A[ESPN Schedule & Stats API] -->|Cron Refresh| C[Data Normalization Engine]
B[Kalshi Open Market API] -->|Cron Refresh| C
C -->|Store snapshot| D[(Neon Serverless PostgreSQL)]
E[Client Browser] -->|Fast Read| F[Next.js App Router /api/projections]
F -->|Cache-first read| D
G[Clerk Auth Shield] -->|Verifies JWT| F
Under this pattern:
- Background workers pull schedule, team metrics, and player performance data from ESPN and public prediction market rates from Kalshi's open APIs.
- The data is normalized, and our AI projection models generate snapshots.
- These snapshots are written directly to a Neon Serverless PostgreSQL instance using transaction-safe upserts into a
projection_snapshotstable. - When a user loads their NBA projections feed, the Next.js App Router reads directly from Neon. Latency drops from over 1,500ms to less than 45ms.
Hardening the Neon DB Cache Layer
We leveraged Neon Postgres (DATABASE_URL) to build a robust, serverless storage layer. Serverless architectures can struggle with cold starts and connection pooling, but Neon handles scaling transparently.
To keep queries lightning fast, we optimized the projection_snapshots schema with composite indexes on the game day and player/team identifiers. Here is an illustrative look at the snapshot storage strategy:
// src/lib/db/projections.ts
import { sql } from "@vercel/postgres";
export interface AIProjectionSnapshot {
gameId: string;
homeTeam: string;
awayTeam: string;
projectionScore: number;
confidenceInterval: [number, number];
aiReasoningSummary: string;
marketImpliedProbability: number;
}
export async function getLatestProjections(gameDate: string): Promise<AIProjectionSnapshot[]> {
try {
const { rows } = await sql`
SELECT
game_id as "gameId",
home_team as "homeTeam",
away_team as "awayTeam",
projection_score as "projectionScore",
confidence_interval as "confidenceInterval",
ai_reasoning_summary as "aiReasoningSummary",
market_implied_probability as "marketImpliedProbability"
FROM projection_snapshots
WHERE game_date = ${gameDate}
ORDER BY confidence_score DESC;
`;
return rows as AIProjectionSnapshot[];
} catch (error) {
console.error("Failed to fetch projection snapshots:", error);
return [];
}
}
By decoupling database population from client route evaluation, we completely eliminated runtime external API rate-limiting issues. If the ESPN api returns a 503 or Kalshi throttles our keys, ParlAi gracefully serves the last validated snapshot stored in Neon.
Authentication & User Context with Clerk
Secure user tracking is essential when users personalize dashboards, track active predictions, or save projection parameters. We chose Clerk because of its out-of-the-box support for Next.js App Router, seamless session token propagation, and minimal bundle footprint.
By utilizing Clerk middleware, we guard sensitive AI projection endpoints at the routing edge:
// src/middleware.ts
import { authMiddleware } from "@clerk/nextjs";
export default authMiddleware({
publicRoutes: ["/", "/blog(.*)", "/services(.*)", "/api/webhooks(.*)"],
});
export const config = {
matcher: ["/((?!.+\\.[\\w]+$|_next).*)", "/", "/(api|trpc)(.*)"],
};
This ensures that only authenticated sports analysts can issue high-frequency requests to our predictive endpoints, safeguarding the serverless database from resource exhaustion.
Customizing Notio: A Deep-Forest Design System
In sports analytics, screens are notoriously cluttered, bright, and exhausting to read. To deliver a premium, high-focus research workspace, we created the Notio forest design system.
The core aesthetic principles of Notio are:
- Deep Dark Surfaces: A primary background color palette built on highly saturated dark forest greens (
#040a06and#08140d) instead of flat grays or pitch blacks. This minimizes eye strain during extended analytical sessions. - Luminous Green Accents: High-contrast emerald and bright mint green accents (
#10b981and#34d399) highlighting critical AI confidence metrics and prediction indicators. - Geist Typography: Utilizing Vercel’s Geist Sans typeface for clean, highly legible numeric tables and player research stats.
- Restrained Motion: Very subtle hover highlights and transitions using Framer Motion primitives to maintain a premium, quiet layout that never distracts from high-density data.
// src/components/ui/projection-card.tsx
import { motion } from "framer-motion";
interface ProjectionCardProps {
player: string;
stat: string;
projected: number;
marketLine: number;
confidence: "High" | "Medium" | "Low";
}
export function ProjectionCard({ player, stat, projected, marketLine, confidence }: ProjectionCardProps) {
return (
<motion.div
whileHover={{ y: -2 }}
className="p-4 rounded-xl border border-emerald-950/40 bg-emerald-990/20 backdrop-blur-md transition-shadow hover:shadow-[0_8px_30px_rgb(0,0,0,0.12)] hover:border-emerald-500/20"
>
<div className="flex justify-between items-center mb-2">
<span className="text-sm font-semibold text-zinc-100">{player}</span>
<span className={`px-2 py-0.5 rounded text-xs font-medium ${
confidence === "High" ? "bg-emerald-950 text-emerald-400" : "bg-yellow-950 text-yellow-400"
}`}>
{confidence} Confidence
</span>
</div>
<div className="grid grid-cols-2 gap-4 text-xs font-mono mt-3">
<div>
<span className="text-zinc-500 block">AI Projected</span>
<span className="text-lg font-bold text-emerald-400">{projected} {stat}</span>
</div>
<div>
<span className="text-zinc-500 block">Market Line</span>
<span className="text-lg font-bold text-zinc-300">{marketLine} {stat}</span>
</div>
</div>
</motion.div>
);
}
Production Takeaways
Building ParlAi reinforced several critical principles of modern web app engineering:
- Serve Caches, Calculate Offline: Never force client requests to wait for third-party networks. Live sports data and predictive AI outcomes are perfect candidates for scheduled snapshots.
- Keep the Interface Quiet: If your dashboard serves dense data tables, reduce the visual noise. Avoid flashing animations, heavy gradients, or excessive layout changes. Let pure typography and balanced contrast do the heavy lifting.
- Database Security is Non-Negotiable: When building web apps on serverless Postgres, clamp down on open connections and rate-limit sensitive query paths using fast, edge-based authentication.
Keep the Thread Going
- Service path: Custom Web Development
- Related read: Cache-First Architecture for AI Projection Apps
- Proof point: MintoCrypto
- Ready to scope your own version? Start a project





