diff --git a/e2e/react-start/server-functions/src/routeTree.gen.ts b/e2e/react-start/server-functions/src/routeTree.gen.ts index 6909bce9452..080480bb344 100644 --- a/e2e/react-start/server-functions/src/routeTree.gen.ts +++ b/e2e/react-start/server-functions/src/routeTree.gen.ts @@ -27,6 +27,7 @@ import { Route as FormdataRedirectIndexRouteImport } from './routes/formdata-red import { Route as FactoryIndexRouteImport } from './routes/factory/index' import { Route as CookiesIndexRouteImport } from './routes/cookies/index' import { Route as MiddlewareSendServerFnRouteImport } from './routes/middleware/send-serverFn' +import { Route as MiddlewareRequestMiddlewareRouteImport } from './routes/middleware/request-middleware' import { Route as MiddlewareClientMiddlewareRouterRouteImport } from './routes/middleware/client-middleware-router' import { Route as CookiesSetRouteImport } from './routes/cookies/set' import { Route as FormdataRedirectTargetNameRouteImport } from './routes/formdata-redirect/target.$name' @@ -121,6 +122,12 @@ const MiddlewareSendServerFnRoute = MiddlewareSendServerFnRouteImport.update({ path: '/middleware/send-serverFn', getParentRoute: () => rootRouteImport, } as any) +const MiddlewareRequestMiddlewareRoute = + MiddlewareRequestMiddlewareRouteImport.update({ + id: '/middleware/request-middleware', + path: '/middleware/request-middleware', + getParentRoute: () => rootRouteImport, + } as any) const MiddlewareClientMiddlewareRouterRoute = MiddlewareClientMiddlewareRouterRouteImport.update({ id: '/middleware/client-middleware-router', @@ -155,6 +162,7 @@ export interface FileRoutesByFullPath { '/submit-post-formdata': typeof SubmitPostFormdataRoute '/cookies/set': typeof CookiesSetRoute '/middleware/client-middleware-router': typeof MiddlewareClientMiddlewareRouterRoute + '/middleware/request-middleware': typeof MiddlewareRequestMiddlewareRoute '/middleware/send-serverFn': typeof MiddlewareSendServerFnRoute '/cookies': typeof CookiesIndexRoute '/factory': typeof FactoryIndexRoute @@ -178,6 +186,7 @@ export interface FileRoutesByTo { '/submit-post-formdata': typeof SubmitPostFormdataRoute '/cookies/set': typeof CookiesSetRoute '/middleware/client-middleware-router': typeof MiddlewareClientMiddlewareRouterRoute + '/middleware/request-middleware': typeof MiddlewareRequestMiddlewareRoute '/middleware/send-serverFn': typeof MiddlewareSendServerFnRoute '/cookies': typeof CookiesIndexRoute '/factory': typeof FactoryIndexRoute @@ -202,6 +211,7 @@ export interface FileRoutesById { '/submit-post-formdata': typeof SubmitPostFormdataRoute '/cookies/set': typeof CookiesSetRoute '/middleware/client-middleware-router': typeof MiddlewareClientMiddlewareRouterRoute + '/middleware/request-middleware': typeof MiddlewareRequestMiddlewareRoute '/middleware/send-serverFn': typeof MiddlewareSendServerFnRoute '/cookies/': typeof CookiesIndexRoute '/factory/': typeof FactoryIndexRoute @@ -227,6 +237,7 @@ export interface FileRouteTypes { | '/submit-post-formdata' | '/cookies/set' | '/middleware/client-middleware-router' + | '/middleware/request-middleware' | '/middleware/send-serverFn' | '/cookies' | '/factory' @@ -250,6 +261,7 @@ export interface FileRouteTypes { | '/submit-post-formdata' | '/cookies/set' | '/middleware/client-middleware-router' + | '/middleware/request-middleware' | '/middleware/send-serverFn' | '/cookies' | '/factory' @@ -273,6 +285,7 @@ export interface FileRouteTypes { | '/submit-post-formdata' | '/cookies/set' | '/middleware/client-middleware-router' + | '/middleware/request-middleware' | '/middleware/send-serverFn' | '/cookies/' | '/factory/' @@ -297,6 +310,7 @@ export interface RootRouteChildren { SubmitPostFormdataRoute: typeof SubmitPostFormdataRoute CookiesSetRoute: typeof CookiesSetRoute MiddlewareClientMiddlewareRouterRoute: typeof MiddlewareClientMiddlewareRouterRoute + MiddlewareRequestMiddlewareRoute: typeof MiddlewareRequestMiddlewareRoute MiddlewareSendServerFnRoute: typeof MiddlewareSendServerFnRoute CookiesIndexRoute: typeof CookiesIndexRoute FactoryIndexRoute: typeof FactoryIndexRoute @@ -433,6 +447,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof MiddlewareSendServerFnRouteImport parentRoute: typeof rootRouteImport } + '/middleware/request-middleware': { + id: '/middleware/request-middleware' + path: '/middleware/request-middleware' + fullPath: '/middleware/request-middleware' + preLoaderRoute: typeof MiddlewareRequestMiddlewareRouteImport + parentRoute: typeof rootRouteImport + } '/middleware/client-middleware-router': { id: '/middleware/client-middleware-router' path: '/middleware/client-middleware-router' @@ -473,6 +494,7 @@ const rootRouteChildren: RootRouteChildren = { SubmitPostFormdataRoute: SubmitPostFormdataRoute, CookiesSetRoute: CookiesSetRoute, MiddlewareClientMiddlewareRouterRoute: MiddlewareClientMiddlewareRouterRoute, + MiddlewareRequestMiddlewareRoute: MiddlewareRequestMiddlewareRoute, MiddlewareSendServerFnRoute: MiddlewareSendServerFnRoute, CookiesIndexRoute: CookiesIndexRoute, FactoryIndexRoute: FactoryIndexRoute, diff --git a/e2e/react-start/server-functions/src/routes/middleware/index.tsx b/e2e/react-start/server-functions/src/routes/middleware/index.tsx index 7af7d93343c..1119e6bfea9 100644 --- a/e2e/react-start/server-functions/src/routes/middleware/index.tsx +++ b/e2e/react-start/server-functions/src/routes/middleware/index.tsx @@ -24,6 +24,15 @@ function RouteComponent() { Client Middleware can send server function reference in context +
  • + + Request Middleware in combination with server function + +
  • ) diff --git a/e2e/react-start/server-functions/src/routes/middleware/request-middleware.tsx b/e2e/react-start/server-functions/src/routes/middleware/request-middleware.tsx new file mode 100644 index 00000000000..58d3b6935ef --- /dev/null +++ b/e2e/react-start/server-functions/src/routes/middleware/request-middleware.tsx @@ -0,0 +1,83 @@ +import { createFileRoute } from '@tanstack/react-router' +import { createMiddleware, createServerFn } from '@tanstack/react-start' +import { getRequest } from '@tanstack/react-start/server' +import React from 'react' + +const requestMiddleware = createMiddleware({ type: 'request' }).server( + async ({ next, request }) => { + return next({ + context: { + requestParam: request.url, + requestFunc: getRequest().url, + }, + }) + }, +) + +const serverFn = createServerFn() + .middleware([requestMiddleware]) + .handler(async ({ context: { requestParam, requestFunc } }) => { + return { requestParam, requestFunc } + }) + +export const Route = createFileRoute('/middleware/request-middleware')({ + loader: () => serverFn(), + component: RouteComponent, +}) + +function RouteComponent() { + const loaderData = Route.useLoaderData() + + const [clientData, setClientData] = React.useState( + null, + ) + + return ( +
    +

    Request Middleware in combination with server function

    +
    +
    +
    +

    Loader Data

    Request Param: +
    + {loaderData.requestParam} +
    + Request Func: +
    + {loaderData.requestFunc} +
    +
    +
    +
    + +
    +
    +
    +

    Client Data

    + {clientData ? ( +
    + Request Param: +
    + {clientData.requestParam} +
    + Request Func: +
    + {clientData.requestFunc} +
    +
    + ) : ( + ' Loading ...' + )} +
    +
    +
    + ) +} diff --git a/e2e/react-start/server-functions/tests/server-functions.spec.ts b/e2e/react-start/server-functions/tests/server-functions.spec.ts index 72488ad0dc1..08f48524cb8 100644 --- a/e2e/react-start/server-functions/tests/server-functions.spec.ts +++ b/e2e/react-start/server-functions/tests/server-functions.spec.ts @@ -382,6 +382,32 @@ test.describe('middleware', () => { await runTest(page) }) }) + + test('server function in combination with request middleware', async ({ + page, + }) => { + await page.goto('/middleware/request-middleware') + + await page.waitForLoadState('networkidle') + + async function checkEqual(prefix: string) { + const requestParam = await page + .getByTestId(`${prefix}-data-request-param`) + .textContent() + expect(requestParam).not.toBe('') + const requestFunc = await page + .getByTestId(`${prefix}-data-request-func`) + .textContent() + expect(requestParam).toBe(requestFunc) + } + + await checkEqual('loader') + + await page.getByTestId('client-call-button').click() + await page.waitForLoadState('networkidle') + + await checkEqual('client') + }) }) test('factory', async ({ page }) => { diff --git a/packages/start-client-core/src/createServerFn.ts b/packages/start-client-core/src/createServerFn.ts index 75a9a280017..bc95a6b5b9a 100644 --- a/packages/start-client-core/src/createServerFn.ts +++ b/packages/start-client-core/src/createServerFn.ts @@ -2,8 +2,8 @@ import { isNotFound, isRedirect } from '@tanstack/router-core' import { mergeHeaders } from '@tanstack/router-core/ssr/client' import { TSS_SERVER_FUNCTION_FACTORY } from './constants' -import { getServerContextAfterGlobalMiddlewares } from './getServerContextAfterGlobalMiddlewares' import { getStartOptions } from './getStartOptions' +import { getStartContextServerOnly } from './getStartContextServerOnly' import type { TSS_SERVER_FUNCTION } from './constants' import type { AnyValidator, @@ -130,8 +130,9 @@ export const createServerFn: CreateServerFn = (options, __opts) => { // The extracted function on the server-side calls // this function __executeServer: async (opts: any, signal: AbortSignal) => { + const startContext = getStartContextServerOnly() const serverContextAfterGlobalMiddlewares = - getServerContextAfterGlobalMiddlewares() + startContext.contextAfterGlobalMiddlewares const ctx = { ...extractedFn, ...opts, @@ -140,6 +141,7 @@ export const createServerFn: CreateServerFn = (options, __opts) => { ...opts.context, }, signal, + request: startContext.request, } return executeMiddleware(resolvedMiddleware, 'server', ctx).then( @@ -199,11 +201,16 @@ export async function executeMiddleware( ) } - const middlewareFn = ( - env === 'client' && 'client' in nextMiddleware.options - ? nextMiddleware.options.client - : nextMiddleware.options.server - ) as MiddlewareFn | undefined + let middlewareFn: MiddlewareFn | undefined = undefined + if (env === 'client') { + if ('client' in nextMiddleware.options) { + middlewareFn = nextMiddleware.options.client as MiddlewareFn | undefined + } + } + // env === 'server' + else if ('server' in nextMiddleware.options) { + middlewareFn = nextMiddleware.options.server as MiddlewareFn | undefined + } if (middlewareFn) { // Execute the middleware diff --git a/packages/start-client-core/src/getServerContextAfterGlobalMiddlewares.ts b/packages/start-client-core/src/getServerContextAfterGlobalMiddlewares.ts deleted file mode 100644 index 72b740e8b3c..00000000000 --- a/packages/start-client-core/src/getServerContextAfterGlobalMiddlewares.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { getStartContext } from '@tanstack/start-storage-context' -import { createServerOnlyFn } from './envOnly' - -export const getServerContextAfterGlobalMiddlewares = createServerOnlyFn(() => { - const start = getStartContext() - return start.contextAfterGlobalMiddlewares -}) diff --git a/packages/start-client-core/src/getStartContextServerOnly.ts b/packages/start-client-core/src/getStartContextServerOnly.ts new file mode 100644 index 00000000000..78787a49825 --- /dev/null +++ b/packages/start-client-core/src/getStartContextServerOnly.ts @@ -0,0 +1,4 @@ +import { getStartContext } from '@tanstack/start-storage-context' +import { createServerOnlyFn } from './envOnly' + +export const getStartContextServerOnly = createServerOnlyFn(getStartContext) diff --git a/packages/start-server-core/src/createStartHandler.ts b/packages/start-server-core/src/createStartHandler.ts index 1589a04dc1a..ae5e5ff2afc 100644 --- a/packages/start-server-core/src/createStartHandler.ts +++ b/packages/start-server-core/src/createStartHandler.ts @@ -170,6 +170,7 @@ export function createStartHandler( getRouter, startOptions, contextAfterGlobalMiddlewares: context, + request, }, async () => { try { diff --git a/packages/start-storage-context/src/async-local-storage.ts b/packages/start-storage-context/src/async-local-storage.ts index c353dbaf26d..ec02f0a47be 100644 --- a/packages/start-storage-context/src/async-local-storage.ts +++ b/packages/start-storage-context/src/async-local-storage.ts @@ -3,7 +3,7 @@ import type { Awaitable, RegisteredRouter } from '@tanstack/router-core' export interface StartStorageContext { getRouter: () => Awaitable - + request: Request // TODO type this properly startOptions: /* AnyStartInstanceOptions*/ any