wayfarer-weather/app/api/weather/route.ts

58 lines
2.5 KiB
TypeScript
Raw Normal View History

2026-10-31 21:20:16 -05:00
import { NextResponse, type NextRequest } from "next/server";
import { getLocalContext, type LocalContext } from "@/lib/weather/extras/air-quality";
import { getNwsAlerts } from "@/lib/weather/extras/nws-alerts";
import { isProviderId, isReady, PROVIDERS } from "@/lib/weather/providers";
import { badRequest, errorResponse, forbidCrossSite, parseCoords } from "@/lib/weather/respond";
import type { WeatherAlert, WeatherReport } from "@/lib/weather/types";
/**
* GET /api/weather?provider=openweather&lat=44.77&lon=-93.27
*
* Runs on the server so API keys stay in .env / .env.local and never reach the browser.
*
* Three things are fetched at once:
* 1. the forecast from the chosen provider (required — failure is an error)
* 2. air quality + time zone from Open-Meteo (optional — failure just hides it)
* 3. NWS alerts for US locations (optional — failure just hides them)
*/
export async function GET(request: NextRequest) {
const blocked = forbidCrossSite(request);
if (blocked) return blocked;
const params = request.nextUrl.searchParams;
const providerId = params.get("provider") ?? "openweather";
const coords = parseCoords(params);
if (!isProviderId(providerId)) return badRequest(`Unknown provider "${providerId}"`);
if (!coords) return badRequest("lat and lon are required");
const provider = PROVIDERS[providerId];
if (!isReady(provider)) return badRequest(`${provider.label} has no API key configured`);
const [forecast, context, nwsAlerts] = await Promise.allSettled([
provider.getForecast(coords.lat, coords.lon),
getLocalContext(coords.lat, coords.lon),
getNwsAlerts(coords.lat, coords.lon),
]);
if (forecast.status === "rejected") return errorResponse(forecast.reason);
const local: LocalContext | null = context.status === "fulfilled" ? context.value : null;
const official: WeatherAlert[] = nwsAlerts.status === "fulfilled" ? nwsAlerts.value : [];
// Prefer official NWS alerts in the US; elsewhere use whatever the provider gave us.
const alerts = official.length > 0 ? official : forecast.value.alerts;
const alertSource = official.length > 0 ? "National Weather Service" : alerts.length > 0 ? provider.label : null;
const report: WeatherReport = {
...forecast.value,
provider: providerId,
timeZone: forecast.value.timeZone ?? local?.timeZone ?? null,
airQuality: local?.airQuality ?? null,
alerts,
alertSource,
fetchedAt: new Date().toISOString(),
};
return NextResponse.json(report);
}