Skip to content

Commit

Permalink
Merge branch 'main' of https://github.com/TanStack/router
Browse files Browse the repository at this point in the history
  • Loading branch information
tannerlinsley committed Nov 15, 2024
2 parents 4a1faef + 94347ae commit 01b5c84
Show file tree
Hide file tree
Showing 61 changed files with 311 additions and 129 deletions.
2 changes: 1 addition & 1 deletion docs/framework/react/start/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,7 @@ function Home() {
<button
type="button"
onClick={() => {
updateCount(1).then(() => {
updateCount({ data: 1 }).then(() => {
router.invalidate()
})
}}
Expand Down
19 changes: 10 additions & 9 deletions docs/framework/react/start/server-functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,11 @@ import { createServerFn } from '@tanstack/start'

export const greet = createServerFn({
method: 'GET',
}).handler(async (ctx) => {
return `Hello, ${ctx.data}!`
})
.validator((data: string) => data)
.handler(async (ctx) => {
return `Hello, ${ctx.data}!`
})

greet({
data: 'John',
Expand Down Expand Up @@ -117,7 +119,7 @@ export const greet = createServerFn({ method: 'GET' })
throw new Error('Person must be an object')
}

if (typeof person.name !== 'string') {
if ('name' in person && typeof person.name !== 'string') {
throw new Error('Person.name must be a string')
}

Expand All @@ -142,7 +144,6 @@ const Person = z.object({
})

export const greet = createServerFn({ method: 'GET' })
.validator(zodValidator)
.validator((person: unknown) => {
return Person.parse(person)
})
Expand Down Expand Up @@ -174,7 +175,7 @@ export const greet = createServerFn({ method: 'GET' })
throw new Error('Person must be an object')
}

if (typeof person.name !== 'string') {
if ('name' in person && typeof person.name !== 'string') {
throw new Error('Person.name must be a string')
}

Expand Down Expand Up @@ -257,11 +258,11 @@ type Person = {
age: number
}

export const greet = createServerFn({ method: 'GET' }).handler(
async ({ data }) => {
export const greet = createServerFn({ method: 'GET' })
.validator((data) => data)
.handler(async ({ data }) => {
return `Hello, ${data.name}! You are ${data.age} years old.`
},
)
})

greet({
data: {
Expand Down
26 changes: 26 additions & 0 deletions e2e/start/basic/app/routeTree.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import { Route as rootRoute } from './routes/__root'
import { Route as UsersImport } from './routes/users'
import { Route as StatusImport } from './routes/status'
import { Route as ServerFnsImport } from './routes/server-fns'
import { Route as SearchParamsImport } from './routes/search-params'
import { Route as RedirectImport } from './routes/redirect'
import { Route as PostsImport } from './routes/posts'
Expand Down Expand Up @@ -42,6 +43,12 @@ const StatusRoute = StatusImport.update({
getParentRoute: () => rootRoute,
} as any)

const ServerFnsRoute = ServerFnsImport.update({
id: '/server-fns',
path: '/server-fns',
getParentRoute: () => rootRoute,
} as any)

const SearchParamsRoute = SearchParamsImport.update({
id: '/search-params',
path: '/search-params',
Expand Down Expand Up @@ -170,6 +177,13 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof SearchParamsImport
parentRoute: typeof rootRoute
}
'/server-fns': {
id: '/server-fns'
path: '/server-fns'
fullPath: '/server-fns'
preLoaderRoute: typeof ServerFnsImport
parentRoute: typeof rootRoute
}
'/status': {
id: '/status'
path: '/status'
Expand Down Expand Up @@ -301,6 +315,7 @@ export interface FileRoutesByFullPath {
'/posts': typeof PostsRouteWithChildren
'/redirect': typeof RedirectRoute
'/search-params': typeof SearchParamsRoute
'/server-fns': typeof ServerFnsRoute
'/status': typeof StatusRoute
'/users': typeof UsersRouteWithChildren
'/posts/$postId': typeof PostsPostIdRoute
Expand All @@ -318,6 +333,7 @@ export interface FileRoutesByTo {
'/deferred': typeof DeferredRoute
'/redirect': typeof RedirectRoute
'/search-params': typeof SearchParamsRoute
'/server-fns': typeof ServerFnsRoute
'/status': typeof StatusRoute
'/posts/$postId': typeof PostsPostIdRoute
'/users/$userId': typeof UsersUserIdRoute
Expand All @@ -336,6 +352,7 @@ export interface FileRoutesById {
'/posts': typeof PostsRouteWithChildren
'/redirect': typeof RedirectRoute
'/search-params': typeof SearchParamsRoute
'/server-fns': typeof ServerFnsRoute
'/status': typeof StatusRoute
'/users': typeof UsersRouteWithChildren
'/_layout/_layout-2': typeof LayoutLayout2RouteWithChildren
Expand All @@ -357,6 +374,7 @@ export interface FileRouteTypes {
| '/posts'
| '/redirect'
| '/search-params'
| '/server-fns'
| '/status'
| '/users'
| '/posts/$postId'
Expand All @@ -373,6 +391,7 @@ export interface FileRouteTypes {
| '/deferred'
| '/redirect'
| '/search-params'
| '/server-fns'
| '/status'
| '/posts/$postId'
| '/users/$userId'
Expand All @@ -389,6 +408,7 @@ export interface FileRouteTypes {
| '/posts'
| '/redirect'
| '/search-params'
| '/server-fns'
| '/status'
| '/users'
| '/_layout/_layout-2'
Expand All @@ -409,6 +429,7 @@ export interface RootRouteChildren {
PostsRoute: typeof PostsRouteWithChildren
RedirectRoute: typeof RedirectRoute
SearchParamsRoute: typeof SearchParamsRoute
ServerFnsRoute: typeof ServerFnsRoute
StatusRoute: typeof StatusRoute
UsersRoute: typeof UsersRouteWithChildren
PostsPostIdDeepRoute: typeof PostsPostIdDeepRoute
Expand All @@ -421,6 +442,7 @@ const rootRouteChildren: RootRouteChildren = {
PostsRoute: PostsRouteWithChildren,
RedirectRoute: RedirectRoute,
SearchParamsRoute: SearchParamsRoute,
ServerFnsRoute: ServerFnsRoute,
StatusRoute: StatusRoute,
UsersRoute: UsersRouteWithChildren,
PostsPostIdDeepRoute: PostsPostIdDeepRoute,
Expand All @@ -442,6 +464,7 @@ export const routeTree = rootRoute
"/posts",
"/redirect",
"/search-params",
"/server-fns",
"/status",
"/users",
"/posts_/$postId/deep"
Expand Down Expand Up @@ -472,6 +495,9 @@ export const routeTree = rootRoute
"/search-params": {
"filePath": "search-params.tsx"
},
"/server-fns": {
"filePath": "server-fns.tsx"
},
"/status": {
"filePath": "status.tsx"
},
Expand Down
124 changes: 124 additions & 0 deletions e2e/start/basic/app/routes/server-fns.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import * as React from 'react'
import { createFileRoute } from '@tanstack/react-router'
import { createServerFn } from '@tanstack/start'

export const Route = createFileRoute('/server-fns')({
component: RouteComponent,
})

function RouteComponent() {
return (
<>
<ConsistentServerFnCalls />
</>
)
}

// START CONSISTENT_SERVER_FN_CALLS
const cons_getFn1 = createServerFn()
.validator((d: { username: string }) => d)
.handler(async ({ data }) => {
return { payload: data }
})

const cons_serverGetFn1 = createServerFn()
.validator((d: { username: string }) => d)
.handler(({ data }) => {
return cons_getFn1({ data })
})

const cons_postFn1 = createServerFn({ method: 'POST' })
.validator((d: { username: string }) => d)
.handler(async ({ data }) => {
return { payload: data }
})

const cons_serverPostFn1 = createServerFn({ method: 'POST' })
.validator((d: { username: string }) => d)
.handler(({ data }) => {
return cons_postFn1({ data })
})

/**
* This component checks whether the returned payloads from server function
* are the same, regardless of whether the server function is called directly
* from the client or from within the server function.
* @link https://github.com/TanStack/router/issues/1866
* @link https://github.com/TanStack/router/issues/2481
*/
function ConsistentServerFnCalls() {
const [getServerResult, setGetServerResult] = React.useState({})
const [getDirectResult, setGetDirectResult] = React.useState({})

const [postServerResult, setPostServerResult] = React.useState({})
const [postDirectResult, setPostDirectResult] = React.useState({})

return (
<div className="p-2 border m-2 grid gap-2">
<h3>Consistent Server Fn GET Calls</h3>
<p>
This component checks whether the returned payloads from server function
are the same, regardless of whether the server function is called
directly from the client or from within the server function.
</p>
<div>
It should return{' '}
<code>
<pre data-testid="expected-consistent-server-fns-result">
{JSON.stringify({ payload: { username: 'TEST' } })}
</pre>
</code>
</div>
<p>
{`GET: cons_getFn1 called from server cons_serverGetFn1 returns`}
<br />
<span data-testid="cons_serverGetFn1-response">
{JSON.stringify(getServerResult)}
</span>
</p>
<p>
{`GET: cons_getFn1 called directly returns`}
<br />
<span data-testid="cons_getFn1-response">
{JSON.stringify(getDirectResult)}
</span>
</p>
<p>
{`POST: cons_postFn1 called from cons_serverPostFn1 returns`}
<br />
<span data-testid="cons_serverPostFn1-response">
{JSON.stringify(postServerResult)}
</span>
</p>
<p>
{`POST: cons_postFn1 called directly returns`}
<br />
<span data-testid="cons_postFn1-response">
{JSON.stringify(postDirectResult)}
</span>
</p>
<button
data-testid="test-consistent-server-fn-calls-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={() => {
// GET calls
cons_serverGetFn1({ data: { username: 'TEST' } }).then(
setGetServerResult,
)
cons_getFn1({ data: { username: 'TEST' } }).then(setGetDirectResult)

// POST calls
cons_serverPostFn1({ data: { username: 'TEST' } }).then(
setPostServerResult,
)
cons_postFn1({ data: { username: 'TEST' } }).then(setPostDirectResult)
}}
>
Test Consistent server function responses
</button>
</div>
)
}

// END CONSISTENT_SERVER_FN_CALLS
30 changes: 30 additions & 0 deletions e2e/start/basic/tests/app.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,3 +101,33 @@ test('invoking a server function with custom response status code', async ({
})
await requestPromise
})

test('Consistent server function returns both on client and server for GET and POST calls', async ({
page,
}) => {
await page.goto('/server-fns')

await page.waitForLoadState('networkidle')
const expected =
(await page
.getByTestId('expected-consistent-server-fns-result')
.textContent()) || ''
expect(expected).not.toBe('')

await page.getByTestId('test-consistent-server-fn-calls-btn').click()
await page.waitForLoadState('networkidle')

// GET calls
await expect(page.getByTestId('cons_serverGetFn1-response')).toContainText(
expected,
)
await expect(page.getByTestId('cons_getFn1-response')).toContainText(expected)

// POST calls
await expect(page.getByTestId('cons_serverPostFn1-response')).toContainText(
expected,
)
await expect(page.getByTestId('cons_postFn1-response')).toContainText(
expected,
)
})
4 changes: 2 additions & 2 deletions examples/react/authenticated-routes/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
"start": "vite"
},
"dependencies": {
"@tanstack/react-router": "^1.81.9",
"@tanstack/router-devtools": "^1.81.9",
"@tanstack/react-router": "^1.81.10",
"@tanstack/router-devtools": "^1.81.10",
"@tanstack/router-plugin": "^1.81.9",
"react": "^18.2.0",
"react-dom": "^18.2.0",
Expand Down
4 changes: 2 additions & 2 deletions examples/react/basic-default-search-params/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
},
"dependencies": {
"@tanstack/react-query": "^5.59.20",
"@tanstack/react-router": "^1.81.9",
"@tanstack/router-devtools": "^1.81.9",
"@tanstack/react-router": "^1.81.10",
"@tanstack/router-devtools": "^1.81.10",
"redaxios": "^0.5.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
Expand Down
4 changes: 2 additions & 2 deletions examples/react/basic-file-based-codesplitting/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
"start": "vite"
},
"dependencies": {
"@tanstack/react-router": "^1.81.9",
"@tanstack/router-devtools": "^1.81.9",
"@tanstack/react-router": "^1.81.10",
"@tanstack/router-devtools": "^1.81.10",
"@tanstack/router-plugin": "^1.81.9",
"react": "^18.2.0",
"react-dom": "^18.2.0",
Expand Down
4 changes: 2 additions & 2 deletions examples/react/basic-file-based/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
"start": "vite"
},
"dependencies": {
"@tanstack/react-router": "^1.81.9",
"@tanstack/router-devtools": "^1.81.9",
"@tanstack/react-router": "^1.81.10",
"@tanstack/router-devtools": "^1.81.10",
"@tanstack/router-plugin": "^1.81.9",
"react": "^18.2.0",
"react-dom": "^18.2.0",
Expand Down
4 changes: 2 additions & 2 deletions examples/react/basic-react-query-file-based/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
"dependencies": {
"@tanstack/react-query": "^5.59.20",
"@tanstack/react-query-devtools": "^5.59.20",
"@tanstack/react-router": "^1.81.9",
"@tanstack/router-devtools": "^1.81.9",
"@tanstack/react-router": "^1.81.10",
"@tanstack/router-devtools": "^1.81.10",
"@tanstack/router-plugin": "^1.81.9",
"react": "^18.2.0",
"react-dom": "^18.2.0",
Expand Down
Loading

0 comments on commit 01b5c84

Please sign in to comment.