Dernière activité 1710515426

requests.ts Brut
1export enum Method {
2 GET = "GET",
3 POST = "POST",
4 PUT = "PUT",
5 DELETE = "DELETE",
6 PATCH = "PATCH",
7}
8
9export type ResponseInterceptor = (r: Response, rq?: RequestInit) => void;
10
11export interface TResponse<T> {
12 status: number;
13 error: boolean;
14 data: T;
15 response: Response;
16}
17
18export type RequestArgs<T> = {
19 url: string;
20 body?: T;
21 data?: FormData;
22 headers?: Record<string, string>;
23};
24
25export class Requests {
26 private headers: Record<string, string> = {};
27 private responseInterceptors: ResponseInterceptor[] = [];
28 private bearer: () => string | null = () => null;
29
30 withResponseInterceptor(interceptor: ResponseInterceptor) {
31 this.responseInterceptors.push(interceptor);
32 return this;
33 }
34
35 withBearer(bearer: () => string | null) {
36 this.bearer = bearer;
37 return this;
38 }
39
40 private callResponseInterceptors(response: Response, request?: RequestInit) {
41 this.responseInterceptors.forEach(i => i(response, request));
42 }
43
44 constructor(headers: Record<string, string> = {}) {
45 this.headers = headers;
46 }
47
48 public get<T>(args: RequestArgs<T>): Promise<TResponse<T>> {
49 return this.do<T>(Method.GET, args);
50 }
51
52 public post<T, U>(args: RequestArgs<T>): Promise<TResponse<U>> {
53 return this.do<U>(Method.POST, args);
54 }
55
56 public put<T, U>(args: RequestArgs<T>): Promise<TResponse<U>> {
57 return this.do<U>(Method.PUT, args);
58 }
59
60 public delete<T>(args: RequestArgs<T>): Promise<TResponse<T>> {
61 return this.do<T>(Method.DELETE, args);
62 }
63
64 public patch<T, U>(args: RequestArgs<T>): Promise<TResponse<U>> {
65 return this.do<U>(Method.PATCH, args);
66 }
67
68 private methodSupportsBody(method: Method): boolean {
69 return method === Method.POST || method === Method.PUT || method === Method.PATCH;
70 }
71
72 private async do<T>(method: Method, rargs: RequestArgs<unknown>): Promise<TResponse<T>> {
73 const payload: RequestInit = {
74 method,
75 headers: {
76 ...rargs.headers,
77 ...this.headers,
78 } as Record<string, string>,
79 };
80
81 if (this.methodSupportsBody(method)) {
82 if (rargs.data) {
83 payload.body = rargs.data;
84 } else {
85 // @ts-expect-error - we know that the header is there
86 payload.headers["Content-Type"] = "application/json";
87 payload.body = JSON.stringify(rargs.body);
88 }
89 }
90
91 const maybeBearer = this.bearer();
92 if (maybeBearer) {
93 // @ts-expect-error - we know that the header is there
94 payload.headers.Authorization = `Bearer ${maybeBearer}`;
95 }
96
97 const response = await fetch(rargs.url, payload);
98 this.callResponseInterceptors(response, payload);
99
100 const data: T = await (async () => {
101 if (response.status === 204) {
102 return {} as T;
103 }
104
105 if (response.headers.get("Content-Type")?.startsWith("application/json")) {
106 try {
107 return await response.json();
108 } catch (e) {
109 return {} as T;
110 }
111 }
112
113 return response.body as unknown as T;
114 })();
115
116 return {
117 status: response.status,
118 error: !response.ok,
119 data,
120 response,
121 };
122 }
123}
124