CORE CONCEPTS

Middleware

Rasengan Server uses Futon's Middleware type unchanged ((ctx, next) => Promise<Response>) but layers it at four levels, always applied in the same order.

The Four Layers

Global → Module → Controller → Route → Validation → Handler
Global — every request
bootstrap((app) => { app.use(requestId()); app.enableCors({ origin: 'https://myapp.com' }); });

Two middleware are pre-registered on every ServerApp before your own app.use() calls: a request logger and a body parser (storing the parsed body on ctx.state.body).

Module — scoped to that module's routes
export default defineModule({ prefix: '/admin', middlewares: [requireAdmin], controllers: [AdminController], });
Controller — scoped to that controller's routes
export class AdminController extends Controller { middlewares = [auditLog]; // ... }
Route — scoped to a single route
routes(router: Router) { router.get("/danger-zone", rateLimited, this.dangerZone); }

Why Order Matters

Each layer wraps the next, onion-style — a global middleware's next() call eventually reaches the module middleware, then the controller middleware, then the route middleware, then (if the route has a schema) validation, and finally the handler. A global middleware can inspect or short-circuit everything downstream; a route-level middleware only wraps that one handler.

A global middleware sees every response, even from routes it doesn't know about
app.use(async (ctx, next) => { const start = Date.now(); const response = await next(); // runs module → controller → route → validation → handler console.log( `${ctx.request.method} ${ctx.request.url}${Date.now() - start}ms` ); return response; });

CORS

app.enableCors(options?) is a thin wrapper over Futon's cors() — calling it with no arguments enables CORS with Futon's defaults:

Enabling CORS
app.enableCors(); // permissive defaults app.enableCors({ origin: 'https://myapp.com', credentials: true });

Reusing Futon's Built-ins

Anything exported from @rasenganjs/futonlogger(), bearerToken(), basicAuth(), compress(), requestId(), bodyLimit() — works unchanged with app.use() or as module/controller/route-level middleware, since Rasengan Server's Middleware type is Futon's. See Built-in Middleware for the full list.

Lifecycle Hooks
Validation