Middleware and error responses
Handler-wide middleware is (event, next) => Response, listed outermost first in middleware.
Handler-wide middleware is (event, next) => Response, listed outermost first in middleware. It runs after the guards
and the CSRF check, so a request those refuse never reaches it, and before the route. await next() always resolves
with a Response, also when the rest of the request fails. A route that throws an unexpected error, an HttpError, a
redirect() or a failed validation does not reject next(): the handler turns the failure into the response the client
will get, inside the chain. Code after await next() therefore runs for a 500, a 401 or a 303 as for a 200, and headers
set there are on the error response.
What next() returns for a failure is the final answer for the client, not the error object. In production the answer to
an unexpected error is the generic 500 (the detail went to onError once, before your middleware ran), and an
HttpError 5xx shows its reason phrase. A middleware that wants another answer returns a different Response, or throws
its own error, which is mapped like any other (throw new HttpError(503, ...)). Calling next() twice is still an error
(MIDDLEWARE_NEXT_TWICE, answered 500). The same holds for the middleware export of an API route and for the
middleware option of apiHandler. Server function middleware (serverFn({ middleware })) is a different kind: it
wraps the function and sees its value or its thrown error, not a Response.
The session and rate limit middleware rely on it: the session is saved, and its cookie set, also when the route answered with an error. The next example stamps every answer with the time the route took, errors included.
import type { Middleware } from "@shadow-js/dusk/server";
/** Adds `server-timing` to every response, also to the answer for a thrown error. */
export const timing: Middleware = async (_event, next) => {
const started = performance.now();
const response = await next();
response.headers.set("server-timing", `app;dur=${Math.round(performance.now() - started)}`);
response.headers.set("x-status-seen", String(response.status));
return response;
};