From 795cdc2e2d7550c26997319dc4e36b75ab742dce Mon Sep 17 00:00:00 2001 From: Manuel Schiller Date: Sat, 22 Nov 2025 02:19:43 +0100 Subject: [PATCH 1/3] fix: allow to override serverfn method when using factories fixes: #5872 --- .../factory/-functions/createFooServerFn.ts | 8 +++-- .../src/routes/factory/index.tsx | 34 ++++++++++++++----- .../start-client-core/src/createServerFn.ts | 20 ++++------- .../src/tests/createServerFn.test-d.ts | 17 ---------- 4 files changed, 37 insertions(+), 42 deletions(-) diff --git a/e2e/react-start/server-functions/src/routes/factory/-functions/createFooServerFn.ts b/e2e/react-start/server-functions/src/routes/factory/-functions/createFooServerFn.ts index af13270b627..498b0387349 100644 --- a/e2e/react-start/server-functions/src/routes/factory/-functions/createFooServerFn.ts +++ b/e2e/react-start/server-functions/src/routes/factory/-functions/createFooServerFn.ts @@ -1,10 +1,12 @@ import { createMiddleware, createServerFn } from '@tanstack/react-start' +import { getRequest } from '@tanstack/react-start/server' const fooMiddleware = createMiddleware({ type: 'function' }).server( ({ next }) => { + const request = getRequest() console.log('Foo middleware triggered') return next({ - context: { foo: 'foo' } as const, + context: { foo: 'foo', method: request.method } as const, }) }, ) @@ -12,8 +14,8 @@ const fooMiddleware = createMiddleware({ type: 'function' }).server( export const createFooServerFn = createServerFn().middleware([fooMiddleware]) export const fooFnInsideFactoryFile = createFooServerFn().handler( - async ({ context, method }) => { - console.log('fooFnInsideFactoryFile handler triggered', method) + async ({ context }) => { + console.log('fooFnInsideFactoryFile handler triggered', context.method) return { name: 'fooFnInsideFactoryFile', context, diff --git a/e2e/react-start/server-functions/src/routes/factory/index.tsx b/e2e/react-start/server-functions/src/routes/factory/index.tsx index 65d2d177307..2186aae9ff2 100644 --- a/e2e/react-start/server-functions/src/routes/factory/index.tsx +++ b/e2e/react-start/server-functions/src/routes/factory/index.tsx @@ -39,7 +39,7 @@ const functions = { expected: { name: 'fooFnInsideFactoryFile', - context: { foo: 'foo' }, + context: { foo: 'foo', method: 'GET' }, }, }, fooFn: { @@ -48,7 +48,7 @@ const functions = { expected: { name: 'fooFn', - context: { foo: 'foo' }, + context: { foo: 'foo', method: 'GET' }, }, }, fooFnPOST: { @@ -57,7 +57,7 @@ const functions = { expected: { name: 'fooFnPOST', - context: { foo: 'foo' }, + context: { foo: 'foo', method: 'POST' }, }, }, barFn: { @@ -66,7 +66,7 @@ const functions = { expected: { name: 'barFn', - context: { foo: 'foo', bar: 'bar' }, + context: { foo: 'foo', method: 'GET', bar: 'bar' }, }, }, barFnPOST: { @@ -75,7 +75,7 @@ const functions = { expected: { name: 'barFnPOST', - context: { foo: 'foo', bar: 'bar' }, + context: { foo: 'foo', method: 'POST', bar: 'bar' }, }, }, localFn: { @@ -84,7 +84,13 @@ const functions = { expected: { name: 'localFn', - context: { foo: 'foo', bar: 'bar', local: 'local', another: 'another' }, + context: { + foo: 'foo', + method: 'GET', + bar: 'bar', + local: 'local', + another: 'another', + }, }, }, localFnPOST: { @@ -93,7 +99,13 @@ const functions = { expected: { name: 'localFnPOST', - context: { foo: 'foo', bar: 'bar', local: 'local', another: 'another' }, + context: { + foo: 'foo', + method: 'POST', + bar: 'bar', + local: 'local', + another: 'another', + }, }, }, composedFn: { @@ -101,7 +113,13 @@ const functions = { type: 'serverFn', expected: { name: 'composedFn', - context: { foo: 'foo', bar: 'bar', another: 'another', local: 'local' }, + context: { + foo: 'foo', + method: 'GET', + bar: 'bar', + another: 'another', + local: 'local', + }, }, }, fakeFn: { diff --git a/packages/start-client-core/src/createServerFn.ts b/packages/start-client-core/src/createServerFn.ts index 6fe82e1a1f1..ceeab4678b0 100644 --- a/packages/start-client-core/src/createServerFn.ts +++ b/packages/start-client-core/src/createServerFn.ts @@ -158,13 +158,11 @@ export const createServerFn: CreateServerFn = (options, __opts) => { }, } as ServerFnBuilder const fun = (options?: { method?: Method }) => { - return { - ...res, - options: { - ...res.options, - ...options, - }, + const newOptions = { + ...resolvedOptions, + ...options, } + return createServerFn(undefined, newOptions) as any } return Object.assign(fun, res) as any } @@ -314,16 +312,10 @@ export type ServerFn< TInputValidator, TResponse, > = ( - ctx: ServerFnCtx, + ctx: ServerFnCtx, ) => ServerFnReturnType -export interface ServerFnCtx< - TRegister, - TMethod, - TMiddlewares, - TInputValidator, -> { - method: TMethod +export interface ServerFnCtx { data: Expand> context: Expand> signal: AbortSignal diff --git a/packages/start-client-core/src/tests/createServerFn.test-d.ts b/packages/start-client-core/src/tests/createServerFn.test-d.ts index ec1fc4cbb2f..9d2f74b5382 100644 --- a/packages/start-client-core/src/tests/createServerFn.test-d.ts +++ b/packages/start-client-core/src/tests/createServerFn.test-d.ts @@ -11,12 +11,6 @@ import type { } from '@tanstack/router-core' import type { ConstrainValidator, ServerFnReturnType } from '../createServerFn' -test('createServerFn method with autocomplete', () => { - createServerFn().handler((options) => { - expectTypeOf(options.method).toEqualTypeOf<'GET' | 'POST'>() - }) -}) - test('createServerFn without middleware', () => { expectTypeOf(createServerFn()).toHaveProperty('handler') expectTypeOf(createServerFn()).toHaveProperty('middleware') @@ -24,7 +18,6 @@ test('createServerFn without middleware', () => { createServerFn({ method: 'GET' }).handler((options) => { expectTypeOf(options).toEqualTypeOf<{ - method: 'GET' context: undefined data: undefined signal: AbortSignal @@ -45,7 +38,6 @@ test('createServerFn with validator', () => { const fn = fnAfterValidator.handler((options) => { expectTypeOf(options).toEqualTypeOf<{ - method: 'GET' context: undefined data: { a: string @@ -105,7 +97,6 @@ test('createServerFn with middleware and context', () => { fnWithMiddleware.handler((options) => { expectTypeOf(options).toEqualTypeOf<{ - method: 'GET' context: { readonly a: 'a' readonly b: 'b' @@ -149,7 +140,6 @@ describe('createServerFn with middleware and validator', () => { ) .handler((options) => { expectTypeOf(options).toEqualTypeOf<{ - method: 'GET' context: undefined data: { readonly outputA: 'outputA' @@ -250,7 +240,6 @@ test('createServerFn where validator is a primitive', () => { .inputValidator(() => 'c' as const) .handler((options) => { expectTypeOf(options).toEqualTypeOf<{ - method: 'GET' context: undefined data: 'c' signal: AbortSignal @@ -263,7 +252,6 @@ test('createServerFn where validator is optional if object is optional', () => { .inputValidator((input: 'c' | undefined) => input) .handler((options) => { expectTypeOf(options).toEqualTypeOf<{ - method: 'GET' context: undefined data: 'c' | undefined signal: AbortSignal @@ -285,7 +273,6 @@ test('createServerFn where validator is optional if object is optional', () => { test('createServerFn where data is optional if there is no validator', () => { const fn = createServerFn({ method: 'GET' }).handler((options) => { expectTypeOf(options).toEqualTypeOf<{ - method: 'GET' context: undefined data: undefined signal: AbortSignal @@ -475,7 +462,6 @@ test('incrementally building createServerFn with multiple middleware calls', () builderWithMw1.handler((options) => { expectTypeOf(options).toEqualTypeOf<{ - method: 'GET' context: { readonly a: 'a' } @@ -495,7 +481,6 @@ test('incrementally building createServerFn with multiple middleware calls', () builderWithMw2.handler((options) => { expectTypeOf(options).toEqualTypeOf<{ - method: 'POST' context: { readonly a: 'a' readonly b: 'b' @@ -516,7 +501,6 @@ test('incrementally building createServerFn with multiple middleware calls', () builderWithMw3.handler((options) => { expectTypeOf(options).toEqualTypeOf<{ - method: 'GET' context: { readonly a: 'a' readonly b: 'b' @@ -550,7 +534,6 @@ test('compose middlewares and server function factories', () => { composedBuilder.handler((options) => { expectTypeOf(options).toEqualTypeOf<{ - method: 'GET' context: { readonly a: 'a' readonly b: 'b' From 9e8769eaa3065836985c99aa36b414cb18bc95e4 Mon Sep 17 00:00:00 2001 From: Manuel Schiller Date: Sat, 22 Nov 2025 10:44:53 +0100 Subject: [PATCH 2/3] solid e2e test --- .../factory/-functions/createFooServerFn.ts | 8 +++-- .../src/routes/factory/index.tsx | 34 ++++++++++++++----- 2 files changed, 31 insertions(+), 11 deletions(-) diff --git a/e2e/solid-start/server-functions/src/routes/factory/-functions/createFooServerFn.ts b/e2e/solid-start/server-functions/src/routes/factory/-functions/createFooServerFn.ts index 771d415a14f..c0cb69cff50 100644 --- a/e2e/solid-start/server-functions/src/routes/factory/-functions/createFooServerFn.ts +++ b/e2e/solid-start/server-functions/src/routes/factory/-functions/createFooServerFn.ts @@ -1,10 +1,12 @@ import { createMiddleware, createServerFn } from '@tanstack/solid-start' +import { getRequest } from '@tanstack/solid-start/server' const fooMiddleware = createMiddleware({ type: 'function' }).server( ({ next }) => { + const request = getRequest() console.log('Foo middleware triggered') return next({ - context: { foo: 'foo' } as const, + context: { foo: 'foo', method: request.method } as const, }) }, ) @@ -12,8 +14,8 @@ const fooMiddleware = createMiddleware({ type: 'function' }).server( export const createFooServerFn = createServerFn().middleware([fooMiddleware]) export const fooFnInsideFactoryFile = createFooServerFn().handler( - async ({ context, method }) => { - console.log('fooFnInsideFactoryFile handler triggered', method) + async ({ context }) => { + console.log('fooFnInsideFactoryFile handler triggered', context.method) return { name: 'fooFnInsideFactoryFile', context, diff --git a/e2e/solid-start/server-functions/src/routes/factory/index.tsx b/e2e/solid-start/server-functions/src/routes/factory/index.tsx index 0e99bedf52c..edbf65eb412 100644 --- a/e2e/solid-start/server-functions/src/routes/factory/index.tsx +++ b/e2e/solid-start/server-functions/src/routes/factory/index.tsx @@ -39,7 +39,7 @@ const functions = { expected: { name: 'fooFnInsideFactoryFile', - context: { foo: 'foo' }, + context: { foo: 'foo', method: 'GET' }, }, }, fooFn: { @@ -48,7 +48,7 @@ const functions = { expected: { name: 'fooFn', - context: { foo: 'foo' }, + context: { foo: 'foo', method: 'GET' }, }, }, fooFnPOST: { @@ -57,7 +57,7 @@ const functions = { expected: { name: 'fooFnPOST', - context: { foo: 'foo' }, + context: { foo: 'foo', method: 'POST' }, }, }, barFn: { @@ -66,7 +66,7 @@ const functions = { expected: { name: 'barFn', - context: { foo: 'foo', bar: 'bar' }, + context: { foo: 'foo', method: 'GET', bar: 'bar' }, }, }, barFnPOST: { @@ -75,7 +75,7 @@ const functions = { expected: { name: 'barFnPOST', - context: { foo: 'foo', bar: 'bar' }, + context: { foo: 'foo', method: 'POST', bar: 'bar' }, }, }, localFn: { @@ -84,7 +84,13 @@ const functions = { expected: { name: 'localFn', - context: { foo: 'foo', bar: 'bar', local: 'local', another: 'another' }, + context: { + foo: 'foo', + method: 'GET', + bar: 'bar', + local: 'local', + another: 'another', + }, }, }, localFnPOST: { @@ -93,7 +99,13 @@ const functions = { expected: { name: 'localFnPOST', - context: { foo: 'foo', bar: 'bar', local: 'local', another: 'another' }, + context: { + foo: 'foo', + method: 'POST', + bar: 'bar', + local: 'local', + another: 'another', + }, }, }, composedFn: { @@ -101,7 +113,13 @@ const functions = { type: 'serverFn', expected: { name: 'composedFn', - context: { foo: 'foo', bar: 'bar', another: 'another', local: 'local' }, + context: { + foo: 'foo', + method: 'GET', + bar: 'bar', + another: 'another', + local: 'local', + }, }, }, fakeFn: { From fcfc8c8595968c74b1e65f1cb188054a9095b8cc Mon Sep 17 00:00:00 2001 From: Manuel Schiller Date: Sat, 22 Nov 2025 10:45:06 +0100 Subject: [PATCH 3/3] format unrelated file --- .../basic-cloudflare/worker-configuration.d.ts | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/e2e/solid-start/basic-cloudflare/worker-configuration.d.ts b/e2e/solid-start/basic-cloudflare/worker-configuration.d.ts index b0b47f39468..cb3e271c882 100644 --- a/e2e/solid-start/basic-cloudflare/worker-configuration.d.ts +++ b/e2e/solid-start/basic-cloudflare/worker-configuration.d.ts @@ -2,16 +2,19 @@ // Generated by Wrangler by running `wrangler types` (hash: b11df627d8b3c51b1bf3230a546b0f20) // Runtime types generated with workerd@1.20251118.0 2025-09-24 nodejs_compat declare namespace Cloudflare { - interface Env { - MY_VAR: "Hello from Cloudflare"; - } + interface Env { + MY_VAR: 'Hello from Cloudflare' + } } interface Env extends Cloudflare.Env {} type StringifyValues> = { - [Binding in keyof EnvType]: EnvType[Binding] extends string ? EnvType[Binding] : string; -}; + [Binding in keyof EnvType]: EnvType[Binding] extends string + ? EnvType[Binding] + : string +} declare namespace NodeJS { - interface ProcessEnv extends StringifyValues> {} + interface ProcessEnv + extends StringifyValues> {} } // Begin runtime types