Skip to content

perf: pre-compile middleware chains at build time#3104

Merged
bartlomieju merged 8 commits into
mainfrom
fast-middlewares
Mar 29, 2026
Merged

perf: pre-compile middleware chains at build time#3104
bartlomieju merged 8 commits into
mainfrom
fast-middlewares

Conversation

@marvinhagemeister

@marvinhagemeister marvinhagemeister commented Jul 16, 2025

Copy link
Copy Markdown
Contributor

Pre-compiles middleware chains once at startup instead of constructing them per-request.

Changes

  • Replace runMiddlewares(middlewares, ctx, onError) (per-request) with compileMiddlewares(middlewares, onError) (once at startup) which returns a single Middleware<State> function
  • Router now stores a single compiled handler per method/route (T | null) instead of arrays (T[])
  • The compiled chain passes tail (the original ctx.next) as a parameter to each step, avoiding the infinite recursion bug where a compiled tail reads an already-overwritten ctx.next — safe under concurrent requests
  • Lazy middleware resolution is preserved: a lazy middleware is resolved on first request and the resolved function replaces the lazy wrapper in the closure

Benchmarks

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

  • Existing middleware tests adapted to compileMiddlewares
  • New test: single middleware
  • New test: post-processing after next() (response wrapping)
  • New test: concurrent requests sharing the same compiled chain
  • New test: onError callback invocation
  • New test: empty middleware array falls through to ctx.next
  • New test: compiled chain calls tail next
  • New test: first-registered route wins on duplicate registration
  • Router tests adapted to single-item API

@bartlomieju bartlomieju marked this pull request as ready for review March 26, 2026 08:10
…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>
bartlomieju and others added 2 commits March 26, 2026 09:31
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>
@bartlomieju bartlomieju changed the title feat: speed up middleware matching perf: pre-compile middleware chains at build time Mar 26, 2026
bartlomieju and others added 3 commits March 26, 2026 14:01
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>
@bartlomieju bartlomieju changed the title perf: pre-compile middleware chains at build time feat: pre-compile middleware chains at build time Mar 29, 2026
@bartlomieju bartlomieju changed the title feat: pre-compile middleware chains at build time perf: pre-compile middleware chains at build time Mar 29, 2026
bartlomieju and others added 2 commits March 29, 2026 21:26
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>
@bartlomieju bartlomieju merged commit 4edfe2f into main Mar 29, 2026
7 of 9 checks passed
@bartlomieju bartlomieju deleted the fast-middlewares branch March 29, 2026 19:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants