Why my Next.js dashboard was always showing stale data — and the one-line fix
Source: Dev.to
Overview
The timestamp was updating. The AI recommendations kept coming in. Everything looked alive.
But the sensor values hadn’t moved in twenty minutes.
That’s the kind of bug that gets inside your head. Half the app was working perfectly, which meant the problem wasn’t obvious—it was hiding somewhere in the middle.
SensorWatch AI is an industrial sensor‑monitoring dashboard built with Next.js 14, TypeScript, PostgreSQL, and an LLM integration via OpenRouter. It simulates three industrial sensors, performs real‑time anomaly detection, and generates maintenance recommendations in natural language. On paper it was fine—until it wasn’t.
Everything I tried that didn’t work
The AI hypothesis
OpenRouter’s free tier can be painfully slow. I suspected a race condition between the sensor simulation and the anomaly analysis, so I split the AI call and the sensor simulation into two independent Promise.all flows. The bug persisted.
Front‑end guesses
I wondered whether a useCallback wasn’t re‑executing because of missing dependencies. Adding more console.logs didn’t reveal anything.
The breakthrough
After scattering console.logs everywhere, I discovered that the database was returning exactly the same data on every request—same values, same timestamps. The sensors were still generating new readings; the data simply wasn’t being fetched correctly.
The cause
The problem lived in a single line of the Prisma query:
const readings = await prisma.sensorReading.findMany({
where: { sensorId: sensor.id },
orderBy: { createdAt: "asc" }, // ← wrong direction
take: 50,
});orderBy: { createdAt: "asc" } asks for the oldest 50 records. With thousands of rows in the table, those oldest entries never change, so every request returned the same stale slice of data. The UI appeared alive because timestamps were generated client‑side with new Date(), and the AI endpoint used a correctly ordered query.
The fix
const readings = await prisma.sensorReading.findMany({
where: { sensorId: sensor.id },
orderBy: { createdAt: "desc" }, // newest first
take: 50,
select: {
value: true,
isAnomaly: true,
createdAt: true,
},
});
// Reverse so the chart displays time left‑to‑right
const sortedReadings = readings.reverse();descfetches the 50 most recent records, which change with each simulation cycle..reverse()puts them back in chronological order for the chart (oldest on the left, newest on the right).
Updating the status cards becomes straightforward:
currentValue: sortedReadings[sortedReadings.length - 1]?.value ?? null,
currentIsAnomaly: sortedReadings[sortedReadings.length - 1]?.isAnomaly ?? false,The lesson
When something isn’t displaying correctly, verify the data before touching the UI.
I spent hours chasing symptoms in the frontend, AI pipeline, and fetch logic, while the root cause was a single misplaced asc. A single console.log at the API response level would have shown the identical payload on every request and saved a lot of time.
Check your data first. Then your code.
SensorWatch AI is open source — github.com/RonaldGGA/sensorwatch-ai
Live demo: sensorwatch-ai.vercel.app