Skip to content

Instantly share code, notes, and snippets.

@themakunga
Last active August 21, 2024 13:28
Show Gist options
  • Save themakunga/75be7fadd972708893e640f890b093db to your computer and use it in GitHub Desktop.
Save themakunga/75be7fadd972708893e640f890b093db to your computer and use it in GitHub Desktop.
type safe fetch wrapper
export enum Methods {
post = 'POST',
get = 'GET',
put = 'PUT',
patch = 'PATCH',
delete = 'DELETE',
head = 'HEAD',
options = 'OPTIONS',
}
const bodyContentMethods = ['post', 'put', 'patch'];
interface RequestBasic {
method: Methods;
url: string;
path?: string;
headers: Headers;
}
interface RequestWithoutBody extends RequestBasic {
body?: never;
}
interface RequestWithBody<T> extends RequestBasic {
body: T;
}
export type RequestPayload<T = void> = RequestWithoutBody | RequestWithBody<T>;
interface FetchRequestOptions {
headers: Headers;
method: string;
body?: string;
}
export async function request<T = void, U = void>(
req: RequestPayload<T>,
): Promise<U> {
const options: FetchRequestOptions = {
headers: req.headers,
method: req.method.toUpperCase(),
};
const fullUrl = `${req.url}${req.path ? req.path : ''}`;
const bodyMethod = bodyContentMethods.includes(req.method.toLowerCase());
if (bodyMethod && req.body) {
options.headers.append('Content-Type', 'Application/json');
options.body = JSON.stringify(req.body);
}
if (bodyMethod && !req.body) {
throw new Error(`Request using ${req.method} method must include a body`);
}
return fetch(fullUrl, options)
.then((res) => {
if (!res.ok) {
throw new Error(`${res.status} | ${res.statusText}`);
}
return res.json() as Promise<{ data: U }>;
})
.then((data) => {
return data.data;
})
.catch((err) => {
throw new Error(err.message);
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment