87 lines
2.2 KiB
TypeScript
87 lines
2.2 KiB
TypeScript
"use client";
|
|
|
|
import { useAuth } from "@/context/AuthContext";
|
|
import { useCallback, useMemo } from "react";
|
|
|
|
const DEFAULT_API_BASE =
|
|
process.env.NEXT_PUBLIC_API_BASE_URL ?? "http://localhost:8080";
|
|
|
|
export function useApi(baseUrl: string = DEFAULT_API_BASE) {
|
|
const { token } = useAuth();
|
|
|
|
const apiFetch = useCallback(
|
|
async <T = any>(path: string, options: RequestInit = {}): Promise<T> => {
|
|
// Allow either full URLs or just `/api/...`
|
|
const url = path.startsWith("http")
|
|
? path
|
|
: `${baseUrl.replace(/\/$/, "")}${path}`;
|
|
|
|
const headers: HeadersInit = {
|
|
"Content-Type": "application/json",
|
|
...(options.headers || {}),
|
|
...(token ? { Authorization: `Bearer ${token}` } : {}),
|
|
};
|
|
|
|
const res = await fetch(url, {
|
|
...options,
|
|
headers,
|
|
});
|
|
|
|
if (!res.ok) {
|
|
let bodyText = "";
|
|
try {
|
|
bodyText = await res.text();
|
|
} catch {
|
|
// ignore
|
|
}
|
|
throw new Error(
|
|
`API error ${res.status}: ${
|
|
bodyText || res.statusText || "Unknown error"
|
|
}`
|
|
);
|
|
}
|
|
|
|
// handle 204 / empty body safely
|
|
try {
|
|
return (await res.json()) as T;
|
|
} catch {
|
|
return {} as T;
|
|
}
|
|
},
|
|
[baseUrl, token] // 👈 only changes when baseUrl or token change
|
|
);
|
|
|
|
const get = useCallback(
|
|
<T = any>(path: string) => apiFetch<T>(path, { method: "GET" }),
|
|
[apiFetch]
|
|
);
|
|
|
|
const post = useCallback(
|
|
<T = any>(path: string, body?: unknown) =>
|
|
apiFetch<T>(path, {
|
|
method: "POST",
|
|
body: body !== undefined ? JSON.stringify(body) : undefined,
|
|
}),
|
|
[apiFetch]
|
|
);
|
|
|
|
const put = useCallback(
|
|
<T = any>(path: string, body?: unknown) =>
|
|
apiFetch<T>(path, {
|
|
method: "PUT",
|
|
body: body !== undefined ? JSON.stringify(body) : undefined,
|
|
}),
|
|
[apiFetch]
|
|
);
|
|
|
|
const del = useCallback(
|
|
<T = any>(path: string) => apiFetch<T>(path, { method: "DELETE" }),
|
|
[apiFetch]
|
|
);
|
|
|
|
// Optional: memoize the returned object so destructuring doesn't change refs
|
|
return useMemo(
|
|
() => ({ apiFetch, get, post, put, del }),
|
|
[apiFetch, get, post, put, del]
|
|
);
|
|
} |