From c9cad2e1184dfe5e82270595b145cef46e35d453 Mon Sep 17 00:00:00 2001 From: Manuel Schiller <6340397+schiller-manuel@users.noreply.github.com> Date: Wed, 16 Sep 2026 12:53:12 +0200 Subject: [PATCH 1/2] perf: skip unused Start request setup --- .changeset/honest-walls-attack.md | 8 + .../start-client-core/src/createServerFn.ts | 2 +- .../src/createStartHandler.ts | 44 +++-- .../start-server-core/src/finalManifest.ts | 19 +- .../tests/createStartHandler.test.ts | 174 +++++++++++++++++- .../tests/finalManifest.test.ts | 93 ++++++++++ .../src/async-local-storage.ts | 2 +- 7 files changed, 309 insertions(+), 33 deletions(-) create mode 100644 .changeset/honest-walls-attack.md diff --git a/.changeset/honest-walls-attack.md b/.changeset/honest-walls-attack.md new file mode 100644 index 00000000000..f5344007ff0 --- /dev/null +++ b/.changeset/honest-walls-attack.md @@ -0,0 +1,8 @@ +--- +'@tanstack/start-server-core': patch +'@tanstack/start-storage-context': patch +--- + +Skip empty route middleware chains and unused middleware bookkeeping. Avoid awaiting absent Start configuration, reuse the parsed request origin and serialization adapters, and return cached manifests directly when asset options are static. + +Use the router's decoded pathname for server route handlers and route middleware. diff --git a/packages/start-client-core/src/createServerFn.ts b/packages/start-client-core/src/createServerFn.ts index bfa48039cf2..b1ddaaad448 100644 --- a/packages/start-client-core/src/createServerFn.ts +++ b/packages/start-client-core/src/createServerFn.ts @@ -242,7 +242,7 @@ export async function executeMiddleware( const startContext = getStartContextServerOnly({ throwIfNotFound: false }) if (startContext?.executedRequestMiddlewares) { flattenedMiddlewares = flattenedMiddlewares.filter( - (m) => !startContext.executedRequestMiddlewares.has(m), + (m) => !startContext.executedRequestMiddlewares!.has(m), ) } } diff --git a/packages/start-server-core/src/createStartHandler.ts b/packages/start-server-core/src/createStartHandler.ts index 1ba72ecad3a..3cf459c66f1 100644 --- a/packages/start-server-core/src/createStartHandler.ts +++ b/packages/start-server-core/src/createStartHandler.ts @@ -538,7 +538,6 @@ export function createStartHandler( // in these cases we would prefer to redirect to the new path const { url, handledProtocolRelativeURL } = getNormalizedURL(request.url) const href = url.pathname + url.search + url.hash - const origin = url.origin if (handledProtocolRelativeURL) { return Response.redirect(url, 308) @@ -579,14 +578,15 @@ export function createStartHandler( } // Flatten request middlewares once - const flattenedRequestMiddlewares = requestStartOptions.requestMiddleware - ? flattenMiddlewares(requestStartOptions.requestMiddleware) + const requestMiddlewares = requestStartOptions.requestMiddleware + const flattenedRequestMiddlewares = requestMiddlewares?.length + ? flattenMiddlewares(requestMiddlewares) : [] // Create set for deduplication - const executedRequestMiddlewares = new Set( - flattenedRequestMiddlewares, - ) + const executedRequestMiddlewares = flattenedRequestMiddlewares.length + ? new Set(flattenedRequestMiddlewares) + : undefined // Memoized router getter const getRouter = (): Promise => { @@ -608,14 +608,17 @@ export function createStartHandler( history, isShell, isPrerendering: IS_PRERENDERING, - origin: requestRouter.options.origin ?? origin, + origin: requestRouter.options.origin ?? url.origin, // Start-owned options that RouterConstructorOptions omits. ...{ defaultSsr: requestStartOptions.defaultSsr, - serializationAdapters: [ - ...requestStartOptions.serializationAdapters, - ...(requestRouter.options.serializationAdapters || []), - ], + serializationAdapters: requestRouter.options.serializationAdapters + ?.length + ? [ + ...requestStartOptions.serializationAdapters, + ...requestRouter.options.serializationAdapters, + ] + : requestStartOptions.serializationAdapters, }, basepath: ROUTER_BASEPATH, }) @@ -938,18 +941,15 @@ async function handleServerRoutes({ matchedRoutes?: ReadonlyArray, ) => Promise context: any - executedRequestMiddlewares: Set + executedRequestMiddlewares: Set | undefined }): Promise { const router = await getRouter() - const location = router.latestLocation - // Preserve the encoded pathname exposed to server handlers and middleware. - const pathname = location.href.split(/[?#]/, 1)[0]! + const { pathname } = router.latestLocation // this will perform a fuzzy match, however for server routes we need an exact match // if the route is not an exact match, executeRouter will handle rendering the app router // The cached match avoids another route-tree traversal during the app router render. - const [matchedRoutes, rawParams, foundRoute] = router.getMatchedRoutes( - location.pathname, - ) + const [matchedRoutes, rawParams, foundRoute] = + router.getMatchedRoutes(pathname) const isExactMatch = foundRoute && rawParams['**'] === undefined @@ -965,10 +965,10 @@ async function handleServerRoutes({ const serverMiddleware = route.options.server?.middleware as | Array | undefined - if (serverMiddleware) { + if (serverMiddleware?.length) { const flattened = flattenMiddlewares(serverMiddleware) for (const m of flattened) { - if (!executedRequestMiddlewares.has(m)) { + if (!executedRequestMiddlewares?.has(m)) { routeMiddlewares.push(m.options.server) } } @@ -1019,6 +1019,10 @@ async function handleServerRoutes({ } } + if (!routeMiddlewares.length && !terminalNext) { + return executeRouter(context, matchedRoutes) + } + const response = await executeMiddleware( routeMiddlewares, terminalHandler, diff --git a/packages/start-server-core/src/finalManifest.ts b/packages/start-server-core/src/finalManifest.ts index 274e6cb0006..f94388f9402 100644 --- a/packages/start-server-core/src/finalManifest.ts +++ b/packages/start-server-core/src/finalManifest.ts @@ -190,8 +190,23 @@ export function createFinalManifestResolver( transformResolver.getTransformFn({ warmup: true }), onError: transformResolver.clearCachedCreateTransform, }), - resolveCached: (requestOpts) => - resolveRequest(requestOpts, finalManifestCache), + resolveCached: (requestOpts) => { + if ( + opts.transformAssets === undefined && + handlerDefaultInlineCss !== undefined + ) { + const cachedManifest = finalManifestCache.get( + getFinalManifestCacheKey( + requestOpts.requestInlineCss ?? handlerDefaultInlineCss, + ), + ) + if (cachedManifest) { + return cachedManifest + } + } + + return resolveRequest(requestOpts, finalManifestCache) + }, resolveUncached: (requestOpts) => resolveRequest(requestOpts, undefined), } } diff --git a/packages/start-server-core/tests/createStartHandler.test.ts b/packages/start-server-core/tests/createStartHandler.test.ts index 680bff7ccf1..c333af0000d 100644 --- a/packages/start-server-core/tests/createStartHandler.test.ts +++ b/packages/start-server-core/tests/createStartHandler.test.ts @@ -13,6 +13,7 @@ import { RouterCore, createNonReactiveMutableStore, createNonReactiveReadonlyStore, + createSerializationAdapter, redirect, } from '@tanstack/router-core' import { @@ -28,7 +29,7 @@ import { getStaticHandlerInlineCssDefault, resolveInlineCssForRequest, } from '../src/inlineCss' -import type { AnyRouter } from '@tanstack/router-core' +import type { AnyRouter, AnySerializationAdapter } from '@tanstack/router-core' const startMocks = vi.hoisted(() => { const hadServerFnBase = Object.prototype.hasOwnProperty.call( @@ -41,6 +42,7 @@ const startMocks = vi.hoisted(() => { hadServerFnBase, previousServerFnBase, requestMiddleware: [] as Array, + serializationAdapters: [] as Array, serverFnResult: undefined as undefined | Response | object, serverFnHandler: undefined as undefined | (() => unknown), router: undefined as undefined | AnyRouter, @@ -56,7 +58,7 @@ vi.mock('#tanstack-start-entry', () => ({ ? { getOptions: () => ({ requestMiddleware: startMocks.requestMiddleware, - serializationAdapters: [], + serializationAdapters: startMocks.serializationAdapters, }), } : undefined @@ -173,6 +175,7 @@ function makeCompletingStreamResponse(router: ReturnType) { afterEach(() => { startMocks.requestMiddleware = [] + startMocks.serializationAdapters = [] startMocks.serverFnResult = undefined startMocks.serverFnHandler = undefined startMocks.router = undefined @@ -598,6 +601,138 @@ describe('createStartHandler redirect safety', () => { }) describe('createStartHandler server-route handling', () => { + it.each([ + { path: '/work', method: 'GET', body: 'app response' }, + { path: '/fallback', method: 'GET', body: 'app response' }, + { path: '/fallback', method: 'HEAD', body: '' }, + ])( + 'renders $method $path alongside API routes', + async ({ path, method, body }) => { + const root = new BaseRootRoute() + startMocks.router = new RouterCore( + { + isServer: true, + routeTree: root.addChildren([ + new BaseRoute({ + getParentRoute: () => root, + path: '/work', + component: () => null, + }), + new BaseRoute({ + getParentRoute: () => root, + path: '/fallback', + component: () => null, + server: { + handlers: ({ createHandlers }) => createHandlers({ GET: {} }), + }, + }), + new BaseRoute({ + getParentRoute: () => root, + path: '/api', + server: { handlers: { GET: () => new Response('api response') } }, + }), + ]), + }, + getStoreConfig, + ) + const render = vi.fn(({ router }: { router: AnyRouter }) => { + expect(router.state.matches.at(-1)?.routeId).toBe(path) + return new Response('app response', { + status: 202, + headers: { 'x-rendered': 'true' }, + }) + }) + const handler = createStartHandler(render) + + const response = await handler( + new Request(`http://localhost${path}`, { method }), + {}, + ) + + expect(response.status).toBe(202) + expect(response.headers.get('x-rendered')).toBe('true') + expect(await response.text()).toBe(body) + expect(render).toHaveBeenCalledOnce() + expect(startMocks.router.serverSsr).toBeUndefined() + }, + ) + + it.each([false, true])( + 'runs shared route middleware once and preserves its context (global=%s)', + async (global) => { + const runMiddleware = vi.fn(({ next }) => + next({ context: { message: 'trusted middleware' } }), + ) + const middleware = createMiddleware().server(runMiddleware) + startMocks.requestMiddleware = global ? [middleware] : [] + const root = new BaseRootRoute({ + server: { middleware: [middleware] }, + }) + startMocks.router = new RouterCore( + { + isServer: true, + routeTree: root.addChildren([ + new BaseRoute({ + getParentRoute: () => root, + path: '/', + component: () => null, + }), + ]), + }, + getStoreConfig, + ) + const handler = createStartHandler<{ + server: { requestContext: { message: string; requestValue: string } } + }>(({ router }) => + Response.json(router.options.additionalContext?.serverContext), + ) + + const response = await handler(new Request('http://localhost/'), { + context: { message: 'request context', requestValue: 'retained' }, + }) + + expect(response.status).toBe(200) + expect(await response.json()).toEqual({ + message: 'trusted middleware', + requestValue: 'retained', + }) + expect(runMiddleware).toHaveBeenCalledOnce() + }, + ) + + it('keeps adapter order and router adapters isolated to their request', async () => { + const adapter = (key: string) => + createSerializationAdapter({ + key, + test: (value: unknown): value is Date => value instanceof Date, + toSerializable: (value: Date) => value.toISOString(), + fromSerializable: (value: string) => new Date(value), + }) + const startAdapter = adapter('start') + const routerAdapter = adapter('router') + const startAdapters = [startAdapter] + startMocks.serializationAdapters = startAdapters + let requestCount = 0 + startMocks.routerFactory = () => { + const router = makeRouter() + router.options.serializationAdapters = + requestCount++ === 0 ? [routerAdapter] : [] + return router + } + const handler = createStartHandler(({ router }) => + Response.json( + router.options.serializationAdapters?.map(({ key }) => key), + ), + ) + + const first = await handler(new Request('http://localhost/'), {}) + const second = await handler(new Request('http://localhost/'), {}) + + expect(await first.json()).toEqual(['start', '$TSS/serverfn', 'router']) + expect(await second.json()).toEqual(['start', '$TSS/serverfn']) + expect(startAdapters).toEqual([startAdapter]) + }) + it('keeps default CSRF protection on server function requests', async () => { startMocks.hasStartInstance = false startMocks.hasServerRoutes = false @@ -800,10 +935,8 @@ describe('createStartHandler request location reuse', () => { renderApp ? 'app response' : 'server response', ) expect(input).toHaveBeenCalledOnce() - expect(serverHandler).toHaveBeenCalledExactlyOnceWith( - `/${encodeURIComponent(path)}`, - ) - expect(middlewarePathnames).toEqual([`/${encodeURIComponent(path)}`]) + expect(serverHandler).toHaveBeenCalledExactlyOnceWith(`/${path}`) + expect(middlewarePathnames).toEqual([`/${path}`]) expect(render).toHaveBeenCalledTimes(renderApp ? 1 : 0) expect( getMatchedRoutes.mock.calls.every( @@ -816,6 +949,23 @@ describe('createStartHandler request location reuse', () => { }, ) + it('uses the request URL origin regardless of the Origin header', async () => { + startMocks.router = makeRouterWithRouteWork({}) + const handler = createStartHandler( + ({ router }) => new Response(router.origin), + ) + + const response = await handler( + new Request('https://public.example:8443/work', { + headers: { Origin: 'https://untrusted.example' }, + }), + {}, + ) + + expect(response.status).toBe(200) + expect(await response.text()).toBe('https://public.example:8443') + }) + it('uses the configured origin for server route rewrites', async () => { const input = vi.fn(({ url }: { url: URL }) => { url.pathname = @@ -896,9 +1046,15 @@ describe('createStartHandler request location reuse', () => { expect(parseSearch).toHaveBeenCalledExactlyOnceWith('?page=2') }) - it.each(['a/b', 'a%b', 'a b', 'a?b', 'a#b'])( + it.each([ + ['a/b', 'a%2Fb'], + ['a%b', 'a%25b'], + ['a b', 'a b'], + ['a?b', 'a%3Fb'], + ['a#b', 'a%23b'], + ])( 'preserves encoded params %j when a server handler continues to SSR', - async (value) => { + async (value, pathname) => { const handlerParams: Array = [] const handlerPathnames: Array = [] const root = new BaseRootRoute() @@ -936,7 +1092,7 @@ describe('createStartHandler request location reuse', () => { expect(response.status).toBe(200) expect(handlerParams).toEqual([value]) - expect(handlerPathnames).toEqual([`/params/${encodeURIComponent(value)}`]) + expect(handlerPathnames).toEqual([`/params/${pathname}`]) expect(await response.json()).toEqual({ params: { value }, loaderData: value, diff --git a/packages/start-server-core/tests/finalManifest.test.ts b/packages/start-server-core/tests/finalManifest.test.ts index dabcb19a3d2..dcb84dc0964 100644 --- a/packages/start-server-core/tests/finalManifest.test.ts +++ b/packages/start-server-core/tests/finalManifest.test.ts @@ -85,6 +85,99 @@ describe('final manifest resolver', () => { expect(getBaseManifest).toHaveBeenCalledTimes(2) }) + it.each([undefined, true, false])( + 'shares cached promises without transforms for inlineCss=%s', + async (inlineCss) => { + const getBaseManifest = vi.fn(async () => baseManifest) + const resolver = createFinalManifestResolver({ + inlineCss, + cacheCreateTransform: true, + }) + const requestOpts = { + request: new Request('https://example.com'), + requestInlineCss: undefined, + getBaseManifest, + } + const defaultManifest = await resolver.resolveCached(requestOpts) + const cachedDefault = resolver.resolveCached(requestOpts) + + expect(resolver.resolveCached(requestOpts)).toBe(cachedDefault) + await expect(cachedDefault).resolves.toBe(defaultManifest) + expect(!!defaultManifest.inlineCss).toBe(inlineCss ?? true) + + const overrideOpts = { + ...requestOpts, + requestInlineCss: !(inlineCss ?? true), + } + const overrideManifest = await resolver.resolveCached(overrideOpts) + const cachedOverride = resolver.resolveCached(overrideOpts) + + expect(resolver.resolveCached(overrideOpts)).toBe(cachedOverride) + await expect(cachedOverride).resolves.toBe(overrideManifest) + expect(!!overrideManifest.inlineCss).toBe(overrideOpts.requestInlineCss) + expect(cachedOverride).not.toBe(cachedDefault) + expect(getBaseManifest).toHaveBeenCalledTimes(2) + }, + ) + + it('loads current assets for uncached requests without transforms', async () => { + const getBaseManifest = vi.fn(async () => baseManifest) + const resolver = createFinalManifestResolver({ + cacheCreateTransform: false, + }) + const requestOpts = { + request: new Request('https://example.com'), + requestInlineCss: undefined, + getBaseManifest, + } + const cached = await resolver.resolveCached(requestOpts) + getBaseManifest.mockResolvedValue({ + ...baseManifest, + inlineCss: { styles: { '/assets/app.css': '.app{color:blue}' } }, + }) + + const uncached = await resolver.resolveUncached(requestOpts) + + expect(uncached.inlineCss?.styles['/assets/app.css']).toBe( + '.app{color:blue}', + ) + expect(cached.inlineCss?.styles['/assets/app.css']).toBe('.app{color:red}') + expect(getBaseManifest).toHaveBeenCalledTimes(2) + }) + + it('evaluates request-dependent inline CSS without transforms unless overridden', async () => { + const inlineCss = vi.fn(({ request }: { request: Request }) => + request.url.endsWith('/inline'), + ) + const resolver = createFinalManifestResolver({ + inlineCss, + cacheCreateTransform: true, + }) + const getBaseManifest = vi.fn(async () => baseManifest) + + const inline = await resolver.resolveCached({ + request: new Request('https://example.com/inline'), + requestInlineCss: undefined, + getBaseManifest, + }) + const linked = await resolver.resolveCached({ + request: new Request('https://example.com/linked'), + requestInlineCss: undefined, + getBaseManifest, + }) + const overridden = await resolver.resolveCached({ + request: new Request('https://example.com/linked'), + requestInlineCss: true, + getBaseManifest, + }) + + expect(inline.inlineCss).toBeDefined() + expect(linked.inlineCss).toBeUndefined() + expect(overridden).toBe(inline) + expect(inlineCss).toHaveBeenCalledTimes(2) + expect(getBaseManifest).toHaveBeenCalledTimes(2) + }) + it('evicts rejected cached final manifest promises so requests can retry', async () => { const getBaseManifest = vi .fn<() => Promise>() diff --git a/packages/start-storage-context/src/async-local-storage.ts b/packages/start-storage-context/src/async-local-storage.ts index 21aabd6cdf7..2f79577a53a 100644 --- a/packages/start-storage-context/src/async-local-storage.ts +++ b/packages/start-storage-context/src/async-local-storage.ts @@ -16,7 +16,7 @@ export interface StartStorageContext { contextAfterGlobalMiddlewares: any // Track middlewares that have already executed in the request phase // to prevent duplicate execution - executedRequestMiddlewares: Set + executedRequestMiddlewares?: Set // Type of handler processing this request handlerType: StartHandlerType From 9dffae9090fde178de5f11627c694bf248b1b49f Mon Sep 17 00:00:00 2001 From: "nx-cloud[bot]" <71083854+nx-cloud[bot]@users.noreply.github.com> Date: Wed, 16 Sep 2026 19:30:33 +0000 Subject: [PATCH 2/2] perf: skip unused Start request setup [Self-Healing CI Rerun]