profileShare

rasmusjy / tasteprint

Read-only snapshot

No repository description.

main default branch 169 files Expires Sep 13, 2026, 9:06 AM
api.ts 1,870 bytes
1 import type { ApiProblem } from "../types";
2
3 const API_BASE = import.meta.env.VITE_API_URL ?? "";
4 const TOKEN_KEY = "tasteprint.session";
5
6 export class ApiError extends Error {
7 status: number;
8 errors: Record<string, string>;
9
10 constructor(status: number, problem: ApiProblem) {
11 super(problem.detail || problem.title || "The request could not be completed.");
12 this.name = "ApiError";
13 this.status = status;
14 this.errors = problem.errors ?? {};
15 }
16 }
17
18 export function getSessionToken(): string | null {
19 return localStorage.getItem(TOKEN_KEY);
20 }
21
22 export function setSessionToken(token: string): void {
23 localStorage.setItem(TOKEN_KEY, token);
24 }
25
26 export function clearSessionToken(): void {
27 localStorage.removeItem(TOKEN_KEY);
28 }
29
30 export async function api<T>(path: string, init: RequestInit = {}): Promise<T> {
31 const headers = new Headers(init.headers);
32 const token = getSessionToken();
33 if (token) {
34 headers.set("Authorization", `Bearer ${token}`);
35 }
36 if (init.body && !(init.body instanceof FormData) && !headers.has("Content-Type")) {
37 headers.set("Content-Type", "application/json");
38 }
39 headers.set("Accept", "application/json");
40
41 const response = await fetch(`${API_BASE}${path}`, { ...init, headers });
42 if (!response.ok) {
43 let problem: ApiProblem;
44 try {
45 problem = await response.json() as ApiProblem;
46 } catch {
47 problem = { detail: `Request failed with status ${response.status}.` };
48 }
49 if (response.status === 401) {
50 clearSessionToken();
51 window.dispatchEvent(new Event("tasteprint:unauthorized"));
52 }
53 throw new ApiError(response.status, problem);
54 }
55 if (response.status === 204) {
56 return undefined as T;
57 }
58 return response.json() as Promise<T>;
59 }
60
61 export function jsonBody(value: unknown): Pick<RequestInit, "body"> {
62 return { body: JSON.stringify(value) };
63 }
64