Skip to content

Commit

Permalink
fix(start): returning null from server functions (#3048)
Browse files Browse the repository at this point in the history
Co-authored-by: SeanCassiere <[email protected]>
  • Loading branch information
alakhpc and SeanCassiere authored Dec 22, 2024
1 parent 3505a1f commit 4a7d3ca
Show file tree
Hide file tree
Showing 6 changed files with 106 additions and 1 deletion.
63 changes: 63 additions & 0 deletions e2e/start/basic/app/routes/-server-fns/allow-fn-return-null.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/**
* This exported component checks whether the server function can
* return null without throwing an error or returning something else.
* @link https://github.com/TanStack/router/issues/2776
*/

import * as React from 'react'
import { createServerFn } from '@tanstack/start'

const $allow_return_null_getFn = createServerFn().handler(async () => {
return null
})
const $allow_return_null_postFn = createServerFn({ method: 'POST' }).handler(
async () => {
return null
},
)

export function AllowServerFnReturnNull() {
const [getServerResult, setGetServerResult] = React.useState<any>('-')
const [postServerResult, setPostServerResult] = React.useState<any>('-')

return (
<div className="p-2 border m-2 grid gap-2">
<h3>Allow ServerFn to return `null`</h3>
<p>
This component checks whether the server function can return null
without throwing an error.
</p>
<div>
It should return{' '}
<code>
<pre>{JSON.stringify(null)}</pre>
</code>
</div>
<p>
{`GET: $allow_return_null_getFn returns`}
<br />
<span data-testid="allow_return_null_getFn-response">
{JSON.stringify(getServerResult)}
</span>
</p>
<p>
{`POST: $allow_return_null_postFn returns`}
<br />
<span data-testid="allow_return_null_postFn-response">
{JSON.stringify(postServerResult)}
</span>
</p>
<button
data-testid="test-allow-server-fn-return-null-btn"
type="button"
className="rounded-md bg-white px-2.5 py-1.5 text-sm font-semibold text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50"
onClick={() => {
$allow_return_null_getFn().then(setGetServerResult)
$allow_return_null_postFn().then(setPostServerResult)
}}
>
Test Allow Server Fn Return Null
</button>
</div>
)
}
2 changes: 2 additions & 0 deletions e2e/start/basic/app/routes/server-fns.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { createFileRoute } from '@tanstack/react-router'

import { ConsistentServerFnCalls } from './-server-fns/consistent-fn-calls'
import { MultipartServerFnCall } from './-server-fns/multipart-formdata-fn-call'
import { AllowServerFnReturnNull } from './-server-fns/allow-fn-return-null'

export const Route = createFileRoute('/server-fns')({
component: RouteComponent,
Expand All @@ -13,6 +14,7 @@ function RouteComponent() {
<>
<ConsistentServerFnCalls />
<MultipartServerFnCall />
<AllowServerFnReturnNull />
</>
)
}
4 changes: 4 additions & 0 deletions e2e/start/basic/app/styles/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
@tailwind utilities;

@layer base {
html {
color-scheme: light dark;
}

html,
body {
@apply text-gray-900 bg-gray-50 dark:bg-gray-950 dark:text-gray-200;
Expand Down
20 changes: 20 additions & 0 deletions e2e/start/basic/tests/base.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,3 +192,23 @@ test('env-only functions can only be called on the server or client respectively
'client got: hello',
)
})

test.only('Server function can return null for GET and POST calls', async ({
page,
}) => {
await page.goto('/server-fns')

await page.waitForLoadState('networkidle')
await page.getByTestId('test-allow-server-fn-return-null-btn').click()
await page.waitForLoadState('networkidle')

// GET call
await expect(
page.getByTestId('allow_return_null_getFn-response'),
).toContainText(JSON.stringify(null))

// POST call
await expect(
page.getByTestId('allow_return_null_postFn-response'),
).toContainText(JSON.stringify(null))
})
5 changes: 4 additions & 1 deletion packages/start/src/client/createServerFn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,10 @@ const applyMiddleware = (
context,
sendContext,
headers,
result: userResult?.result ?? (mCtx as any).result,
result:
userResult?.result !== undefined
? userResult.result
: (mCtx as any).result,
} as MiddlewareResult & {
method: Method
})
Expand Down
13 changes: 13 additions & 0 deletions packages/start/src/server-handler/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import {
defaultTransformer,
isNotFound,
isPlainObject,
isRedirect,
} from '@tanstack/react-router'
import invariant from 'tiny-invariant'
Expand Down Expand Up @@ -82,6 +83,12 @@ export async function handleServerRequest(request: Request, _event?: H3Event) {

if (result instanceof Response) {
return result
} else if (
isPlainObject(result) &&
'result' in result &&
result.result instanceof Response
) {
return result.result
}

// TODO: RSCs
Expand Down Expand Up @@ -119,6 +126,12 @@ export async function handleServerRequest(request: Request, _event?: H3Event) {
} catch (error: any) {
if (error instanceof Response) {
return error
} else if (
isPlainObject(error) &&
'result' in error &&
error.result instanceof Response
) {
return error.result
}

// Currently this server-side context has no idea how to
Expand Down

0 comments on commit 4a7d3ca

Please sign in to comment.