← back to Norma
app/api/fred/route.ts
60 lines
import { NextRequest, NextResponse } from 'next/server';
import { requireRole } from '@/lib/require-role';
const FRED_BASE = 'https://api.stlouisfed.org/fred';
// Key economic series for student debt advocacy
const SERIES_CATALOG: Record<string, { id: string; label: string; units: string }> = {
total_student_debt: { id: 'SLOAS', label: 'Total Student Loans Outstanding', units: 'Billions of $' },
avg_student_debt: { id: 'STDSL', label: 'Avg Student Loan Balance', units: 'USD' },
fed_funds_rate: { id: 'FEDFUNDS', label: 'Federal Funds Rate', units: 'Percent' },
delinquency_rate: { id: 'DRSFRMACBS', label: 'Delinquency Rate (Consumer)', units: 'Percent' },
unemployment_rate: { id: 'UNRATE', label: 'US Unemployment Rate', units: 'Percent' },
cpi: { id: 'CPIAUCSL', label: 'Consumer Price Index', units: 'Index 1982-84=100' },
};
/**
* GET /api/fred?action=series|observations&series=total_student_debt
*/
export async function GET(request: NextRequest) {
const auth = requireRole(request, 'admin', 'staff');
if (auth instanceof NextResponse) return auth;
const apiKey = process.env.FRED_API_KEY;
if (!apiKey) {
return NextResponse.json({ error: 'FRED_API_KEY not configured', catalog: SERIES_CATALOG }, { status: 503 });
}
const { searchParams } = new URL(request.url);
const action = searchParams.get('action') || 'catalog';
try {
if (action === 'catalog') {
return NextResponse.json({ catalog: SERIES_CATALOG });
}
if (action === 'observations') {
const seriesKey = searchParams.get('series') || 'total_student_debt';
const seriesInfo = SERIES_CATALOG[seriesKey];
if (!seriesInfo) return NextResponse.json({ error: 'Unknown series key' }, { status: 400 });
const limit = searchParams.get('limit') || '24';
const res = await fetch(
`${FRED_BASE}/series/observations?series_id=${seriesInfo.id}&api_key=${apiKey}&file_type=json&sort_order=desc&limit=${limit}`
);
if (!res.ok) throw new Error(`FRED returned ${res.status}`);
const data = await res.json();
return NextResponse.json({
series: seriesInfo,
observations: data.observations || [],
count: data.count || 0,
});
}
return NextResponse.json({ error: `Unknown action: ${action}` }, { status: 400 });
} catch (err) {
return NextResponse.json({ error: (err as Error).message }, { status: 500 });
}
}