Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions e2e/react-start/server-functions/src/routeTree.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -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',
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -227,6 +237,7 @@ export interface FileRouteTypes {
| '/submit-post-formdata'
| '/cookies/set'
| '/middleware/client-middleware-router'
| '/middleware/request-middleware'
| '/middleware/send-serverFn'
| '/cookies'
| '/factory'
Expand All @@ -250,6 +261,7 @@ export interface FileRouteTypes {
| '/submit-post-formdata'
| '/cookies/set'
| '/middleware/client-middleware-router'
| '/middleware/request-middleware'
| '/middleware/send-serverFn'
| '/cookies'
| '/factory'
Expand All @@ -273,6 +285,7 @@ export interface FileRouteTypes {
| '/submit-post-formdata'
| '/cookies/set'
| '/middleware/client-middleware-router'
| '/middleware/request-middleware'
| '/middleware/send-serverFn'
| '/cookies/'
| '/factory/'
Expand All @@ -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
Expand Down Expand Up @@ -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'
Expand Down Expand Up @@ -473,6 +494,7 @@ const rootRouteChildren: RootRouteChildren = {
SubmitPostFormdataRoute: SubmitPostFormdataRoute,
CookiesSetRoute: CookiesSetRoute,
MiddlewareClientMiddlewareRouterRoute: MiddlewareClientMiddlewareRouterRoute,
MiddlewareRequestMiddlewareRoute: MiddlewareRequestMiddlewareRoute,
MiddlewareSendServerFnRoute: MiddlewareSendServerFnRoute,
CookiesIndexRoute: CookiesIndexRoute,
FactoryIndexRoute: FactoryIndexRoute,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,15 @@ function RouteComponent() {
Client Middleware can send server function reference in context
</Route.Link>
</li>
<li>
<Route.Link
to="./request-middleware"
data-testid="request-middleware-link"
reloadDocument={true}
>
Request Middleware in combination with server function
</Route.Link>
</li>
</ul>
</div>
)
Expand Down
Original file line number Diff line number Diff line change
@@ -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<typeof loaderData | null>(
null,
)

return (
<div>
<h2>Request Middleware in combination with server function</h2>
<br />
<div>
<div data-testid="loader-data">
<h3>Loader Data</h3>Request Param:
<div data-testid="loader-data-request-param">
{loaderData.requestParam}
</div>
Request Func:
<div data-testid="loader-data-request-func">
{loaderData.requestFunc}
</div>
</div>
<br />
<div data-testid="client-call">
<button
data-testid="client-call-button"
onClick={async () => {
const data = await serverFn()
setClientData(data)
}}
>
Call server function from client
</button>
</div>
<br />
<div data-testid="client-data-container">
<h3>Client Data</h3>
{clientData ? (
<div data-testid="client-data">
Request Param:
<div data-testid="client-data-request-param">
{clientData.requestParam}
</div>
Request Func:
<div data-testid="client-data-request-func">
{clientData.requestFunc}
</div>
</div>
) : (
' Loading ...'
)}
</div>
</div>
</div>
)
}
26 changes: 26 additions & 0 deletions e2e/react-start/server-functions/tests/server-functions.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 }) => {
Expand Down
21 changes: 14 additions & 7 deletions packages/start-client-core/src/createServerFn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -130,8 +130,9 @@ export const createServerFn: CreateServerFn<Register> = (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,
Expand All @@ -140,6 +141,7 @@ export const createServerFn: CreateServerFn<Register> = (options, __opts) => {
...opts.context,
},
signal,
request: startContext.request,
}

return executeMiddleware(resolvedMiddleware, 'server', ctx).then(
Expand Down Expand Up @@ -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
Expand Down

This file was deleted.

4 changes: 4 additions & 0 deletions packages/start-client-core/src/getStartContextServerOnly.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { getStartContext } from '@tanstack/start-storage-context'
import { createServerOnlyFn } from './envOnly'

export const getStartContextServerOnly = createServerOnlyFn(getStartContext)
1 change: 1 addition & 0 deletions packages/start-server-core/src/createStartHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ export function createStartHandler<TRegister = Register>(
getRouter,
startOptions,
contextAfterGlobalMiddlewares: context,
request,
},
async () => {
try {
Expand Down
2 changes: 1 addition & 1 deletion packages/start-storage-context/src/async-local-storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type { Awaitable, RegisteredRouter } from '@tanstack/router-core'

export interface StartStorageContext {
getRouter: () => Awaitable<RegisteredRouter>

request: Request
// TODO type this properly
startOptions: /* AnyStartInstanceOptions*/ any

Expand Down
Loading