Skip to main content
Server functions are the backbone of RefactKit’s backend. Created with createServerFn from @tanstack/react-start, they execute exclusively on the Nitro v3 server — never in the browser bundle — and are called directly from components or route loaders as if they were ordinary async functions. Every server function in the boilerplate follows the same four-step security pattern: validate, authenticate, authorize, execute.

The four-step security pattern

1

Validate input with Zod

Parse the incoming data argument through a Zod schema before touching anything else. This catches type mismatches and missing fields at the boundary, before any database calls happen.
z.parse() throws a ZodError if validation fails. TanStack Start surfaces this as a rejected promise that you can catch on the client.
2

Authenticate — read the session from cookies

Call auth.api.getSession() with the current request headers. The session is stored in an encrypted JWE cookie — no database hit when the cache is warm.
3

Authorize — verify org membership and role

Query the member table to confirm the authenticated user actually belongs to the target organization. Optionally enforce a minimum role.
4

Execute business logic

With input validated and identity confirmed, run your database queries, storage operations, or external API calls. Return a plain serializable value — TanStack Start serializes it for the client automatically.

GET and POST examples

Use method: 'GET' for read operations and method: 'POST' for writes, deletions, and anything with side effects. This matches HTTP semantics and allows TanStack Query to cache GET results correctly.

Calling server functions from a component

Server functions are called with a { data: ... } argument. On the client, they behave like any other async function:
When used inside a TanStack Query mutation:

Server function files in src/server/

Each file groups functions by domain. Keep new functions in the appropriate existing file, or create a new file for a distinct domain.

Updating the query key registry

After writing a new server function, add a corresponding queryOptions entry in src/server/query-keys.ts. This keeps cache keys consistent between SSR loaders and client-side useQuery calls:
Never import a server function inside a file that is also imported by client-only modules (e.g., component files that don’t use createFileRoute). TanStack Start tree-shakes server functions from the client bundle based on the use server boundary — importing them in shared modules can break this boundary and expose server code to the browser.