01
Authorize the resource, not the UI
A role is only a policy input. The server must verify that the current principal can perform the requested action on the requested resource. Hide controls for usability, but enforce the decision in Actions and server guards.
- Model permissions as explicit verbs such as invoice:approve.
- Check tenant, ownership, and row-level scope.
- Return safe 403 responses and emit audit events for sensitive decisions.
02
Use Action authorize for mutations
The framework Action API accepts an authorize callback after validation. Resolve the principal there and perform role plus resource checks before handle executes.
- Keep authorization independent from UI state.
- Do not accept role or user ID as a trusted client decision.
- Test deny paths at least as carefully as allow paths.
03
Compose server middleware around Nexis
createMiddleware and composeMiddleware are the concise Node APIs. Application middleware can establish a correlation ID, apply rate limits, resolve a session, protect private prefixes, set headers, and then call Nexis route and Action handling.
- Set security headers before delegating.
- Use a distributed rate limiter in multi-instance hosting.
- Never enable trusted proxy behavior unless the proxy sanitizes forwarded headers.
import { createServer as createHttpServer } from 'node:http'
import { composeMiddleware, createMiddleware } from '@mohammedaydan/serve'
const handler = composeMiddleware(
requestIdMiddleware,
sessionResolutionMiddleware,
rateLimitMiddleware,
createMiddleware('./dist/client', {
actionOrigins: ['https://app.example.com'],
}),
)
createHttpServer(handler).listen(4173)SCOPE BOUNDARY
Do not confuse a pattern with a built-in.
Middleware illustrates Node composition. On Deno or Cloudflare, compose equivalent Fetch handlers; do not import Node APIs into edge code.
PRACTICE LAB
Prove the behavior.
Add table-driven tests for member, manager, and admin permissions plus an ownership mismatch. Verify a denied Action never reaches its handle.