Skip to content

Middleware

Middleware runs before route dispatch. Each slot can halt the chain and return a response immediately (e.g. 401 unauthorized).

Function Scope
use_middleware(app, mw) All paths on server or router
use_middleware(app, prefix, router) Mount sub-router (with its middleware) under prefix

Middleware factories return a Middleware value:

Factory Express equivalent
logger() access logger / morgan
json() express.json()
urlencoded() express.urlencoded()
cors(origin) cors({ origin })
middleware(fn) custom (req, res, next)

Express

const app = express();
app.use(express.json());
app.use('/api', requireAuth);
app.get('/api/secret', (req, res) => res.json({ secret: true }));

vow-web-server

import vws from vow_web_server
import vow_web_server.request
import vow_web_server.response
import vow_web_server.middleware
fn require_auth(req: request.Request) -> middleware.MwResult {
if len(vws.header(req, "Authorization")) == 0 {
return vws.halt(vws.json(401, "{\"error\":\"unauthorized\"}"));
}
return vws.next(req);
}
fn secret(_req: request.Request) -> response.Response {
return vws.json("{\"secret\":true}");
}
fn main(caps: Caps) -> int {
let rt = vws.runtime(caps);
var app = vws.server();
app = vws.use_middleware(app, vws.cors("*"));
var api = vws.router();
api = vws.use_middleware(api, vws.middleware(require_auth));
api = vws.get(api, "/secret", secret);
app = vws.use_middleware(app, "/api", api);
return vws.serve(app, rt, 8787);
}

See vow-examples/vow-web-server/middleware_chain.vow and the HTTP middleware tutorial.

Separate from auth/json middleware — declares capability requirements for path prefixes. See Net grants.

Function Scope
use_grant(app, kind) All paths (v0.3 handle API)
use_grant_at(a, prefix, kind) Paths under prefix (legacy app())
use_grant_at_scope(a, prefix, kind, scope) Path prefix + attenuation hint (legacy)
var app = vws.server();
app = vws.use_grant(app, vws.kind_fs_read());
Factory Behavior
logger() Prints METHOD path access log
json() Parses JSON body into context_value (max 64 KiB)
urlencoded() Reads application/x-www-form-urlencoded into context
cors(origin) CORS headers + OPTIONS preflight

Legacy enum kinds (kind_logger(), use_log(a), etc.) remain on the v0.2 app() builder.

Built-in auth header checking is available via legacy use_auth(a) or a custom function:

Express

function requireAuth(req, res, next) {
if (!req.headers.authorization) return res.status(401).json({ error: 'unauthorized' });
next();
}

vow-web-server

fn require_auth(req: request.Request) -> middleware.MwResult {
if len(vws.header(req, "Authorization")) == 0 {
return vws.halt(vws.json(401, "{\"error\":\"unauthorized\"}"));
}
return vws.next(req);
}
var api = vws.router();
api = vws.use_middleware(api, vws.middleware(require_auth));
api = vws.get(api, "/x", health);
app = vws.use_middleware(app, "/api", api);

Unit tests:

assert vws.try_handle(app, "GET /api/x") == 401;
assert vws.try_handle(app, "GET /other") == 404; // no auth on /other

When Content-Type contains application/json, json() reads and validates JSON, then stores the raw JSON string on the request via with_context. Handlers read it with context_value(req).

Invalid JSON → 400. Body too large → 413.

Express

app.use(express.json());
app.post('/items', (req, res) => { /* req.body */ });

vow-web-server

var api = vws.router();
api = vws.use_middleware(api, vws.json());
api = vws.post(api, "/items", create_item);
app = vws.use_middleware(app, "/api", api);
// handler: let body = vws.context_value(req);

Slots run in registration order on the matched path prefix. Halting stops the chain:

var app = vws.server();
app = vws.use_middleware(app, vws.logger());
app = vws.use_middleware(app, vws.middleware(require_auth));
app = vws.get(app, "/health", health);
// Logger runs, then auth halts with 401 before the route.

Immutable app() builder with enum kinds:

v0.3 v0.2 legacy
use_middleware(a, logger()) use_log(a)
use_middleware(a, json()) use_json(a)
use_middleware(a, urlencoded()) use_urlencoded(a)
use_middleware(a, cors("*")) use_cors(a, "*")
use_middleware(a, prefix, router) mount(a, prefix, sub) + use_at

try_handle(app, raw) runs the full middleware + router pipeline without delivering HTTP I/O. Returns the HTTP status code:

assert vws.try_handle(app, "GET /health") == 200;
assert vws.try_handle(app, "GET /api/x") == 401;

Raw format: "METHOD /path" optionally with query (GET /items?q=1). See Testing for testutil helpers.