Building a Real-time Lottery Web Application with Node.js & Next.js
High-traffic websites receiving thousands of concurrent hits must optimize API request quotas. In this guide, you will learn how to implement an in-memory or Redis caching proxy layer in Node.js/Next.js, define strict TypeScript interfaces, and render a stunning React UI component for Powerball and Mega Millions results.
REDIS PROXY CACHING ARCHITECTURE (SUB-10MS LATENCY)
TypeScript Response Interfaces
Start by creating strong TypeScript interfaces for US Lottery API JSON payloads. Save this in src/types/lottery.ts:
export interface LotteryDrawData {
game_id: "powerball" | "mega-millions";
game_name: string;
draw_date: string;
winning_numbers: number[];
powerball?: number;
mega_ball?: number;
multiplier: string;
estimated_jackpot: string;
cash_option?: string;
has_jackpot_winner: boolean;
next_draw_date: string;
next_jackpot_estimate: string;
}
export interface ApiResponse<T> {
status: "success" | "error";
timestamp: string;
data: T;
message?: string;
}Redis Caching Proxy Route Handler
Implement a Next.js App Router Route Handler at src/app/api/lottery/powerball/route.ts. This checks Redis before calling US Lottery API, reducing upstream bandwidth by 99.9%.
import { NextResponse } from "next/server";
import Redis from "ioredis";
// Initialize Redis client (falls back to memory if REDIS_URL is unconfigured)
const redis = process.env.REDIS_URL ? new Redis(process.env.REDIS_URL) : null;
const CACHE_KEY = "uslottery:powerball:latest";
const CACHE_TTL_SECONDS = 300; // 5 minutes cache
export async function GET() {
try {
// 1. Check Redis Cache
if (redis) {
const cachedData = await redis.get(CACHE_KEY);
if (cachedData) {
return NextResponse.json(JSON.parse(cachedData), {
headers: {
"X-Cache-Status": "HIT",
"Cache-Control": "public, max-age=300, s-maxage=300, stale-while-revalidate=60",
},
});
}
}
// 2. Cache MISS — Query US Lottery API Upstream
const apiKey = process.env.US_LOTTERY_API_KEY;
if (!apiKey) {
return NextResponse.json(
{ status: "error", message: "US_LOTTERY_API_KEY is not configured." },
{ status: 500 }
);
}
const response = await fetch("https://uslotteryapi.com/v1/results/powerball?latest=true", {
headers: {
Authorization: `Bearer ${apiKey}`,
Accept: "application/json",
},
next: { revalidate: 300 },
});
if (!response.ok) {
return NextResponse.json(
{ status: "error", message: `Upstream API error ${response.status}` },
{ status: response.status }
);
}
const result = await response.json();
// 3. Set Redis Cache
if (redis && result.status === "success") {
await redis.setex(CACHE_KEY, CACHE_TTL_SECONDS, JSON.stringify(result));
}
return NextResponse.json(result, {
headers: {
"X-Cache-Status": "MISS",
"Cache-Control": "public, max-age=300, s-maxage=300, stale-while-revalidate=60",
},
});
} catch (error: any) {
return NextResponse.json(
{ status: "error", message: error.message || "Internal server error" },
{ status: 500 }
);
}
}React UI Component for Powerball Results
Now build a responsive, glassmorphic React UI component in src/components/PowerballCard.tsx that renders white balls, the red Powerball, and jackpot estimates:
import { LotteryDrawData } from "@/types/lottery";
interface PowerballCardProps {
data: LotteryDrawData;
}
export function PowerballCard({ data }: PowerballCardProps) {
return (
<div className="p-6 rounded-2xl border border-border bg-card/80 backdrop-blur-xl shadow-2xl max-w-md w-full">
{/* Card Header */}
<div className="flex items-center justify-between mb-4">
<div>
<h3 className="text-xl font-extrabold text-white">Powerball</h3>
<span className="text-xs font-mono text-foreground/50">
Draw Date: {data.draw_date}
</span>
</div>
<span className="px-3 py-1 rounded-full bg-red-500/20 text-red-400 border border-red-500/30 text-xs font-bold font-mono">
{data.multiplier} Multiplier
</span>
</div>
{/* Winning Numbers Row */}
<div className="flex items-center gap-2 mb-6">
{data.winning_numbers.map((num, idx) => (
<div
key={idx}
className="w-10 h-10 rounded-full bg-white text-black font-extrabold flex items-center justify-center text-sm shadow-[0_0_10px_rgba(255,255,255,0.4)]"
>
{num}
</div>
))}
{/* Red Powerball */}
{data.powerball !== undefined && (
<div className="w-10 h-10 rounded-full bg-red-600 text-white font-extrabold flex items-center justify-center text-sm shadow-[0_0_15px_rgba(239,68,68,0.6)] ml-1">
{data.powerball}
</div>
)}
</div>
{/* Jackpot Info */}
<div className="p-4 rounded-xl bg-background/60 border border-border flex items-center justify-between">
<div>
<span className="text-[10px] font-mono text-foreground/50 uppercase block">
Estimated Jackpot
</span>
<span className="text-xl font-black text-secondary">
{data.estimated_jackpot}
</span>
</div>
<div className="text-right">
<span className="text-[10px] font-mono text-foreground/50 uppercase block">
Next Draw
</span>
<span className="text-xs font-bold text-primary font-mono">
{data.next_draw_date}
</span>
</div>
</div>
</div>
);
}Performance Verification & Benchmarking
By combining Next.js App Router Route Handlers with Redis caching, your lottery web application achieves incredible latency metrics:
Build Scalable Node.js Lottery Apps Today
Get your free API key and connect your Next.js, Express, or Nuxt web app to official US lottery feeds.