Skip to content

Commit 8a1f565

Browse files
committed
simplify error bodies
1 parent c3da44b commit 8a1f565

File tree

2 files changed

+118
-24
lines changed

2 files changed

+118
-24
lines changed

src/app.ts

+5-19
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { logger } from "./lib/logger.js"
1313
import { v1Routes } from "./routes/v1/ids/handler.js"
1414
import { v2Routes } from "./routes/v2/ids/handler.js"
1515
import { thetvdbRoutes } from "./routes/v2/thetvdb/handler.js"
16-
import { CacheTimes, cacheReply } from "./utils.js"
16+
import { CacheTimes, cacheReply, createErrorJson } from "./utils.js"
1717

1818
export const createApp = () => {
1919
const app = new Hono()
@@ -35,13 +35,7 @@ export const createApp = () => {
3535
.use("*", sentry({ dsn: process.env.SENTRY_DSN! }))
3636
.use("*", cors({ origin: (origin) => origin }))
3737
.use("*", secureHeaders())
38-
.notFound((c) => {
39-
c.status(404)
40-
return c.json({
41-
error: "Not Found",
42-
statusCode: 404,
43-
})
44-
})
38+
.notFound((c) => createErrorJson(c, new HTTPException(404)))
4539
.onError((error, c) => {
4640
/* c8 ignore next 4 */
4741
if (error instanceof HTTPException) {
@@ -51,19 +45,11 @@ export const createApp = () => {
5145
cacheReply(res, CacheTimes.WEEK)
5246
}
5347

54-
c.status(error.status)
55-
return c.json({
56-
statusCode: error.status,
57-
error: error.message,
58-
})
48+
return createErrorJson(c, error)
5949
}
6050

61-
const badImpl = new HTTPException(500, { message: "Internal Server Error", cause: error })
62-
c.status(badImpl.status)
63-
return c.json({
64-
statusCode: badImpl.status,
65-
error: badImpl.message,
66-
})
51+
const badImpl = new HTTPException(500, { cause: error })
52+
return createErrorJson(c, badImpl)
6753
})
6854
.route("/api", v1Routes)
6955
.route("/api/v2", v2Routes)

src/utils.ts

+113-5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import type { Context } from "hono"
2+
import type { HTTPException } from "hono/http-exception"
3+
import type { StatusCode } from "hono/utils/http-status"
24
import type { JSONSchema7 } from "json-schema"
35
import type { TypeOf, ZodError, z } from "zod"
46

@@ -15,6 +17,114 @@ export const mergeSchemas = <One extends JSONSchema7, Two extends JSONSchema7>(
1517
},
1618
})
1719

20+
type ErrorJson = {
21+
code?: string
22+
statusCode: number
23+
error: string
24+
message?: string
25+
}
26+
27+
const getErrorText = (code: StatusCode) => {
28+
switch (code) {
29+
case 400:
30+
return "Bad Request"
31+
case 401:
32+
return "Unauthorized"
33+
case 403:
34+
return "Forbidden"
35+
case 404:
36+
return "Not Found"
37+
case 405:
38+
return "Method Not Allowed"
39+
case 406:
40+
return "Not Acceptable"
41+
case 408:
42+
return "Request Timeout"
43+
case 409:
44+
return "Conflict"
45+
case 410:
46+
return "Gone"
47+
case 411:
48+
return "Length Required"
49+
case 412:
50+
return "Precondition Failed"
51+
case 413:
52+
return "Payload Too Large"
53+
case 414:
54+
return "URI Too Long"
55+
case 415:
56+
return "Unsupported Media Type"
57+
case 416:
58+
return "Range Not Satisfiable"
59+
case 417:
60+
return "Expectation Failed"
61+
case 418:
62+
return "I'm a teapot"
63+
case 421:
64+
return "Misdirected Request"
65+
case 422:
66+
return "Unprocessable Entity"
67+
case 423:
68+
return "Locked"
69+
case 424:
70+
return "Failed Dependency"
71+
case 425:
72+
return "Too Early"
73+
case 426:
74+
return "Upgrade Required"
75+
case 428:
76+
return "Precondition Required"
77+
case 429:
78+
return "Too Many Requests"
79+
case 431:
80+
return "Request Header Fields Too Large"
81+
case 451:
82+
return "Unavailable For Legal Reasons"
83+
case 500:
84+
return "Internal Server Error"
85+
case 501:
86+
return "Not Implemented"
87+
case 502:
88+
return "Bad Gateway"
89+
case 503:
90+
return "Service Unavailable"
91+
case 504:
92+
return "Gateway Timeout"
93+
case 505:
94+
return "HTTP Version Not Supported"
95+
case 506:
96+
return "Variant Also Negotiates"
97+
case 507:
98+
return "Insufficient Storage"
99+
case 508:
100+
return "Loop Detected"
101+
case 510:
102+
return "Not Extended"
103+
case 511:
104+
return "Network Authentication Required"
105+
default:
106+
return "Error"
107+
}
108+
}
109+
110+
export const createErrorJson = (c: Context, input: Pick<HTTPException, "status" | "message"> & { code?: string }) => {
111+
const status: StatusCode = input.status
112+
const body: Omit<ErrorJson, "error" | "statusCode"> = {
113+
message: input.message ?? "An error occurred.",
114+
}
115+
116+
if (input.code != null) {
117+
body.code = input.code
118+
}
119+
120+
c.status(status)
121+
return c.json({
122+
...body,
123+
statusCode: status,
124+
error: getErrorText(status),
125+
})
126+
}
127+
18128
export const zHook = <T extends z.ZodType<any, z.ZodTypeDef, any>>(result: TypeOf<T>, c: Context) => {
19129
if (result.success === true) return
20130

@@ -24,12 +134,10 @@ export const zHook = <T extends z.ZodType<any, z.ZodTypeDef, any>>(result: TypeO
24134
...Object.entries(flat.fieldErrors).map(([key, value]) => `${key}: ${value?.join(", ") ?? "error"}`),
25135
]
26136

27-
c.status(400)
28-
return c.json({
29-
code: "FST_ERR_VALIDATION",
30-
error: "Bad Request",
31-
statusCode: 400,
137+
return createErrorJson(c, {
138+
status: 400,
32139
message: messages.join(", "),
140+
code: "FST_ERR_VALIDATION",
33141
})
34142
}
35143

0 commit comments

Comments
 (0)