@techchintan just removing dynamicParams and revalidate and introducing export const dynamic = "error" did not ensure static generation. I kept getting dynamically generated pages for the paths that are not in the list of “popular cities” (staticParams) with a response header x-nextjs-cache: MISS (localhost) as well as x-vercel-cache: MISS (vercel).
Only after explicitly setting export const dynamicParams = false, I started getting 404 errors for the paths that are not in the staticParams list. But even after that, for the paths that are in the staticParams list, I keep getting responses with x-vercel-cache: PRERENDER on vercel, while getting x-nextjs-cache: HIT on localhost.
I need to set it straight though that in my original post I had said that “I always see DB hits after the request“. This is not true. I do not see DB a hit after the request with PRERENDER response. The only culprit is that it’s way too slow. The response with PRERENDER has “Waiting for server response” = 360 ms, while for the subsequent responses with HIT are 40 ms.
Here is the page.tsx - I tried to shorten it for brevity as much as possible.
import React from 'react';
import { supabase } from '@/lib/database';
import type { City } from '@/lib/supabase';
import { getStaticCities, createCitySlug } from '@/lib/monthly-weather-server';
// Monthly aggregate data structure from monthly_aggregates view
interface MonthlyAggregate {
geoname_id: number;
month: number;
tmax_mean: number | null;
tmin_mean: number | null;
}
// Page params interface
interface PageParams {
country: string;
city: string;
geoname_id: string;
}
export const dynamic = "error"
export const dynamicParams = false
// Server-side function to fetch city data by geoname_id
async function getCityData(geonameId: number): Promise<City | null> {
try {
const { data, error } = await supabase
.from('cities')
.select(`
geoname_id,
name,
country_code,
country_name
`)
.eq('geoname_id', geonameId)
.single();
if (error) {
return null;
}
return data as City;
} catch (error) {
return null;
}
}
// Server-side function to fetch monthly weather data by geoname_id
async function getMonthlyWeatherData(geonameId: number): Promise<MonthlyAggregate[]> {
try {
const { data, error } = await supabase
.from('monthly_aggregates')
.select(`
geoname_id,
month,
tmax_mean,
tmin_mean
`)
.eq('geoname_id', geonameId)
.order('month');
if (error) {
throw new Error('Failed to fetch aggregates');
}
return data as MonthlyAggregate[] || [];
} catch (error) {
console.error('Error fetching monthly weather data:', error);
throw error;
}
}
// Generate static params for popular cities
export async function generateStaticParams(): Promise<PageParams[]> {
try {
// Get all cities from supabase table "popular_cities" for build-time generation
const cities = await getStaticCities();
const params: PageParams[] = cities.map(city => ({
country: city.country_code.toLowerCase(),
city: createCitySlug(city.name), // just removes special characters and trims
geoname_id: city.geoname_id.toString()
}));
return params;
} catch (error) {
console.error('Error generating static params:', error);
// Return empty array to prevent build failure
return [];
}
}
// Helper function to get month name
function getMonthName(month: number): string {
const months = [
'January', 'February', 'March', 'April', 'May', 'June',
'July', 'August', 'September', 'October', 'November', 'December'
];
return months[month - 1] || `Month ${month}`;
}
// Helper function to format temperature
//... omitting
// Main page component - data fetching happens at build time due to static generation
export default async function MonthlyTestPage({
params
}: {
params: Promise<PageParams>;
}) {
const { country, city, geoname_id } = await params;
const geonameId = parseInt(geoname_id, 10);
try {
const [cityData, weatherData] = await Promise.all([
getCityData(geonameId),
getMonthlyWeatherData(geonameId)
]);
// ... omitting style definitions
return (
<div style={containerStyle}>
<div style={cityInfoStyle}>
<h1 style={cityTitleStyle}>{`Monthly Weather Data - ${cityData.name}, ${cityData.country_name}`}</h1>
<p style={cityParaStyle}><strong>Country:</strong> {`${cityData.country_name} (${cityData.country_code})`}</p>
<p style={cityParaStyle}><strong>Geoname ID:</strong> {cityData.geoname_id}</p>
</div>
{weatherData.length > 0 ? (
<table style={tableStyle}>
<thead>
<tr>
<th style={headerStyle}>Month</th>
<th style={headerStyle}>Max Temp</th>
<th style={headerStyle}>Min Temp</th>
</tr>
</thead>
<tbody>
{weatherData.map((data) => (
<tr key={data.month}>
<td style={cellStyle}>{getMonthName(data.month)}</td>
<td style={cellStyle}>{formatTemp(data.tmax_mean)}</td>
<td style={cellStyle}>{formatTemp(data.tmin_mean)}</td>
</tr>
))}
</tbody>
</table>
) : (
<p>No monthly data available for this city.</p>
)}
</div>
);
} catch (error) {
return (
<div>
<h1>Error Loading Data</h1>
<p>Failed to load city or weather data. Please try again later.</p>
</div>
);
}
}