SPB Git

spb/airiskindex Public

The most methodologically rigorous, fully transparent AI job-exposure index.

TypeScript 88% Python 6.1% SQL 2.7% CSS 1.2% JavaScript 0.9% Shell 0.8%
1.6 KB · 50 lines typescript
Raw Blame History
1//  File:    route.ts2//  Path:    apps/web/app/api/v1/occupations/route.ts3//  Project: AI Risk Index — airiskindex.io4//  Author:  Simon-Pierre Boucher5//  Contact: contact@spboucher.ai6//  Copyright © 2026 Simon-Pierre Boucher. All rights reserved.7//8//  Description: Public API: occupation list/search (paginated).910import { prisma } from "@airiskindex/db";11import { INDEX_VERSION } from "@airiskindex/scoring";12import type { NextRequest } from "next/server";1314export const dynamic = "force-dynamic";1516export async function GET(request: NextRequest): Promise<Response> {17  const { searchParams } = new URL(request.url);18  const q = searchParams.get("q") ?? undefined;19  const page = Math.max(1, Number(searchParams.get("page") ?? "1") || 1);20  const perPage = Math.min(100, Math.max(1, Number(searchParams.get("per_page") ?? "25") || 25));2122  const where = q23    ? {24        OR: [25          { title: { contains: q, mode: "insensitive" as const } },26          { code: { startsWith: q } },27        ],28      }29    : {};3031  try {32    const [total, items] = await Promise.all([33      prisma.occupation.count({ where }),34      prisma.occupation.findMany({35        where,36        orderBy: { code: "asc" },37        skip: (page - 1) * perPage,38        take: perPage,39        select: { code: true, title: true },40      }),41    ]);42    return Response.json(43      { index_version: INDEX_VERSION, page, per_page: perPage, total, items },44      { headers: { "Cache-Control": "public, s-maxage=3600, stale-while-revalidate=600" } },45    );46  } catch {47    return Response.json({ error: "database_unavailable" }, { status: 503 });48  }49}50