perf: pre-compile middleware chains at build time#3104
Merged
Conversation
…ling Pre-compiles middleware arrays into single handler functions during app initialization instead of rebuilding the chain on every request. Router now stores compiled handlers (T | null) instead of arrays (T[]). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6aa0d38 to
1d04b58
Compare
When the router changed from arrays to single items, router.add() started overwriting existing routes. This broke cases where a handler and an fsRoute target the same path — the handler registered first should win. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Single middleware - Post-processing after next() (response wrapping) - Concurrent requests sharing compiled chain (verifies no state leakage) - onError callback invocation - Empty array falls through to ctx.next Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The router changed from accumulating handlers (T[]) to storing a single handler (T | null) with first-registration priority. Add tests to document this behavior: duplicate registrations for the same method + pattern preserve the first handler, while different methods on the same pattern work independently. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Tests added on main used the old array-based router API (handlers: [], router.add(..., [A])). Updated to match the new single-item API (item:, router.add(..., A)) introduced by this PR. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Ari4ka
approved these changes
Mar 30, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Pre-compiles middleware chains once at startup instead of constructing them per-request.
Changes
runMiddlewares(middlewares, ctx, onError)(per-request) withcompileMiddlewares(middlewares, onError)(once at startup) which returns a singleMiddleware<State>functionT | null) instead of arrays (T[])tail(the originalctx.next) as a parameter to each step, avoiding the infinite recursion bug where a compiled tail reads an already-overwrittenctx.next— safe under concurrent requestsBenchmarks
Synthetic benchmarks (10 middlewares,
deno bench) show the per-request savings are small in isolation — middleware execution and async/await overhead dominate. The main value is architectural: building the chain once is correct and eliminates per-request allocations, which may compound under high concurrency.Test plan
compileMiddlewaresnext()(response wrapping)onErrorcallback invocationctx.nextnext