Middleware
middleware.ts becomes the middleware option in src/entry-server.ts: an array of (event, next) => Response functions, outermost first.
middleware.ts becomes the middleware option in src/entry-server.ts: an array of (event, next) => Response
functions, outermost first. There is no matcher: the array runs for every request that passes the host, CSRF and
size checks, including API routes and server function calls, so test event.url.pathname yourself. A middleware
answers by returning a Response or by throwing redirect() or HttpError; it continues with next() and may change
the response it returns. Per-route middleware exists for API routes (export const middleware) and server functions
(middleware: option).
| Next.js middleware | Dusk |
|---|---|
NextResponse.redirect(new URL("/login", request.url)) |
throw redirect("/login", { status: 303 }) |
NextResponse.next({ headers }) |
Set event.responseHeaders before next(), or edit the returned response |
request.cookies.get("x") |
event.cookies.get("x") |
NextResponse.rewrite(...) |
None. Return a Response built from your own fetch call, or add a route |
export const config = { matcher } |
None, see above |
| Edge runtime | None. Node |
src/entry-server.tsts
import { redirect } from "@shadow-js/dusk";
import type { HandlerOptions, Middleware } from "@shadow-js/dusk/server";
/** Pages under /admin need the `viewer` cookie. */
const admin: Middleware = (event, next) => {
if (event.url.pathname.startsWith("/admin") && event.cookies.get("viewer") === undefined) {
throw redirect("/login", { status: 303 });
}
return next();
};
/** Public caching for the post pages. Without it every response is `private, no-store`. */
const cache: Middleware = (event, next) => {
if (event.method === "GET" && event.url.pathname.startsWith("/posts")) {
event.responseHeaders.set("cache-control", "public, max-age=60");
}
return next();
};
const options: Omit<HandlerOptions, "manifest"> = {
security: { allowedHosts: ["localhost", "blog.example.com"] },
middleware: [admin, cache],
};
export default options;
src/routes/login.tsxtsx
import type { JSX } from "@shadow-js/web";
export default function Login(): JSX.Element {
return <h1>Sign in</h1>;
}
src/routes/admin.tsxtsx
import type { JSX } from "@shadow-js/web";
export default function Admin(): JSX.Element {
return <h1>Admin</h1>;
}