haixunMaster/lib/threads-api/client.ts

66 lines
1.6 KiB
TypeScript

const GRAPH_BASE = "https://graph.threads.net/v1.0";
export class ThreadsApiError extends Error {
constructor(
message: string,
public readonly code?: number,
public readonly subcode?: number
) {
super(message);
this.name = "ThreadsApiError";
}
}
async function parseJson(res: Response): Promise<Record<string, unknown>> {
const text = await res.text();
try {
return JSON.parse(text) as Record<string, unknown>;
} catch {
throw new ThreadsApiError(text || `HTTP ${res.status}`, res.status);
}
}
export async function threadsGraphGet<T>(
path: string,
params: Record<string, string>
): Promise<T> {
const url = new URL(`${GRAPH_BASE}${path}`);
for (const [key, value] of Object.entries(params)) {
url.searchParams.set(key, value);
}
const res = await fetch(url.toString(), { method: "GET" });
const json = await parseJson(res);
if (!res.ok) {
throw new ThreadsApiError(
String(json.error_message ?? json.error ?? "Threads API 請求失敗"),
typeof json.code === "number" ? json.code : res.status
);
}
return json as T;
}
export async function threadsGraphPost<T>(
path: string,
params: Record<string, string>
): Promise<T> {
const body = new URLSearchParams(params);
const res = await fetch(`${GRAPH_BASE}${path}`, {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body,
});
const json = await parseJson(res);
if (!res.ok) {
throw new ThreadsApiError(
String(json.error_message ?? json.error ?? "Threads API 請求失敗"),
typeof json.code === "number" ? json.code : res.status
);
}
return json as T;
}