-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathhttpClient.ts
More file actions
126 lines (106 loc) · 3.14 KB
/
httpClient.ts
File metadata and controls
126 lines (106 loc) · 3.14 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
/**
* Author: Kate, Zhang
* Date: 03/18/2023
* Update At: 03/20/2023
*/
export default class HttpClient {
private readonly baseUri: string;
private readonly options: HttpClientOptions;
constructor(baseUri: string, options?: HttpClientOptions) {
this.baseUri = baseUri;
this.options = options ?? {};
}
private getRequestBody(body: unknown): { body: RequestInit["body"] } {
if (body instanceof FormData) {
return { body };
} else if (typeof body === "object" && body !== null) {
return { body: JSON.stringify(body) };
}
return { body } as { body: RequestInit["body"] };
}
private async request<T>(conf: RequestConf): Promise<T | undefined> {
const { apiPath, data, method, query } = conf;
const search = query ? `?${new URLSearchParams(query).toString()}` : "";
const url = `${this.baseUri}/${apiPath}${search}`;
const ignoreHeader = data instanceof FormData;
const reqestInit = {
...this.options,
method: method.toUpperCase(),
...(data !== undefined && this.getRequestBody(data)),
// to upload multiplepart file: https://stackoverflow.com/questions/36067767/how-do-i-upload-a-file-with-the-js-fetch-api
...(!ignoreHeader && {
headers: {
...this.options.headers,
...conf.headers,
},
}),
};
const response = await fetch(url, reqestInit);
const text = await response.text();
// make sure the response http code is between 200~299
if (response.ok) {
// the response body could be empty
if (text) {
try {
return JSON.parse(text) as T;
} catch {
// the response could be a string
return text as T;
}
}
return;
}
throw new Error(`HTTP error! status: ${response.status}, info: ${text}`);
}
delete<T>(conf: Omit<RequestConf, "method">): Promise<T | undefined> {
return this.request({
...conf,
method: "DELETE",
});
}
get<T>(conf: Omit<RequestConf, "method" | "data">): Promise<T | undefined> {
return this.request({
...conf,
method: "GET",
});
}
patch<T>(conf: UpdateRequestConf): Promise<T | undefined> {
return this.request({
...conf,
method: "PATCH",
});
}
post<T>(conf: UpdateRequestConf): Promise<T | undefined> {
return this.request({
...conf,
method: "POST",
});
}
put<T>(conf: UpdateRequestConf): Promise<T | undefined> {
return this.request({
...conf,
method: "PUT",
});
}
}
export type HttpClientOptions = Pick<
RequestInit,
"credentials" | "headers" | "mode"
>;
/*
export interface HttpClientOptions {
mode?: "no-cors" | "cors" | "same-origin";
creadentials?: "omit" | "same-origin" | "include"; // the mechinsm about brining cookie to BE
headers?: Record<string, string>;
}*/
interface RequestConf {
apiPath: string;
data?: unknown;
method: "DELETE" | "GET" | "POST" | "PATCH" | "PUT";
query?: Record<string, string> | string[][];
headers?: HttpClientOptions["headers"];
}
interface UpdateRequestConf
extends Required<Pick<RequestConf, "data" | "apiPath">> {
headers: RequestConf["headers"];
}