fetch-buddy
Version:
A TypeScript API client that creates fetch requests from a staticly typed structured object
103 lines (102 loc) • 3.32 kB
JavaScript
import { RequestError, formatStructuredApiRequest } from "./utils.js";
export class FetchBuddy {
_baseUrl;
_headers;
constructor({ domain, version }) {
this._baseUrl = `${domain}${version ? `/${version}` : ""}`;
this._headers = new Headers();
}
get headers() {
return this._headers;
}
async getResponseError(res, error) {
const errorText = await res.text();
return new RequestError({
code: res.status,
data: { message: errorText },
message: error instanceof Error ? error.message : String(error),
});
}
getRequestError(error) {
return new RequestError({
code: 500,
data: {},
message: error instanceof Error ? error.message : String(error),
});
}
async handleResponseOk(res) {
try {
// Check if response is explicitly empty (No Content)
if (res.status === 204 || res.status === 205) {
return null;
}
// Check if Content-Length header is 0 (not guaranteed in all responses)
const contentLength = res.headers.get("Content-Length");
if (contentLength === "0") {
return null;
}
// Check Content-Type before parsing
const contentType = res.headers.get("Content-Type") || "";
if (contentType.includes("application/json")) {
return await res.json();
}
if (contentType.includes("text/")) {
return await res.text();
}
return res; // Return raw response for unknown types
}
catch (error) {
throw await this.getResponseError(res.clone(), error);
}
}
async request(url, init) {
try {
const reqUrl = this._baseUrl.concat(url);
const reqHeaders = Object.fromEntries(this.headers);
const res = await fetch(reqUrl, {
...init,
headers: {
...reqHeaders,
...(init?.headers ?? {}),
},
});
if (res.ok) {
return (await this.handleResponseOk(res));
}
throw await this.getResponseError(res, res.statusText);
}
catch (error) {
if (error instanceof RequestError) {
throw error;
}
throw this.getRequestError(error);
}
}
get(args, init) {
const url = typeof args === "string"
? args
: formatStructuredApiRequest(args);
return this.request(url, {
...init,
method: "GET",
headers: {
"Content-Type": "application/json",
...(init?.headers ?? {}),
},
});
}
post(args, body, init) {
const url = typeof args === "string"
? args
: formatStructuredApiRequest(args);
return this.request(url, {
...init,
method: "POST",
body: JSON.stringify(body),
headers: {
"Content-Type": "application/json",
...(init?.headers ?? {}),
},
});
}
}