dipper/src/lib/api/request.ts

95 lines
2.4 KiB
TypeScript

import { PUBLIC_API_BASE } from '$env/static/public';
import type { Cookies } from '@sveltejs/kit';
import { passthroughSession } from './cookies';
export class APIError extends Error {
readonly code?: string;
constructor(
readonly response: Response,
body?: { code: string; message: string }
) {
super(body?.message ?? response.statusText);
this.code = body?.code;
Object.setPrototypeOf(this, APIError.prototype);
}
}
export type Stringer = { toString(): string };
export type QuerySerializable = { [key: string | number | symbol]: Stringer | null | undefined };
export function withQuery(path: string, query?: QuerySerializable) {
return query ? `${path}?${asQueryParams(query).toString()}` : path;
}
export function asQueryParams<T extends QuerySerializable>(obj: T) {
const params = new URLSearchParams();
for (const key of Object.getOwnPropertyNames(obj)) {
const value = obj[key];
if (value == null || typeof value === 'undefined') continue;
params.append(key, value.toString());
}
return params;
}
type Body = { [key: string]: unknown } | object | void;
export async function apiCall<B extends Body>(
method: string,
path: string,
body?: B,
cookies?: Cookies
) {
const session = cookies?.get('session');
const response = await fetch(`${PUBLIC_API_BASE}/${path}`, {
method,
body: body ? JSON.stringify(body) : undefined,
credentials: 'same-origin',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
Cookie: session ? `session=${session}` : ''
}
});
if (cookies) {
passthroughSession(response, cookies);
}
if (response.ok) {
return response;
}
// Check for content
if (response.headers.get('Content-Type')?.includes('application/json')) {
const body = await response.json();
throw new APIError(response, body);
}
throw new APIError(response);
}
export async function callJSON<R, B extends Body = void>(
method: string,
path: string,
body?: B,
cookies?: Cookies
) {
return apiCall(method, path, body, cookies).then((response) => response.json() as R);
}
export async function callText<B extends Body = void>(
method: string,
path: string,
body?: B,
cookies?: Cookies
) {
return apiCall(method, path, body, cookies).then((response) => response.text());
}
export async function callBytes<B extends Body = void>(
method: string,
path: string,
body?: B,
cookies?: Cookies
) {
return apiCall(method, path, body, cookies).then((response) => response.blob());
}