Skip to content

Commit 2fd4cba

Browse files
fix: allow to override serverfn method when using factories (#5938)
* fix: allow to override serverfn method when using factories fixes: #5872 * solid e2e test * format unrelated file
1 parent ab676cb commit 2fd4cba

File tree

7 files changed

+77
-59
lines changed

7 files changed

+77
-59
lines changed

e2e/react-start/server-functions/src/routes/factory/-functions/createFooServerFn.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,21 @@
11
import { createMiddleware, createServerFn } from '@tanstack/react-start'
2+
import { getRequest } from '@tanstack/react-start/server'
23

34
const fooMiddleware = createMiddleware({ type: 'function' }).server(
45
({ next }) => {
6+
const request = getRequest()
57
console.log('Foo middleware triggered')
68
return next({
7-
context: { foo: 'foo' } as const,
9+
context: { foo: 'foo', method: request.method } as const,
810
})
911
},
1012
)
1113

1214
export const createFooServerFn = createServerFn().middleware([fooMiddleware])
1315

1416
export const fooFnInsideFactoryFile = createFooServerFn().handler(
15-
async ({ context, method }) => {
16-
console.log('fooFnInsideFactoryFile handler triggered', method)
17+
async ({ context }) => {
18+
console.log('fooFnInsideFactoryFile handler triggered', context.method)
1719
return {
1820
name: 'fooFnInsideFactoryFile',
1921
context,

e2e/react-start/server-functions/src/routes/factory/index.tsx

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ const functions = {
3939

4040
expected: {
4141
name: 'fooFnInsideFactoryFile',
42-
context: { foo: 'foo' },
42+
context: { foo: 'foo', method: 'GET' },
4343
},
4444
},
4545
fooFn: {
@@ -48,7 +48,7 @@ const functions = {
4848

4949
expected: {
5050
name: 'fooFn',
51-
context: { foo: 'foo' },
51+
context: { foo: 'foo', method: 'GET' },
5252
},
5353
},
5454
fooFnPOST: {
@@ -57,7 +57,7 @@ const functions = {
5757

5858
expected: {
5959
name: 'fooFnPOST',
60-
context: { foo: 'foo' },
60+
context: { foo: 'foo', method: 'POST' },
6161
},
6262
},
6363
barFn: {
@@ -66,7 +66,7 @@ const functions = {
6666

6767
expected: {
6868
name: 'barFn',
69-
context: { foo: 'foo', bar: 'bar' },
69+
context: { foo: 'foo', method: 'GET', bar: 'bar' },
7070
},
7171
},
7272
barFnPOST: {
@@ -75,7 +75,7 @@ const functions = {
7575

7676
expected: {
7777
name: 'barFnPOST',
78-
context: { foo: 'foo', bar: 'bar' },
78+
context: { foo: 'foo', method: 'POST', bar: 'bar' },
7979
},
8080
},
8181
localFn: {
@@ -84,7 +84,13 @@ const functions = {
8484

8585
expected: {
8686
name: 'localFn',
87-
context: { foo: 'foo', bar: 'bar', local: 'local', another: 'another' },
87+
context: {
88+
foo: 'foo',
89+
method: 'GET',
90+
bar: 'bar',
91+
local: 'local',
92+
another: 'another',
93+
},
8894
},
8995
},
9096
localFnPOST: {
@@ -93,15 +99,27 @@ const functions = {
9399

94100
expected: {
95101
name: 'localFnPOST',
96-
context: { foo: 'foo', bar: 'bar', local: 'local', another: 'another' },
102+
context: {
103+
foo: 'foo',
104+
method: 'POST',
105+
bar: 'bar',
106+
local: 'local',
107+
another: 'another',
108+
},
97109
},
98110
},
99111
composedFn: {
100112
fn: composedFn,
101113
type: 'serverFn',
102114
expected: {
103115
name: 'composedFn',
104-
context: { foo: 'foo', bar: 'bar', another: 'another', local: 'local' },
116+
context: {
117+
foo: 'foo',
118+
method: 'GET',
119+
bar: 'bar',
120+
another: 'another',
121+
local: 'local',
122+
},
105123
},
106124
},
107125
fakeFn: {

e2e/solid-start/basic-cloudflare/worker-configuration.d.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,19 @@
22
// Generated by Wrangler by running `wrangler types` (hash: b11df627d8b3c51b1bf3230a546b0f20)
33
// Runtime types generated with workerd@1.20251118.0 2025-09-24 nodejs_compat
44
declare namespace Cloudflare {
5-
interface Env {
6-
MY_VAR: "Hello from Cloudflare";
7-
}
5+
interface Env {
6+
MY_VAR: 'Hello from Cloudflare'
7+
}
88
}
99
interface Env extends Cloudflare.Env {}
1010
type StringifyValues<EnvType extends Record<string, unknown>> = {
11-
[Binding in keyof EnvType]: EnvType[Binding] extends string ? EnvType[Binding] : string;
12-
};
11+
[Binding in keyof EnvType]: EnvType[Binding] extends string
12+
? EnvType[Binding]
13+
: string
14+
}
1315
declare namespace NodeJS {
14-
interface ProcessEnv extends StringifyValues<Pick<Cloudflare.Env, "MY_VAR">> {}
16+
interface ProcessEnv
17+
extends StringifyValues<Pick<Cloudflare.Env, 'MY_VAR'>> {}
1518
}
1619

1720
// Begin runtime types

e2e/solid-start/server-functions/src/routes/factory/-functions/createFooServerFn.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,21 @@
11
import { createMiddleware, createServerFn } from '@tanstack/solid-start'
2+
import { getRequest } from '@tanstack/solid-start/server'
23

34
const fooMiddleware = createMiddleware({ type: 'function' }).server(
45
({ next }) => {
6+
const request = getRequest()
57
console.log('Foo middleware triggered')
68
return next({
7-
context: { foo: 'foo' } as const,
9+
context: { foo: 'foo', method: request.method } as const,
810
})
911
},
1012
)
1113

1214
export const createFooServerFn = createServerFn().middleware([fooMiddleware])
1315

1416
export const fooFnInsideFactoryFile = createFooServerFn().handler(
15-
async ({ context, method }) => {
16-
console.log('fooFnInsideFactoryFile handler triggered', method)
17+
async ({ context }) => {
18+
console.log('fooFnInsideFactoryFile handler triggered', context.method)
1719
return {
1820
name: 'fooFnInsideFactoryFile',
1921
context,

e2e/solid-start/server-functions/src/routes/factory/index.tsx

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ const functions = {
3939

4040
expected: {
4141
name: 'fooFnInsideFactoryFile',
42-
context: { foo: 'foo' },
42+
context: { foo: 'foo', method: 'GET' },
4343
},
4444
},
4545
fooFn: {
@@ -48,7 +48,7 @@ const functions = {
4848

4949
expected: {
5050
name: 'fooFn',
51-
context: { foo: 'foo' },
51+
context: { foo: 'foo', method: 'GET' },
5252
},
5353
},
5454
fooFnPOST: {
@@ -57,7 +57,7 @@ const functions = {
5757

5858
expected: {
5959
name: 'fooFnPOST',
60-
context: { foo: 'foo' },
60+
context: { foo: 'foo', method: 'POST' },
6161
},
6262
},
6363
barFn: {
@@ -66,7 +66,7 @@ const functions = {
6666

6767
expected: {
6868
name: 'barFn',
69-
context: { foo: 'foo', bar: 'bar' },
69+
context: { foo: 'foo', method: 'GET', bar: 'bar' },
7070
},
7171
},
7272
barFnPOST: {
@@ -75,7 +75,7 @@ const functions = {
7575

7676
expected: {
7777
name: 'barFnPOST',
78-
context: { foo: 'foo', bar: 'bar' },
78+
context: { foo: 'foo', method: 'POST', bar: 'bar' },
7979
},
8080
},
8181
localFn: {
@@ -84,7 +84,13 @@ const functions = {
8484

8585
expected: {
8686
name: 'localFn',
87-
context: { foo: 'foo', bar: 'bar', local: 'local', another: 'another' },
87+
context: {
88+
foo: 'foo',
89+
method: 'GET',
90+
bar: 'bar',
91+
local: 'local',
92+
another: 'another',
93+
},
8894
},
8995
},
9096
localFnPOST: {
@@ -93,15 +99,27 @@ const functions = {
9399

94100
expected: {
95101
name: 'localFnPOST',
96-
context: { foo: 'foo', bar: 'bar', local: 'local', another: 'another' },
102+
context: {
103+
foo: 'foo',
104+
method: 'POST',
105+
bar: 'bar',
106+
local: 'local',
107+
another: 'another',
108+
},
97109
},
98110
},
99111
composedFn: {
100112
fn: composedFn,
101113
type: 'serverFn',
102114
expected: {
103115
name: 'composedFn',
104-
context: { foo: 'foo', bar: 'bar', another: 'another', local: 'local' },
116+
context: {
117+
foo: 'foo',
118+
method: 'GET',
119+
bar: 'bar',
120+
another: 'another',
121+
local: 'local',
122+
},
105123
},
106124
},
107125
fakeFn: {

packages/start-client-core/src/createServerFn.ts

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -158,13 +158,11 @@ export const createServerFn: CreateServerFn<Register> = (options, __opts) => {
158158
},
159159
} as ServerFnBuilder<Register, Method>
160160
const fun = (options?: { method?: Method }) => {
161-
return {
162-
...res,
163-
options: {
164-
...res.options,
165-
...options,
166-
},
161+
const newOptions = {
162+
...resolvedOptions,
163+
...options,
167164
}
165+
return createServerFn(undefined, newOptions) as any
168166
}
169167
return Object.assign(fun, res) as any
170168
}
@@ -314,16 +312,10 @@ export type ServerFn<
314312
TInputValidator,
315313
TResponse,
316314
> = (
317-
ctx: ServerFnCtx<TRegister, TMethod, TMiddlewares, TInputValidator>,
315+
ctx: ServerFnCtx<TRegister, TMiddlewares, TInputValidator>,
318316
) => ServerFnReturnType<TRegister, TResponse>
319317

320-
export interface ServerFnCtx<
321-
TRegister,
322-
TMethod,
323-
TMiddlewares,
324-
TInputValidator,
325-
> {
326-
method: TMethod
318+
export interface ServerFnCtx<TRegister, TMiddlewares, TInputValidator> {
327319
data: Expand<IntersectAllValidatorOutputs<TMiddlewares, TInputValidator>>
328320
context: Expand<AssignAllServerFnContext<TRegister, TMiddlewares, {}>>
329321
signal: AbortSignal

packages/start-client-core/src/tests/createServerFn.test-d.ts

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,13 @@ import type {
1111
} from '@tanstack/router-core'
1212
import type { ConstrainValidator, ServerFnReturnType } from '../createServerFn'
1313

14-
test('createServerFn method with autocomplete', () => {
15-
createServerFn().handler((options) => {
16-
expectTypeOf(options.method).toEqualTypeOf<'GET' | 'POST'>()
17-
})
18-
})
19-
2014
test('createServerFn without middleware', () => {
2115
expectTypeOf(createServerFn()).toHaveProperty('handler')
2216
expectTypeOf(createServerFn()).toHaveProperty('middleware')
2317
expectTypeOf(createServerFn()).toHaveProperty('inputValidator')
2418

2519
createServerFn({ method: 'GET' }).handler((options) => {
2620
expectTypeOf(options).toEqualTypeOf<{
27-
method: 'GET'
2821
context: undefined
2922
data: undefined
3023
signal: AbortSignal
@@ -45,7 +38,6 @@ test('createServerFn with validator', () => {
4538

4639
const fn = fnAfterValidator.handler((options) => {
4740
expectTypeOf(options).toEqualTypeOf<{
48-
method: 'GET'
4941
context: undefined
5042
data: {
5143
a: string
@@ -105,7 +97,6 @@ test('createServerFn with middleware and context', () => {
10597

10698
fnWithMiddleware.handler((options) => {
10799
expectTypeOf(options).toEqualTypeOf<{
108-
method: 'GET'
109100
context: {
110101
readonly a: 'a'
111102
readonly b: 'b'
@@ -149,7 +140,6 @@ describe('createServerFn with middleware and validator', () => {
149140
)
150141
.handler((options) => {
151142
expectTypeOf(options).toEqualTypeOf<{
152-
method: 'GET'
153143
context: undefined
154144
data: {
155145
readonly outputA: 'outputA'
@@ -250,7 +240,6 @@ test('createServerFn where validator is a primitive', () => {
250240
.inputValidator(() => 'c' as const)
251241
.handler((options) => {
252242
expectTypeOf(options).toEqualTypeOf<{
253-
method: 'GET'
254243
context: undefined
255244
data: 'c'
256245
signal: AbortSignal
@@ -263,7 +252,6 @@ test('createServerFn where validator is optional if object is optional', () => {
263252
.inputValidator((input: 'c' | undefined) => input)
264253
.handler((options) => {
265254
expectTypeOf(options).toEqualTypeOf<{
266-
method: 'GET'
267255
context: undefined
268256
data: 'c' | undefined
269257
signal: AbortSignal
@@ -285,7 +273,6 @@ test('createServerFn where validator is optional if object is optional', () => {
285273
test('createServerFn where data is optional if there is no validator', () => {
286274
const fn = createServerFn({ method: 'GET' }).handler((options) => {
287275
expectTypeOf(options).toEqualTypeOf<{
288-
method: 'GET'
289276
context: undefined
290277
data: undefined
291278
signal: AbortSignal
@@ -475,7 +462,6 @@ test('incrementally building createServerFn with multiple middleware calls', ()
475462

476463
builderWithMw1.handler((options) => {
477464
expectTypeOf(options).toEqualTypeOf<{
478-
method: 'GET'
479465
context: {
480466
readonly a: 'a'
481467
}
@@ -495,7 +481,6 @@ test('incrementally building createServerFn with multiple middleware calls', ()
495481

496482
builderWithMw2.handler((options) => {
497483
expectTypeOf(options).toEqualTypeOf<{
498-
method: 'POST'
499484
context: {
500485
readonly a: 'a'
501486
readonly b: 'b'
@@ -516,7 +501,6 @@ test('incrementally building createServerFn with multiple middleware calls', ()
516501

517502
builderWithMw3.handler((options) => {
518503
expectTypeOf(options).toEqualTypeOf<{
519-
method: 'GET'
520504
context: {
521505
readonly a: 'a'
522506
readonly b: 'b'
@@ -550,7 +534,6 @@ test('compose middlewares and server function factories', () => {
550534

551535
composedBuilder.handler((options) => {
552536
expectTypeOf(options).toEqualTypeOf<{
553-
method: 'GET'
554537
context: {
555538
readonly a: 'a'
556539
readonly b: 'b'

0 commit comments

Comments
 (0)