httpPlugin
import { httpPlugin } from '@routecraft/routecraft'
Serves routes over HTTP. Backs the http() source; routes declare .from(http({ path, method })) and the plugin mounts them on a named server.
http is a first-class core config key, so the common path is defineConfig({ http: {...} }) rather than plugins: [httpPlugin(...)]. The factory is exported for programmatic composition.
import { defineConfig, jwt } from '@routecraft/routecraft'
export const craftConfig = defineConfig({
servers: { public: { port: 8080, host: '0.0.0.0' } },
http: {
server: 'public',
auth: jwt({ secret: process.env.JWT_SECRET!, issuer: '...', audience: '...' }),
},
})
Options
Per-route authorization uses the existing .authorize({ roles, scopes }) builder. The mount, not the route, decides authentication: routes on a walled mount require a valid credential, routes on a mount with no wall (auth: false) never see one, and such a route that declares .authorize() forces verification through the inherited validator for itself alone. See Mounts and authentication for the full matrix. Built-in endpoints /health, /ready, and /openapi.json serve from the default mount at / unless a user route claims the same path, and describe only routes on that mount's server when mounts span several listeners.
Custom mounts
Custom plugins can join the same listener during apply():
import { requireWebIngress, type CraftPlugin } from '@routecraft/routecraft'
const healthPlugin: CraftPlugin = {
name: 'health',
apply(ctx) {
const ingress = requireWebIngress(ctx, 'public')
const unmount = ingress.mountHttp({
id: 'custom-health',
claims: () => [{ kind: 'exact', path: '/internal/health', methods: ['GET'] }],
handler: () => Response.json({ ok: true }),
})
ctx.registerTeardown(unmount)
},
}
Claims are thunks evaluated once during startup validation. This lets plugins register dynamic routes during route startup while still detecting cross-mount conflicts before the listener binds.
A mount whose responses stream or stay quiet indefinitely (SSE, MCP-style channels) declares longLived: true; the ingress then exempts its requests from the listener's idle timeout while every other connection keeps the bounded default.
Lifecycle
apply(ctx)validates options, publishes the route registry, and mounts it on the selected named server. It does not bind a socket.- During context start, all mount claims on all named servers are validated before any listener binds. The server then emits
server:listening { server, port, host }. - On context stop, named servers drain active requests, close idle connections, and emit
server:closed { server }. - A bind failure (
EADDRINUSE/EADDRNOTAVAIL) surfaces asRC5019.
Events
See HTTP plugin events for the full list. The plugin also re-uses the framework's auth:success / auth:rejected events with source: "http".
Related
http()adapter -- both source and destination overloads.- Configuration -- the
serversandhttpfirst-class config keys. .authorize()-- per-route role/scope/predicate checks.