Last active
September 19, 2020 13:44
-
-
Save alexeyraspopov/7080c76d360554461149d5603f6fa41e to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* @flow */ | |
/** | |
* @example Simple GET request | |
* let users = await request('/api/users'); | |
* | |
* @example Cancellable GET request | |
* let [result, abort] = request('/api/users', { cancellable: true }); | |
* | |
* @example Simple POST request | |
* let task = await request('/api/tasks', { | |
* method: 'POST', | |
* body: { title, description }, | |
* }); | |
* | |
* @example Cancellable POST request | |
* let [result, abort] = request('/api/tasks', { | |
* cancellable: true, | |
* method: 'POST', | |
* body: { title, description }, | |
* }); | |
*/ | |
// Default get request that returns a promise | |
declare function request<Payload>( | |
url: string, | |
options?: { | |
method?: 'GET', | |
headers?: { [string]: string } | Headers, | |
signal?: AbortSignal, | |
}, | |
): Promise<Payload>; | |
// Cancellable get request that returns a pair of promise and abort function | |
declare function request<Payload>( | |
url: string, | |
options?: { | |
method?: 'GET', | |
headers?: { [string]: string } | Headers, | |
cancellable: true, | |
}, | |
): [Promise<Payload>, () => void]; | |
// Default actionable request with body that returns a promise | |
declare function request<Payload>( | |
url: string, | |
options?: { | |
method: 'POST' | 'PATCH' | 'PUT', | |
body?: string | FormData | { ... }, | |
headers?: { [string]: string } | Headers, | |
signal?: AbortSignal, | |
}, | |
): Promise<Payload>; | |
// Cancellable actionable request with body that returns a pair of promise and abort function | |
declare function request<Payload>( | |
url: string, | |
options?: { | |
method: 'POST' | 'PATCH' | 'PUT', | |
body?: string | FormData | { ... }, | |
headers?: { [string]: string } | Headers, | |
cancellable: true, | |
}, | |
): [Promise<Payload>, () => void]; | |
// Default body-less request that returns a promise without any data | |
declare function request<Payload>( | |
url: string, | |
options?: { | |
method: 'HEAD' | 'DELETE', | |
headers?: { [string]: string } | Headers, | |
signal?: AbortSignal, | |
}, | |
): Promise<void>; | |
// Cancellable body-less request that returns a pair of promise without any data and abort function | |
declare function request<Payload>( | |
url: string, | |
options?: { | |
method: 'HEAD' | 'DELETE', | |
headers?: { [string]: string } | Headers, | |
cancellable: true, | |
}, | |
): [Promise<void>, () => void]; | |
export default function request(url, options = {}) { | |
let method = options.method ?? 'GET'; | |
let headers = options.headers ?? { 'Content-Type': 'application/json' }; | |
// In a case of body being an object, let's stringify it so the user don't need to | |
let body = | |
options.body instanceof FormData || typeof options.body === 'string' | |
? options.body | |
: options.body && JSON.stringify(options.body); | |
// Despite providing cancellable API, there are cases where we need special access to the controller | |
let signal = options.signal; | |
if (options.cancellable) { | |
let ctl = new AbortController(); | |
let signal = ctl.signal; | |
let response = makeRequest(url, { method, headers, body, signal }); | |
return [response, () => ctl.abort()]; | |
} else { | |
return makeRequest(url, { method, headers, body, signal }); | |
} | |
} | |
async function makeRequest(url, options) { | |
let response = await fetch(url, options); | |
// 4xx requests include a JSON payload that describes the error | |
if (response.status >= 400 && response.status < 500) { | |
let payload = await response.json(); | |
throw payload; | |
} | |
// Otherwise, fallback to status text | |
if (!response.ok) { | |
throw new Error(response.statusText); | |
} | |
if (response.status !== 204 && options.method !== 'HEAD') { | |
return response.json(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment