[]previous and next page

Middleware

middleware.ts becomes the middleware option in src/entry-server.ts: an array of (event, next) => Response functions, outermost first.

For agentsUpdated

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>;
}