Routes & introspection
Plugin routes, path helpers, RouteValue / MetaMap, match captures.
Author guide — edit
docs/.vitepress/plugin-sdk-guides/routes.md, thenpnpm docs:generate.
Adding routes from a plugin
app.get("/_devtools/app", handler);
app.post("/_tasks/enqueue", handler);
app.mount("/api", |r| { /* nested router */ });Examples: static (mount + /*path), devtools, openapi, i18n prefix, tasks HTTP enqueue.
Guard sensitive surfaces (bearer token, cli_mode, localhost-only) — see tasks enqueue + DevTools release guard.
Path helpers
use sova_core::extend::{normalize_path, to_brace_path, join_paths};Static files and OpenAPI rely on consistent path normalization.
Route introspection (RouteValue / MetaMap)
Plugins attach typed metadata to routes for other systems:
| Consumer | Meta |
|---|---|
| openapi | operation schemas |
| vld | validation rules / coverage |
| meta | per-page OG / robots / sitemap |
use sova_core::extend::{MetaMap, RouteValue, Needs};
// attach via router builders / with_update patterns used in those cratesIf your plugin needs OpenAPI or validation awareness, follow vld/openapi for RouteTable / RouteEntry iteration rather than inventing a parallel registry.
Match captures
After the router matches:
| Type | Purpose | Used by |
|---|---|---|
MatchedRoute / MatchedRouteCapture | pattern + captures | devtools, observability |
MatchedMeta / MatchedMetaCapture | overlay page meta | meta |
use sova_core::{MatchedRouteCapture, Request};
let cap = MatchedRouteCapture::new();
req.set(cap.clone());
let res = next(req).await;
let pattern = cap.get(); // Option after matchInstall capture before next so the router can fill it.
Needs on routes
See State & dependencies — session uses Needs::<CookieLayerPresent> so session routes refuse to build without cookies MW.
