-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathfetcher.ts
136 lines (122 loc) · 3.92 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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
import { createError } from './error-handling.ts'
import { objectToQueryString } from './url.ts'
type UrlParams = Record<string, string | number | boolean | null | undefined>
type SendReqOptions<TPayload, TParams extends UrlParams> = Omit<RequestInit, 'body'> & {
url: string
payload?: TPayload
params?: TParams
gql?: string
}
const send = async <TResponse, TPayload, TParams extends UrlParams>({
url,
params,
payload,
gql,
...options
}: SendReqOptions<TPayload, TParams>) => {
const defaultOptions: RequestInit = {}
if (gql) {
defaultOptions.method = 'POST'
defaultOptions.body = JSON.stringify({ query: gql, variables: payload })
} else if (options.method && options.method.toLowerCase() !== 'get') {
defaultOptions.body = payload === undefined ? null : JSON.stringify(payload)
}
const finalUrl = params ? [url, objectToQueryString(params)].join('?') : url
const finalOptions = {
headers: { 'Content-Type': 'application/json', ...options.headers },
...defaultOptions,
...options,
}
const res = await fetch(finalUrl, finalOptions)
const contentType = res.headers.get('content-type')
const isJsonFile = /\.json(\?.+)?$/i.test(finalUrl)
if (contentType?.includes('application/json') || isJsonFile) {
const resJson = await res.json()
if (gql) {
if (resJson.errors) {
throw createError('Error GraphQL response', {
contentType,
status: res.status,
statusText: res.statusText,
response: resJson.errors,
request: finalOptions,
})
}
return resJson.data as TResponse
}
if (res.ok) return resJson as TResponse
throw createError('Fetch error', {
contentType,
status: res.status,
statusText: res.statusText,
response: resJson,
request: finalOptions,
})
}
// Try getting raw text response, then throw error with that text response.
const resText = await res.text().catch(() => undefined)
throw createError('Response type is not a JSON', {
contentType,
status: res.status,
statusText: res.statusText,
response: resText,
request: finalOptions,
})
}
type SendHttpReq = {
get: <
TResponse,
TParams extends UrlParams = Record<string, string | number | boolean | null | undefined>,
>(
options: Omit<RequestInit, 'body'> & { url: string; params?: TParams },
) => Promise<TResponse>
post: <
TResponse,
TPayload = any,
TParams extends UrlParams = Record<string, string | number | boolean | null | undefined>,
>(
options: Omit<RequestInit, 'body'> & { url: string; params?: TParams; payload?: TPayload },
) => Promise<TResponse>
put: <
TResponse,
TPayload = any,
TParams extends UrlParams = Record<string, string | number | boolean | null | undefined>,
>(
options: Omit<RequestInit, 'body'> & { url: string; params?: TParams; payload?: TPayload },
) => Promise<TResponse>
delete: <
TResponse,
TPayload = any,
TParams extends UrlParams = Record<string, string | number | boolean | null | undefined>,
>(
options: Omit<RequestInit, 'body'> & { url: string; params?: TParams; payload?: TPayload },
) => Promise<TResponse>
gql: <TResponse, TPayload = any>(
options: Omit<RequestInit, 'body'> & { url: string; gql: string; payload?: TPayload },
) => Promise<TResponse>
}
/**
* Send HTTP request.
*/
export const http: SendHttpReq = {
/**
* Send HTTP request with GET method.
*/
get: (options) => send({ method: 'get', ...options }),
/**
* Send HTTP request with POST method.
*/
post: (options) => send({ method: 'post', ...options }),
/**
* Send HTTP request with PUT method.
*/
put: (options) => send({ method: 'put', ...options }),
/**
* Send HTTP request with DELETE method.
*/
delete: (options) => send({ method: 'delete', ...options }),
/**
* Send HTTP request for GraphQL server.
*/
gql: (options) => send({ method: 'post', ...options }),
}