Skip to content

Commit 722d586

Browse files
authored
feat: allow stacking error handlers (#3085)
1 parent 2ebb0e3 commit 722d586

7 files changed

Lines changed: 61 additions & 6 deletions

File tree

src/core/config/resolvers/error.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,13 @@ import { join } from "pathe";
44

55
export async function resolveErrorOptions(options: NitroOptions) {
66
if (!options.errorHandler) {
7-
options.errorHandler = join(
8-
runtimeDir,
9-
`internal/error/${options.dev ? "dev" : "prod"}`
10-
);
7+
options.errorHandler = [];
8+
} else if (!Array.isArray(options.errorHandler)) {
9+
options.errorHandler = [options.errorHandler];
1110
}
11+
12+
// Always add the default error handler as the last one
13+
options.errorHandler.push(
14+
join(runtimeDir, `internal/error/${options.dev ? "dev" : "prod"}`)
15+
);
1216
}

src/rollup/config.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import { sourcemapMininify } from "./plugins/sourcemap-min";
4141
import { storage } from "./plugins/storage";
4242
import { timing } from "./plugins/timing";
4343
import { virtual } from "./plugins/virtual";
44+
import { errorHandler } from "./plugins/error-handler";
4445
import { resolveAliases } from "./utils";
4546

4647
export const getRollupConfig = (nitro: Nitro): RollupConfig => {
@@ -344,6 +345,9 @@ export const getRollupConfig = (nitro: Nitro): RollupConfig => {
344345
rollupConfig.plugins.push(handlersMeta(nitro));
345346
}
346347

348+
// Error handler
349+
rollupConfig.plugins.push(errorHandler(nitro));
350+
347351
// Polyfill
348352
rollupConfig.plugins.push(
349353
virtual(
@@ -393,7 +397,6 @@ export const plugins = [
393397
alias({
394398
entries: resolveAliases({
395399
"#build": buildDir,
396-
"#nitro-internal-virtual/error-handler": nitro.options.errorHandler,
397400
"#internal/nitro": runtimeDir,
398401
"nitro/runtime": runtimeDir,
399402
"nitropack/runtime": runtimeDir,
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import type { Nitro } from "nitropack/types";
2+
import { virtual } from "./virtual";
3+
4+
export function errorHandler(nitro: Nitro) {
5+
return virtual(
6+
{
7+
"#nitro-internal-virtual/error-handler": () => {
8+
const errorHandlers = Array.isArray(nitro.options.errorHandler)
9+
? nitro.options.errorHandler
10+
: [nitro.options.errorHandler];
11+
12+
return /* js */ `
13+
${errorHandlers.map((h, i) => `import errorHandler$${i} from "${h}";`).join("\n")}
14+
15+
const errorHandlers = [${errorHandlers.map((_, i) => `errorHandler$${i}`).join(", ")}];
16+
17+
export default async function(error, event) {
18+
for (const handler of errorHandlers) {
19+
try {
20+
await handler(error, event);
21+
if (event.handled) {
22+
return; // Response handled
23+
}
24+
} catch(error) {
25+
// Handler itself thrown, log and continue
26+
console.error(error);
27+
}
28+
}
29+
// H3 will handle fallback
30+
}
31+
`;
32+
},
33+
},
34+
nitro.vfs
35+
);
36+
}

src/types/config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ export interface NitroOptions extends PresetOptions {
181181
handlers: NitroEventHandler[];
182182
routeRules: { [path: string]: NitroRouteRules };
183183
devHandlers: NitroDevEventHandler[];
184-
errorHandler: string;
184+
errorHandler: string | string[];
185185
devErrorHandler: NitroErrorHandler;
186186
prerender: {
187187
/**

test/fixture/error.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export default defineNitroErrorHandler((error, event) => {
2+
if (event.path.includes("?custom_error_handler")) {
3+
return send(event, "custom_error_handler");
4+
}
5+
});

test/fixture/nitro.config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ export default defineNitroConfig({
8181
"db:migrate": { description: "Migrate database" },
8282
"db:seed": { description: "Seed database" },
8383
},
84+
errorHandler: "~/error.ts",
8485
routeRules: {
8586
"/api/param/prerender4": { prerender: true },
8687
"/api/param/prerender2": { prerender: false },

test/tests.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,12 @@ export function testNitro(
394394
"x-content-type-options": "nosniff",
395395
"x-frame-options": "DENY",
396396
});
397+
398+
const { data } = await callHandler({
399+
url: "/api/error?custom_error_handler",
400+
});
401+
expect(status).toBe(503);
402+
expect(data).toBe("custom_error_handler");
397403
});
398404

399405
it.skipIf(isWindows && ctx.preset === "nitro-dev")(

0 commit comments

Comments
 (0)