From a5f1c39b86809afb9e0eb8dc4a8658e87ba98ab0 Mon Sep 17 00:00:00 2001 From: Jannik Zschiesche Date: Tue, 21 Oct 2025 14:42:32 +0200 Subject: [PATCH 1/4] Improve error context --- src/api.ts | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/src/api.ts b/src/api.ts index 532d85e..ebe0ec9 100644 --- a/src/api.ts +++ b/src/api.ts @@ -135,7 +135,7 @@ export async function fetchApi < catch (error) { logger?.error( - "API request failed due to error", + "API request failed due to an unknown error", { err: error, url: url.toString(), @@ -146,12 +146,14 @@ export async function fetchApi < } // endregion + const contentType = response.headers.get("content-type") ?? "application/octet-stream"; + const responseContentAsString = await response.text(); let responseData: unknown; // region parse JSON try { - if (!response.headers.get("content-type")?.includes("application/json")) + if (!contentType.includes("application/json")) { throw new Error("API response is no json"); } @@ -163,9 +165,11 @@ export async function fetchApi < logger?.error( "API response is no JSON", { - contentType: response.headers.get("content-type"), + contentType: contentType, url: url.toString(), error: error, + content: responseContentAsString, + statusCode: response.status, }, ); @@ -196,7 +200,11 @@ export async function fetchApi < { if (response.ok) { - logger?.error("Got error response, but API response is success"); + logger?.error("Got error response, but API response is success", { + statusCode: response.status, + content: responseContentAsString, + contentType: contentType, + }); } throw new ApiError( @@ -209,6 +217,9 @@ export async function fetchApi < { logger?.debug("No failure cause", { error: failureResponse.error, + statusCode: response.status, + content: responseContentAsString, + contentType: contentType, }); } @@ -225,6 +236,9 @@ export async function fetchApi < error: "unparseable response", successIssues: successResponse.error, failureIssues: failureResponse.error, + statusCode: response.status, + content: responseContentAsString, + contentType: contentType, }, ); From 0c10d3ad2c88377738afc6ebc17d54753cd6fef3 Mon Sep 17 00:00:00 2001 From: Jannik Zschiesche Date: Tue, 21 Oct 2025 14:48:00 +0200 Subject: [PATCH 2/4] Pass more data in RequestError() --- src/api.ts | 73 ++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 65 insertions(+), 8 deletions(-) diff --git a/src/api.ts b/src/api.ts index ebe0ec9..79bfd2c 100644 --- a/src/api.ts +++ b/src/api.ts @@ -62,15 +62,25 @@ class ApiError extends Error class RequestError extends Error { - private response: Response; + readonly #response: Response; + readonly #content: string; + readonly #contentType: string; + readonly #error: string; /** * */ - constructor (response: Response) + constructor ( + response: Response, + content: string, + contentType: string, + error: string, + ) { super(); - this.response = response; + this.#response = response; + this.#content = content; + this.#contentType = contentType; } /** @@ -78,7 +88,39 @@ class RequestError extends Error */ get is404 () { - return 404 === this.response.status; + return 404 === this.statusCode; + } + + /** + * + */ + get statusCode () : number + { + return this.#response.status; + } + + /** + * + */ + get content () : string + { + return this.#content; + } + + /** + * + */ + get contentType () : string + { + return this.#contentType; + } + + /** + * + */ + get error () : string + { + return this.#error; } } @@ -155,7 +197,12 @@ export async function fetchApi < { if (!contentType.includes("application/json")) { - throw new Error("API response is no json"); + throw new RequestError( + response, + responseContentAsString, + contentType, + "API response is no json", + ); } responseData = await response.json(); @@ -173,7 +220,7 @@ export async function fetchApi < }, ); - throw new Error("API response is no json"); + throw error; } // endregion @@ -225,7 +272,12 @@ export async function fetchApi < if (404 === response.status) { - throw new RequestError(response); + throw new RequestError( + response, + responseContentAsString, + contentType, + "Request is 404", + ); } logger?.error( @@ -242,5 +294,10 @@ export async function fetchApi < }, ); - throw new Error(`Invalid API response`); + throw new RequestError( + response, + responseContentAsString, + contentType, + "Invalid API response", + ); } From 0825e03764ae4e33112557f9cb68cf2a0dd7a580 Mon Sep 17 00:00:00 2001 From: Jannik Zschiesche Date: Tue, 21 Oct 2025 14:49:33 +0200 Subject: [PATCH 3/4] Add changelog --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0803dbe..b7188af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +2.7.1 +===== + +* (improvement) Pass more info in `RequestError`. +* (improvement) Use `RequestError` for every request if possible. + + 2.7.0 ===== From dd29fe6b0dcadeba00c1c700cbead8823c868d67 Mon Sep 17 00:00:00 2001 From: Jannik Zschiesche Date: Tue, 21 Oct 2025 15:54:24 +0200 Subject: [PATCH 4/4] Add debug and fix invalid response content read --- src/api.ts | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/src/api.ts b/src/api.ts index 79bfd2c..f299fd9 100644 --- a/src/api.ts +++ b/src/api.ts @@ -65,7 +65,7 @@ class RequestError extends Error readonly #response: Response; readonly #content: string; readonly #contentType: string; - readonly #error: string; + readonly #errorMessage: string; /** * @@ -74,13 +74,14 @@ class RequestError extends Error response: Response, content: string, contentType: string, - error: string, + errorMessage: string, ) { super(); this.#response = response; this.#content = content; this.#contentType = contentType; + this.#errorMessage = errorMessage; } /** @@ -118,9 +119,22 @@ class RequestError extends Error /** * */ - get error () : string + get errorMessage () : string { - return this.#error; + return this.#errorMessage; + } + + /** + * + */ + get debug () : Record + { + return { + errorMessage: this.errorMessage, + contentType: this.contentType, + content: this.content, + statusCode: this.statusCode, + }; } } @@ -189,7 +203,7 @@ export async function fetchApi < // endregion const contentType = response.headers.get("content-type") ?? "application/octet-stream"; - const responseContentAsString = await response.text(); + const responseContentAsString = await response.clone().text(); let responseData: unknown; // region parse JSON