Middleware
Shape
Middleware receives (ctx, next) and can:
- return a
Responseimmediately - call
next(ctx)and return the downstream response - replace the request before passing it on
import type { ServerMiddlewareFunction } from "sevok";
const poweredBy: ServerMiddlewareFunction = async (ctx, next) => {
const response = await next(ctx);
response.headers.set("x-powered-by", "sevok");
return response;
};
Registering Middleware
import { serve } from "sevok";
import { NodeRuntimeAdapter } from "sevok/node";
serve({
adapter: new NodeRuntimeAdapter(),
middleware: [poweredBy],
routes: {
"/": () => new Response("ok"),
},
fetch: () => new Response("Not Found", { status: 404 }),
});
Named Middleware
ServerMiddlewareName can be extended with module augmentation when you want
typed names for a middleware registry.
Use middlewareResolver when your middleware array may contain names such as
"auth" instead of executable middleware functions. The resolver receives the
name and must return the middleware function that should run for that entry.
If a named middleware entry cannot be resolved, sevok skips that entry and continues the chain. Throw from your resolver if unresolved names should fail the request instead.
import { serve } from "sevok";
import type { ServerMiddlewareResolver } from "sevok";
declare module "sevok" {
interface ServerMiddlewareNameMap {
auth: true;
cache: true;
}
}
const middlewareResolver: ServerMiddlewareResolver = (name) => {
if (name === "auth") {
return async (ctx, next) => next(ctx);
}
if (name === "cache") {
return async (ctx, next) => next(ctx);
}
};
serve({
middleware: ["auth"],
middlewareResolver,
fetch: () => new Response("ok"),
});
Short-Circuiting
Middleware can stop the chain by returning a response directly.
const auth: ServerMiddlewareFunction = async (ctx, next) => {
if (!ctx.request.headers.get("authorization")) {
return new Response("Unauthorized", { status: 401 });
}
return next(ctx);
};
Static Files
serveStatic() is implemented as middleware.
import { serve } from "sevok";
import { serveStatic } from "sevok/static";
import { NodeRuntimeAdapter } from "sevok/node";
serve({
adapter: new NodeRuntimeAdapter(),
middleware: [serveStatic({ dir: "./public" })],
fetch: () => new Response("Not Found", { status: 404 }),
});
Request Logging
The optional logger lives in a separate subpath export.
import { serve } from "sevok";
import { log } from "sevok/log";
import { NodeRuntimeAdapter } from "sevok/node";
serve({
adapter: new NodeRuntimeAdapter(),
middleware: [log()],
routes: {
"/": () => new Response("ok"),
},
fetch: () => new Response("Not Found", { status: 404 }),
});
Plugins
ServerPlugin runs during Server construction and can modify server.options
or register process-level behavior.
sevok itself uses plugins internally for:
- request error normalization
- graceful shutdown handling in supported runtimes