export enum Method { GET = "GET", POST = "POST", PUT = "PUT", DELETE = "DELETE", } export type RequestInterceptor = (r: Response) => void; export type ResponseInterceptor = (r: Response) => void; export interface TResponse { status: number; error: boolean; data: T; response: Response; } export type RequestArgs = { url: string; body?: T; data?: FormData; headers?: Record; }; export class Requests { private baseUrl: string; private token: () => string; private headers: Record = {}; private responseInterceptors: ResponseInterceptor[] = []; addResponseInterceptor(interceptor: ResponseInterceptor) { this.responseInterceptors.push(interceptor); } private callResponseInterceptors(response: Response) { this.responseInterceptors.forEach(i => i(response)); } private url(rest: string): string { return this.baseUrl + rest; } constructor(baseUrl: string, token: string | (() => string) = "", headers: Record = {}) { this.baseUrl = baseUrl; this.token = typeof token === "string" ? () => token : token; this.headers = headers; } public get(args: RequestArgs): Promise> { return this.do(Method.GET, args); } public post(args: RequestArgs): Promise> { return this.do(Method.POST, args); } public put(args: RequestArgs): Promise> { return this.do(Method.PUT, args); } public delete(args: RequestArgs): Promise> { return this.do(Method.DELETE, args); } private methodSupportsBody(method: Method): boolean { return method === Method.POST || method === Method.PUT; } private async do(method: Method, rargs: RequestArgs): Promise> { const payload: RequestInit = { method, headers: { ...rargs.headers, ...this.headers, } as Record, }; const token = this.token(); if (token !== "" && payload.headers !== undefined) { payload.headers["Authorization"] = token; // eslint-disable-line dot-notation } if (this.methodSupportsBody(method)) { if (rargs.data) { payload.body = rargs.data; } else { payload.headers["Content-Type"] = "application/json"; payload.body = JSON.stringify(rargs.body); } } const response = await fetch(this.url(rargs.url), payload); this.callResponseInterceptors(response); const data: T = await (async () => { if (response.status === 204) { return {} as T; } if (response.headers.get("Content-Type")?.startsWith("application/json")) { try { return await response.json(); } catch (e) { return {} as T; } } return response.body as unknown as T; })(); return { status: response.status, error: !response.ok, data, response, }; } }