Middleware & Request Lifecycle
Relevant source files
The following files were used as context for generating this wiki page:
The Farm.js request lifecycle manages the progression of a web request from its arrival at the server to the final response delivery. It integrates a cascading middleware system that allows developers to intercept, modify, or short-circuit requests based on file-based routing or global configuration. This system operates across both development (Vite) and production (Nitro) environments.
Sources: ARCHITECTURE.md, packages/farm/src/middleware/manager.ts:18-35
Middleware Architecture
Farm.js employs a MiddlewareManager to discover and execute middleware logic. Middleware is categorized into two types: configuration-based middleware defined in farm.config.ts and file-based middleware discovered within the src/app directory.
Discovery and Ordering
The MiddlewareManager recursively scans the application directories for middleware.ts (or .js, .tsx, .jsx) files. It sorts discovered middleware by path depth to ensure a root-to-leaf execution order. Global configuration middleware executes before file-based middleware.
Middleware Context
Each middleware handler receives a MiddlewareContext providing access to the request, response, URL parameters, and specialized helpers for cookies and headers. It also includes a data Map for sharing information between middleware and subsequent page components.
| Property | Type | Description |
|---|---|---|
request | IncomingMessage | The raw Node.js request object. |
response | ServerResponse | The raw Node.js response object. |
data | Map<string, any> | Storage for data shared with pages. |
locals | Map<string, any> | Storage for server-only context. |
params | Record<string, string> | Parsed dynamic route parameters. |
Sources: packages/farmjs-plugin/src/middleware/index.ts:74-110, packages/farm/src/middleware/manager.ts:251-260
Request Lifecycle Flow
The lifecycle varies slightly between development and production but maintains a consistent logical progression.
1. Initialization and Routing
The request is first intercepted by the server (Vite in dev, Nitro in prod). The RouteManager matches the URL to a specific file-based route, parsing dynamic segments into parameters.
2. Middleware Execution
The MiddlewareManager.execute method initiates the chain.
- Short-circuiting: If a middleware returns a
Responseor callsctx.redirect(), the cycle terminates, and the response is sent immediately. - Rewriting: Middleware can call
ctx.rewrite(), which updates the request URL and restarts the routing process internally. - Data Injection: Information added to
ctx.datais eventually exposed to React components via themiddlewareprop.
3. Rendering and Response
After middleware completion, the request proceeds to the matched route handler (API or Page).
- API Routes: Handled by
APIRouteManager, executing the corresponding method (GET, POST, etc.). - Pages: Rendered via the
ServerRenderer, which handles React Server Components (RSC) and HTML streaming. - Post-Processing: Final headers are applied, and if compression is enabled, the response is compressed before delivery.
Sources: packages/farm/src/vite.ts:635-700, packages/farm/src/nitro/universal-build.ts:1820-1850
Execution Environment Differences
| Feature | Development (Vite) | Production (Nitro) |
|---|---|---|
| Discovery | Dynamic watching and HMR reloads. | Pre-bundled and sorted during build. |
| Loading | server.ssrLoadModule for latest code. | Statically imported in the SSR entry. |
| Error Handling | Detailed logs and HMR error recovery. | Production lifecycle event emission. |
Sources: packages/farm/src/vite.ts:1210-1240, packages/farm/src/nitro/universal-build.ts:1530-1560
Short-Circuiting and Redirects
Middleware can prevent a request from reaching the route handler by returning a Response object or using context helpers.
// Example short-circuit in middleware
export async function middleware(request: Request, context: RequestMiddlewareContext) {
const session = context.get("session");
if (!session) {
// Short-circuit the request and redirect
return Response.redirect(new URL("/login", request.url));
}
}
When a short-circuit occurs, the MiddlewareManager emits a middleware.shortCircuit event for observability and sends the response via sendWebResponse.
Sources: packages/farm/src/middleware/manager.ts:188-205, packages/farmjs-plugin/src/rsc/entries/rsc.ts:10-25
The request lifecycle ensures that every request is validated and enriched by the middleware layer before any rendering occurs, maintaining a predictable and secure environment for application logic.