Skip to content

Commit bba3bff

Browse files
committed
Add better status messages
1 parent e326725 commit bba3bff

6 files changed

Lines changed: 61 additions & 6 deletions

File tree

mod.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ export { FetchClient } from "./src/FetchClient.ts";
22
export type { FetchClientOptions } from "./src/FetchClientOptions.ts";
33
export type { FetchClientResponse } from "./src/FetchClientResponse.ts";
44
export { FetchClientError } from "./src/FetchClientError.ts";
5+
export { getStatusText } from "./src/HttpStatusText.ts";
56
export { ResponsePromise } from "./src/ResponsePromise.ts";
67
export { ProblemDetails } from "./src/ProblemDetails.ts";
78
export {

src/FetchClient.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import type { FetchClientOptions } from "./FetchClientOptions.ts";
1212
import { type IObjectEvent, ObjectEvent } from "./ObjectEvent.ts";
1313
import { ResponsePromise } from "./ResponsePromise.ts";
1414
import { FetchClientError } from "./FetchClientError.ts";
15+
import { getStatusText } from "./HttpStatusText.ts";
1516

1617
type Fetch = typeof globalThis.fetch;
1718
type RequestInitWithObjectBody = Omit<RequestInit, "body"> & {
@@ -729,7 +730,7 @@ export class FetchClient {
729730
return {
730731
url,
731732
status: problem.status ?? 422,
732-
statusText: problem.title ?? "Unprocessable Entity",
733+
statusText: problem.title ?? getStatusText(problem.status ?? 422),
733734
body: null,
734735
bodyUsed: true,
735736
ok: false,
@@ -835,7 +836,9 @@ export class FetchClient {
835836

836837
response.problem ??= new ProblemDetails();
837838
response.problem.status = response.status;
838-
response.problem.title = `Unexpected status code: ${response.status}`;
839+
if (!response.problem.title) {
840+
response.problem.title = getStatusText(response.status);
841+
}
839842
response.problem.setErrorMessage(response.problem.title);
840843

841844
throw new FetchClientError(response);

src/FetchClientError.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { FetchClientResponse } from "./FetchClientResponse.ts";
2+
import { getStatusText } from "./HttpStatusText.ts";
23

34
/**
45
* Error wrapper for non-2xx responses.
@@ -14,7 +15,7 @@ export class FetchClientError extends Error {
1415
super(
1516
message ??
1617
response.problem?.title ??
17-
`Unexpected status code: ${response.status}`,
18+
getStatusText(response.status),
1819
);
1920
this.name = "FetchClientError";
2021
this.response = response;

src/HttpStatusText.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
const STATUS_TEXT: Record<number, string> = {
2+
400: "Bad Request",
3+
401: "Unauthorized",
4+
402: "Payment Required",
5+
403: "Forbidden",
6+
404: "Not Found",
7+
405: "Method Not Allowed",
8+
406: "Not Acceptable",
9+
407: "Proxy Authentication Required",
10+
408: "Request Timeout",
11+
409: "Conflict",
12+
410: "Gone",
13+
411: "Length Required",
14+
412: "Precondition Failed",
15+
413: "Content Too Large",
16+
414: "URI Too Long",
17+
415: "Unsupported Media Type",
18+
416: "Range Not Satisfiable",
19+
417: "Expectation Failed",
20+
418: "I'm a Teapot",
21+
421: "Misdirected Request",
22+
422: "Unprocessable Entity",
23+
423: "Locked",
24+
424: "Failed Dependency",
25+
425: "Too Early",
26+
426: "Upgrade Required",
27+
428: "Precondition Required",
28+
429: "Too Many Requests",
29+
431: "Request Header Fields Too Large",
30+
451: "Unavailable For Legal Reasons",
31+
500: "Internal Server Error",
32+
501: "Not Implemented",
33+
502: "Bad Gateway",
34+
503: "Service Unavailable",
35+
504: "Gateway Timeout",
36+
505: "HTTP Version Not Supported",
37+
506: "Variant Also Negotiates",
38+
507: "Insufficient Storage",
39+
508: "Loop Detected",
40+
510: "Not Extended",
41+
511: "Network Authentication Required",
42+
};
43+
44+
/**
45+
* Returns the standard HTTP reason phrase for a status code,
46+
* or `"Unknown Status Code"` if the code is not recognized.
47+
*/
48+
export function getStatusText(status: number): string {
49+
return STATUS_TEXT[status] ?? "Unknown Status Code";
50+
}

src/tests/ErrorHandling.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,12 +148,12 @@ Deno.test("handles 400 response with non-JSON text", async () => {
148148
assert(response.problem);
149149
assert(response.problem.errors);
150150
assert(response.problem.title);
151-
assertStringIncludes(response.problem.title, "Unexpected status");
151+
assertStringIncludes(response.problem.title, "Bad Request");
152152
assert(response.problem.errors.general);
153153
assertEquals(response.problem.errors.general.length, 1);
154154
assertStringIncludes(
155155
response.problem.errors.general[0],
156-
"Unexpected status",
156+
"Bad Request",
157157
);
158158
}
159159

src/tests/RateLimit.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ Deno.test("RateLimitMiddleware - returns 429 response when configured", async ()
198198
const response = (error as { response: FetchClientResponse<unknown> })
199199
.response;
200200
assertEquals(response.status, 429);
201-
assertEquals(response.problem?.title, "Unexpected status code: 429");
201+
assertEquals(response.problem?.title, "Too Many Requests");
202202
if (response.problem?.detail) {
203203
assertEquals(
204204
response.problem.detail.includes("Custom rate limit message"),

0 commit comments

Comments
 (0)