DEV Community

Cover image for Understanding Server Functions: TanStack Start vs Next.js
Abdul Halim
Abdul Halim

Posted on

Understanding Server Functions: TanStack Start vs Next.js

As a senior frontend engineer, I've extensively worked on Next.js and recently started studying TanStack Start. Today, I want to share insights about server functions and how these two frameworks approach them differently.

What Are Server Functions?

Server functions are functions that run exclusively on the server but can be called from client-side code. They bridge the gap between your frontend and backend, allowing you to write server-side logic alongside your React components without setting up separate API endpoints.

TanStack Start's Approach

TanStack Start takes a unique approach to server functions that emphasizes type safety and simplicity.

Creating a Server Function

In TanStack Start, you create a server function using the createServerFn function:

import { createServerFn } from '@tanstack/start'

export const getUser = createServerFn('GET', async (userId: string) => {
  // This code runs only on the server
  const user = await db.users.findOne({ id: userId })
  return user
})
Enter fullscreen mode Exit fullscreen mode

The first argument specifies the HTTP method, and the second is your server-side logic. Simple and straightforward.

Key Features

Type Safety First: TanStack Start provides end-to-end type safety. The types from your server function automatically flow to the client, giving you autocomplete and type checking without extra configuration.

Explicit HTTP Methods: You explicitly define which HTTP method your function uses. This makes the intent clear and helps with caching and optimization.

// GET request - typically for data fetching
const getData = createServerFn('GET', async () => {...})

// POST request - for mutations
const createData = createServerFn('POST', async (data) => {...})
Enter fullscreen mode Exit fullscreen mode

Direct Integration with TanStack Router: Server functions work seamlessly with TanStack Router's data loading system:

export const Route = createFileRoute('/users/$userId')({
  loader: async ({ params }) => {
    const user = await getUser(params.userId)
    return user
  },
})
Enter fullscreen mode Exit fullscreen mode

Next.js Approach

Next.js uses Server Actions, which have a different philosophy.

Creating a Server Action

In Next.js, you use the 'use server' directive:

'use server'

export async function getUser(userId: string) {
  const user = await db.users.findOne({ id: userId })
  return user
}
Enter fullscreen mode Exit fullscreen mode

You can also define inline server actions within components:

export default function MyComponent() {
  async function handleSubmit(formData: FormData) {
    'use server'
    // Server-side logic here
  }

  return <form action={handleSubmit}>...</form>
}
Enter fullscreen mode Exit fullscreen mode

Key Characteristics

Form-Centric: Server Actions are designed primarily around form submissions and progressive enhancement.

Implicit POST Methods: By default, Server Actions use POST requests. This is great for mutations but can be less intuitive for data fetching.

Built into React: Server Actions are a React feature that Next.js leverages, meaning they're deeply integrated with React's concurrent features.

Key Differences

1. Explicitness vs Convention

TanStack Start is explicit about HTTP methods and function creation. You use createServerFn and specify the method upfront.

Next.js relies more on conventions. The 'use server' directive turns any function into a server action, which is convenient but less explicit.

2. Type Safety

TanStack Start provides stronger type safety out of the box. The connection between client and server code is more tightly coupled, giving you better TypeScript support.

Next.js relies on you to maintain type consistency between client and server, which can lead to more runtime errors if you are not careful.

3. HTTP Method Control

TanStack Start gives you fine-grained control over HTTP methods (GET, POST, PUT, DELETE, etc.).

Next.js Server Actions primarily use POST, though you can work around this with additional setup.

4. Integration with Data Loading

TanStack Start's server functions are designed to work seamlessly with its router's loader pattern, making data fetching predictable and type-safe.

Next.js Server Actions are more focused on mutations and form handling, with data fetching typically done through other mechanisms like Server Components.

5. Error Handling

Both frameworks handle errors differently:

// TanStack Start
const myFunction = createServerFn('POST', async (data) => {
  try {
    return await processData(data)
  } catch (error) {
    throw new Error('Processing failed')
  }
})

// Next.js
'use server'
async function myAction(data: FormData) {
  try {
    return await processData(data)
  } catch (error) {
    return { error: 'Processing failed' }
  }
}
Enter fullscreen mode Exit fullscreen mode

When to Use Which?

Choose TanStack Start if you:

  • Want explicit, type-safe server functions
  • Need fine control over HTTP methods
  • Prefer a more traditional API-like approach
  • Value strong TypeScript integration

Choose Next.js if you:

  • Are heavily focused on form handling and mutations
  • Want progressive enhancement out of the box
  • Prefer React-centric solutions
  • Need the broader Next.js ecosystem

Conclusion

Both TanStack Start and Next.js offer powerful server function capabilities, but with different philosophies. TanStack Start provides more explicit control and stronger type safety, making it excellent for developers who want clarity and predictability. Next.js offers convenience and deep React integration, perfect for form-heavy applications.

Understanding these differences helps you choose the right tool for your project and use it effectively. Neither approach is inherently better; they solve similar problems with different trade-offs.

As frontend engineers, our job is to understand these nuances and make informed decisions based on our project's specific needs.

Top comments (0)