Skip to content

Commit f1b971f

Browse files
committed
404 / error handling
# Conflicts: # packages/typescript-koa-runtime/src/server.ts
1 parent d652b28 commit f1b971f

File tree

6 files changed

+78
-26
lines changed

6 files changed

+78
-26
lines changed

e2e/src/express.entrypoint.ts

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import {ExpressRuntimeError} from "@nahkies/typescript-express-runtime/errors"
21
import {type NextFunction, type Request, type Response, Router} from "express"
32
import {bootstrap} from "./generated/server/express"
43
import {createRequestHeadersRouter} from "./routes/express/request-headers"
@@ -26,15 +25,22 @@ export async function startExpressServer() {
2625
origin: "http://example.com",
2726
},
2827
router: createRouter(),
29-
})
30-
31-
app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
32-
if (res.headersSent) {
33-
return next(err)
34-
}
35-
36-
const {status, body} = createErrorResponse(err)
37-
res.status(status).json(body)
28+
notFoundHandler: (req: Request, res: Response, next: NextFunction) => {
29+
res.status(404).json({code: 404, message: "route not found"})
30+
},
31+
errorHandler: (
32+
err: Error,
33+
req: Request,
34+
res: Response,
35+
next: NextFunction,
36+
) => {
37+
if (res.headersSent) {
38+
return next(err)
39+
}
40+
41+
const {status, body} = createErrorResponse(err)
42+
res.status(status).json(body)
43+
},
3844
})
3945

4046
return {app, server, address}

e2e/src/index.axios.spec.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,14 +66,18 @@ describe.each(startServerFunctions)(
6666
() => {
6767
throw new Error("expected request to fail")
6868
},
69-
(err: unknown) => err,
69+
(err: AxiosError) => err,
7070
)
7171

7272
expect(err).toMatchObject({
7373
message: "Request failed with status code 404",
7474
name: "AxiosError",
7575
status: 404,
7676
})
77+
expect(err.response?.data).toMatchObject({
78+
code: 404,
79+
message: "route not found",
80+
})
7781
})
7882
})
7983

e2e/src/index.fetch.spec.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,10 @@ describe.each(startServerFunctions)(
6464
})
6565

6666
expect(res.status).toBe(404)
67+
await expect(res.json()).resolves.toMatchObject({
68+
code: 404,
69+
message: "route not found",
70+
})
6771
})
6872
})
6973

e2e/src/koa.entrypoint.ts

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,6 @@ import {createErrorResponse} from "./shared"
77
function createRouter() {
88
const router = new Router()
99

10-
router.use(async (ctx, next) => {
11-
try {
12-
await next()
13-
} catch (err) {
14-
const {status, body} = createErrorResponse(err)
15-
ctx.status = status
16-
ctx.body = body
17-
}
18-
})
19-
2010
const requestHeadersRouter = createRequestHeadersRouter()
2111
const validationRouter = createValidationRouter()
2212

@@ -37,6 +27,25 @@ export async function startKoaServer() {
3727
allowMethods: ["GET", "OPTIONS"],
3828
origin: "http://example.com",
3929
},
30+
middleware: [
31+
async function errorHandler(ctx, next) {
32+
try {
33+
await next()
34+
} catch (err) {
35+
const {status, body} = createErrorResponse(err)
36+
ctx.status = status
37+
ctx.body = body
38+
}
39+
},
40+
async function notFoundHandler(ctx, next) {
41+
await next()
42+
if (ctx.body || ctx.status !== 404) {
43+
return
44+
}
45+
ctx.status = 404
46+
ctx.body = {code: 404, message: "route not found"}
47+
},
48+
],
4049
router: createRouter(),
4150
})
4251
}

packages/typescript-express-runtime/src/server.ts

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,12 @@ import type {AddressInfo} from "node:net"
44

55
import type {OptionsJson} from "body-parser"
66
import Cors, {type CorsOptions, type CorsOptionsDelegate} from "cors"
7-
import express, {type Express, type RequestHandler, type Router} from "express"
7+
import express, {
8+
type ErrorRequestHandler,
9+
type Express,
10+
type RequestHandler,
11+
type Router,
12+
} from "express"
813

914
// from https://stackoverflow.com/questions/39494689/is-it-possible-to-restrict-number-to-a-certain-range
1015
type Enumerate<
@@ -79,10 +84,20 @@ export type ServerConfig = {
7984

8085
/**
8186
* provide arbitrary express middleware to be mounted before all request handlers
82-
* useful for mounting logging, error handlers, alternative body parsers, etc
87+
* useful for mounting logging, alternative body parsers, etc
8388
*/
8489
middleware?: RequestHandler[]
8590

91+
/**
92+
* Provide a custom 404 handler
93+
*/
94+
notFoundHandler?: RequestHandler
95+
96+
/**
97+
* Provide a custom error handler
98+
*/
99+
errorHandler?: ErrorRequestHandler
100+
86101
/**
87102
* the router to use, normally obtained by calling the generated `createRouter`
88103
* function
@@ -119,6 +134,8 @@ export async function startServer({
119134
body = undefined,
120135
port = 0,
121136
router,
137+
notFoundHandler,
138+
errorHandler,
122139
}: ServerConfig): Promise<{
123140
app: Express
124141
server: Server
@@ -143,6 +160,14 @@ export async function startServer({
143160

144161
app.use(router)
145162

163+
if (notFoundHandler) {
164+
app.use(notFoundHandler)
165+
}
166+
167+
if (errorHandler) {
168+
app.use(errorHandler)
169+
}
170+
146171
return new Promise((resolve, reject) => {
147172
try {
148173
const server = app.listen(port)

packages/typescript-koa-runtime/src/server.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,11 @@ export type KoaRuntimeResponder<
6262
}
6363

6464
export type ServerConfig = {
65-
/** set to "disabled" to disable cors middleware, omit or pass undefined for defaults */
65+
/**
66+
* set to "disabled" to disable cors middleware, omit or pass undefined for defaults
67+
*
68+
* the default behavior is to allow all origins
69+
**/
6670
cors?: "disabled" | Cors.Options | undefined
6771

6872
/**
@@ -74,8 +78,8 @@ export type ServerConfig = {
7478
body?: "disabled" | KoaBodyMiddlewareOptions | undefined
7579

7680
/**
77-
*
78-
* useful for mounting logging, alternative body parsers, etc.
81+
* allows you to provide arbitrary koa middleware
82+
* useful for mounting logging, error handlers, 404 handling, alternative body parsers, etc.
7983
*/
8084
middleware?: Middleware[]
8185

0 commit comments

Comments
 (0)