Middleware
Drop a _middleware.ts in any app/routes/** directory and its default export — a Hono middleware — runs before every page, handler, loader, and action at or under that directory. Call next() to continue; return a Response to short-circuit. This is the declarative place for an auth guard.
// app/routes/admin/_middleware.ts → guards /admin and everything under it
import type { PageMiddleware } from "chevalier";
import { getSession } from "chevalier";
const guard: PageMiddleware = async (c, next) => {
const session = await getSession<{ userId: number }>(
c,
Deno.env.get("SESSION_SECRET")!,
);
if (!session.data.userId) return c.redirect("/login");
await next();
};
export default guard;A _middleware.ts guards its own directory index (/admin) and its subtree, but not siblings. Nest directories to layer guards: they compose outer-to-inner, so routes/_middleware.ts wraps routes/admin/_middleware.ts. One guard per directory — compose multiple concerns inside the handler.
Observability
Logging and metrics are bring-your-own — Chevalier ships no logger. To log every request, put a _middleware.ts at the root of app/routes. Drop in Hono's logger for a quick start, or write your own for structured lines:
// app/routes/_middleware.ts → logs every request
import type { PageMiddleware } from "chevalier";
const log: PageMiddleware = async (c, next) => {
const start = performance.now();
await next();
const ms = (performance.now() - start).toFixed(1);
console.log(`${c.req.method} ${c.req.path} ${c.res.status} ${ms}ms`);
};
export default log;