UNPKG

@dvsa/appdev-api-common

Version:

Utils library for common API functionality

155 lines (154 loc) 5.84 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.HTTP = exports.HTTPError = void 0; class HTTPError extends Error { response; constructor(message, response) { super(message); this.response = response; this.name = "HTTPError"; this.response = response; } } exports.HTTPError = HTTPError; // biome-ignore lint/complexity/noStaticOnlyClass: makes sense for an HTTP utility to encompass all methods class HTTP { /** * Performs an HTTP GET request. * Note: This method will throw an HTTPError if the response is not ok (status code 200-299) to emulate Axios behaviour. * @param url * @param options */ static async get(url, options) { const response = await fetch(url, { method: "GET", ...options }); const serialisedResponse = await HTTP.serialise(response); if (!response.ok) { throw new HTTPError(`HTTP GET request failed with status ${response.status}`, serialisedResponse); } return serialisedResponse; } /** * Performs an HTTP POST request. * Note: This method will throw an HTTPError if the response is not ok (status code 200-299) to emulate Axios behaviour. * @param url * @param body * @param options */ static async post(url, body, options) { const { contentType, processedBody } = HTTP.prepareBody(body); const response = await fetch(url, { ...options, method: "POST", headers: { ...(contentType && { "Content-Type": contentType }), ...options?.headers, }, body: processedBody, }); const serialisedResponse = await HTTP.serialise(response); if (!response.ok) { throw new HTTPError(`HTTP POST request failed with status ${response.status}`, serialisedResponse); } return serialisedResponse; } /** * Performs an HTTP PUT request. * Note: This method will throw an HTTPError if the response is not ok (status code 200-299) to emulate Axios behaviour. * @param url * @param body * @param options */ static async put(url, body, options) { const { contentType, processedBody } = HTTP.prepareBody(body); const response = await fetch(url, { ...options, method: "PUT", headers: { ...(contentType && { "Content-Type": contentType }), ...options?.headers, }, body: processedBody, }); const serialisedResponse = await HTTP.serialise(response); if (!response.ok) { throw new HTTPError(`HTTP PUT request failed with status ${response.status}`, serialisedResponse); } return serialisedResponse; } /** * Performs an HTTP DELETE request. * Note: This method will throw an HTTPError if the response is not ok (status code 200-299) to emulate Axios behaviour. * @param url * @param options */ static async delete(url, options) { const response = await fetch(url, { method: "DELETE", ...options }); const serialisedResponse = await HTTP.serialise(response); if (!response.ok) { throw new HTTPError(`HTTP DELETE request failed with status ${response.status}`, serialisedResponse); } return serialisedResponse; } static async serialise(response) { let body; try { // Clone the response so we don't consume the original body const clonedResponse = response.clone(); // Extract the content-type header so we know how to serialise it const contentType = response.headers.get("content-type") || ""; // Check if the JSON header is present if (contentType.includes("application/json")) { // parse to text first const text = await clonedResponse.text(); // if there is an empty body e.g. 204, calling `.json()` would cause an error to be thrown, therefore check length body = text.trim().length ? JSON.parse(text) : null; } // Check if the body is a buffer else if (contentType.includes("application/pdf") || contentType.includes("image/") || contentType.includes("application/octet-stream")) { const buffer = await clonedResponse.arrayBuffer(); body = Buffer.from(buffer).toString("base64"); } // Otherwise attempt to serialise as text else { body = await clonedResponse.text(); } } catch (error) { console.error("Serialisation error:", error); body = null; } return { url: response.url, status: response.status, statusText: response.statusText, headers: Object.fromEntries(response.headers.entries()), redirected: response.redirected, type: response.type, body, }; } static prepareBody(body) { if (body instanceof FormData) { return { contentType: undefined, processedBody: body }; } if (body instanceof URLSearchParams) { return { contentType: "application/x-www-form-urlencoded", processedBody: body, }; } if (typeof body === "string") { return { contentType: "text/plain", processedBody: body, }; } return { contentType: "application/json", processedBody: JSON.stringify(body), }; } } exports.HTTP = HTTP;