-
Notifications
You must be signed in to change notification settings - Fork 61
/
fetcher.ts
122 lines (114 loc) · 2.78 KB
/
fetcher.ts
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
import { camel, pascal } from "case";
/**
* Get fetcher template
*
* @param contextPath import the context from another file
*/
export const getFetcher = ({
prefix,
contextPath,
baseUrl,
}: {
prefix: string;
contextPath?: string;
baseUrl?: string;
}) =>
`${
contextPath
? `import { ${pascal(prefix)}Context } from "./${contextPath}";`
: `export type ${pascal(prefix)}FetcherExtraProps = {
/**
* You can add some extra props to your generated fetchers.
*
* Note: You need to re-gen after adding the first property to
* have the \`${pascal(prefix)}FetcherExtraProps\` injected in \`${pascal(
prefix
)}Components.ts\`
**/
}`
}
const baseUrl = ${baseUrl ? `"${baseUrl}"` : `""; // TODO add your baseUrl`}
export type ErrorWrapper<TError> =
| TError
| { status: "unknown"; payload: string };
export type ${pascal(
prefix
)}FetcherOptions<TBody, THeaders, TQueryParams, TPathParams> = {
url: string;
method: string;
body?: TBody;
headers?: THeaders;
queryParams?: TQueryParams;
pathParams?: TPathParams;
} & ${
contextPath
? `${pascal(prefix)}Context["fetcherOptions"];`
: `${pascal(prefix)}FetcherExtraProps`
}
export async function ${camel(prefix)}Fetch<
TData,
TError,
TBody extends {} | undefined | null,
THeaders extends {},
TQueryParams extends {},
TPathParams extends {}
>({
url,
method,
body,
headers,
pathParams,
queryParams,
}: ${pascal(prefix)}FetcherOptions<
TBody,
THeaders,
TQueryParams,
TPathParams
>): Promise<TData> {
try {
const response = await window.fetch(\`\${baseUrl}\${resolveUrl(url, queryParams, pathParams)}\`,
{
method: method.toUpperCase(),
body: body ? JSON.stringify(body) : undefined,
headers: {
"Content-Type": "application/json",
...headers,
},
}
);
if (!response.ok) {
let error: ErrorWrapper<TError>;
try {
error = await response.json();
} catch (e) {
error = {
status: "unknown" as const,
payload:
e instanceof Error
? \`Unexpected error (\${e.message})\`
: "Unexpected error"
};
}
throw error;
}
return await response.json();
} catch (e) {
throw {
status: "unknown" as const,
payload:
e instanceof Error
? \`Network error (\${e.message})\`
: "Network error"
}
}
}
const resolveUrl = (
url: string,
queryParams: Record<string, string> = {},
pathParams: Record<string, string> = {}
) => {
let query = new URLSearchParams(queryParams).toString();
if (query) query = \`?\${query}\`;
return url.replace(/\\{\\w*\\}/g, (key) => pathParams[key.slice(1, -1)]) + query;
};
`;