disclosure-bureau/web/app/api/relations/route.ts

61 lines
1.8 KiB
TypeScript
Raw Permalink Normal View History

/**
* /api/relations read typed relations for an entity.
*
* GET /api/relations?class=person&id=j-edgar-hoover
* Returns relations where this entity is source OR target,
* grouped by relation_type and direction.
*/
import { NextRequest } from "next/server";
import { pgQuery } from "@/lib/retrieval/db";
export const runtime = "nodejs";
export const dynamic = "force-dynamic";
function json(data: unknown, status = 200) {
return new Response(JSON.stringify(data), {
status,
headers: { "content-type": "application/json" },
});
}
interface Relation {
source_class: string;
source_id: string;
relation_type: string;
target_class: string;
target_id: string;
evidence_ref: string | null;
confidence: string;
}
export async function GET(req: NextRequest) {
const u = new URL(req.url);
const cls = u.searchParams.get("class") ?? "";
const id = u.searchParams.get("id") ?? "";
if (!cls || !id) return json({ error: "class and id required" }, 400);
try {
const outgoing = await pgQuery<Relation>(
`SELECT source_class, source_id, relation_type, target_class, target_id,
evidence_ref, confidence
FROM public.relations
WHERE source_class = $1 AND source_id = $2
ORDER BY confidence DESC, relation_type, target_class, target_id
LIMIT 200`,
[cls, id],
);
const incoming = await pgQuery<Relation>(
`SELECT source_class, source_id, relation_type, target_class, target_id,
evidence_ref, confidence
FROM public.relations
WHERE target_class = $1 AND target_id = $2
ORDER BY confidence DESC, relation_type, source_class, source_id
LIMIT 200`,
[cls, id],
);
return json({ outgoing, incoming });
} catch (e) {
return json({ error: "db_unavailable", message: (e as Error).message }, 503);
}
}