tanstack-start-server-fn-testing
Unit-test TanStack Start createServerFn handlers via a global vi.mock that combines two patterns from Discussion #2701
Best use case
tanstack-start-server-fn-testing is best used when you need a repeatable AI agent workflow instead of a one-off prompt.
Unit-test TanStack Start createServerFn handlers via a global vi.mock that combines two patterns from Discussion #2701
Teams using tanstack-start-server-fn-testing should expect a more consistent output, faster repeated execution, less prompt rewriting.
When to use this skill
- You want a reusable workflow that can be run more than once with consistent structure.
When not to use this skill
- You only need a quick one-off answer and do not need a reusable workflow.
- You cannot install or maintain the underlying files, dependencies, or repository context.
Installation
Claude Code / Cursor / Codex
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/tanstack-start-server-fn-testing/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How tanstack-start-server-fn-testing Compares
| Feature / Agent | tanstack-start-server-fn-testing | Standard Approach |
|---|---|---|
| Platform Support | Not specified | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | Unknown | N/A |
Frequently Asked Questions
What does this skill do?
Unit-test TanStack Start createServerFn handlers via a global vi.mock that combines two patterns from Discussion #2701
Where can I find the source code?
You can find the source code on GitHub using the link provided at the top of the page.
SKILL.md Source
# TanStack Start Server Function Unit Testing
**Updated:** 2026-05-12
**Context:** Testing internal logic of `createServerFn`-defined server functions with Vitest, without spinning up the full TanStack Start server runtime.
## Problem
`createServerFn` is a server-only API. Importing it normally in tests fails because:
1. The `'use server'` pragma check throws `Invariant failed: createServerFn must be called with a function that is marked with the 'use server' pragma` at runtime.
2. The `tanstackStart()` Vite plugin rewrites the `handler(fn)` argument into a client RPC stub at build time, so even mocking `createServerFn` can't reach the original handler logic.
## Solution: combine two patterns from Discussion #2701
This project's solution lives in `src/test/server-fn-mock.ts` and is built from **two posts in [TanStack Router Discussion #2701](https://github.com/TanStack/router/discussions/2701)** combined into one approach.
### Source 1 — overall direction (the opening post)
- URL: <https://github.com/TanStack/router/discussions/2701#discussion-7425606>
- Author: `cameronb23` (Nov 2024)
- Take: "the best that I have come up with is mocking the call globally."
- Code snippet (old `createServerFn(method, fn)` signature):
```ts
vi.mock(import("@tanstack/start"), async (importOriginal) => {
const original = await importOriginal();
return {
...original,
createServerFn: (_ignoredMethod, fn) => fn,
};
});
```
- We adopt the **global-mock direction** from this post. We do **not** adopt the literal code because it targets a legacy `createServerFn` signature; the current API is a builder.
### Source 2 — builder-aware mock (Enhanced)
- URL: <https://github.com/TanStack/router/discussions/2701#discussioncomment-15184454>
- Take: hoist a builder object that satisfies the full `.middleware().inputValidator().handler()` chain.
- Code snippet:
```ts
const mockServerFunctionBuider = vi.hoisted(() => ({
middleware: vi.fn(() => mockServerFunctionBuider),
inputValidator: vi.fn(() => mockServerFunctionBuider),
handler: vi.fn((func) => func),
}));
vi.mock("@tanstack/react-start", async (importOriginal) => ({
...(await importOriginal()),
createServerFn: vi.fn(() => mockServerFunctionBuider),
}));
```
- We adopt the **builder structure** from this comment, because our server fns use `.inputValidator(...).handler(...)`.
### Our project-specific divergence
The Source 2 snippet makes `inputValidator: () => builder`, which **skips the validator entirely**. We instead **run the validator** so that existing tests for `v.ValiError` (invalid input cases) continue to work end-to-end.
Concretely:
```ts
inputValidator(validate) {
return {
handler(fn) {
return (opts) => fn({ data: validate(opts.data) })
},
}
}
```
## Required setup
Three pieces work together:
### 1. `src/test/server-fn-mock.ts`
Holds the `vi.mock("@tanstack/react-start", ...)` factory described above. The file's top-level comment cites both Discussion #2701 URLs.
### 2. `src/test/setup.ts`
Imports the mock as a side effect so it applies to every test:
```ts
import "~/test/server-fn-mock";
```
### 3. `vite.config.ts` — disable `tanstackStart()` plugin during Vitest
```ts
const isVitest = process.env.VITEST === "true";
export default defineConfig({
plugins: [
tailwindcss(),
...(isVitest ? [] : [tanstackStart()]),
react(),
babel({ presets: [reactCompilerPreset()] }),
],
});
```
Without this, the plugin replaces `handler(fn)` at build time with a client RPC stub, and the mock can never reach the original `fn`.
## Writing tests
After the setup above, server fn tests are plain imports + calls — no `import?raw`, no `new Function`, no regex source rewriting.
### GET (no input validation)
```ts
import { describe, expect, it } from "vite-plus/test";
import { fetchUsersServer } from "~/features/users/api/users-server";
describe("fetchUsersServer", () => {
it("returns all users", async () => {
const result = await fetchUsersServer();
expect(result).toHaveLength(N);
});
});
```
### POST with `inputValidator` — happy path + validation error
```ts
import * as v from "valibot";
import { describe, expect, it } from "vite-plus/test";
import { createUserServer } from "~/features/users/api/create-user-server";
describe("createUserServer", () => {
it("creates a user", async () => {
await expect(
createUserServer({ data: { email: "a@b.c", name: "Alice", role: "admin" } }),
).resolves.toMatchObject({ email: "a@b.c" });
});
it("throws ValiError on invalid input", async () => {
await expect(
createUserServer({ data: { email: "", name: "", role: "admin" } }),
).rejects.toBeInstanceOf(v.ValiError);
});
});
```
## What this approach does NOT cover
- Global / route middleware execution (Discussion #2701 also proposes an "integration harness" using `requestHandler` + `runWithStartContext` + the `#tanstack-start-server-fn-resolver` virtual module — see the opening post). We don't use it because this project has no global middleware and the integration harness is ~80 lines of glue tied to internal APIs.
- Response / Headers / Status assertions: the mock returns the handler's raw value, not a `Response`. Test the handler payload directly.
- Nested server-fn calls (one server fn calling another): not supported by either approach in Discussion #2701.
If middleware behavior or `Response` shape ever needs to be tested, revisit the integration harness option from the opening post.
## Examples in this project
All 17 server fns under `src/features/**/api/*-server.ts` (plus `src/features/auth/server/auth-server.ts`) have matching `*.test.ts` files using this pattern. Reference implementations:
- GET: `src/features/users/api/users-server.test.ts`
- POST + inputValidator: `src/features/users/api/create-user-server.test.ts`
## References
- Discussion thread: <https://github.com/TanStack/router/discussions/2701>
- Pattern 1 (global mock direction): <https://github.com/TanStack/router/discussions/2701#discussion-7425606>
- Pattern 2 (builder structure): <https://github.com/TanStack/router/discussions/2701#discussioncomment-15184454>Related Skills
webapp-testing
Toolkit for interacting with and testing local web applications using Playwright. Supports verifying frontend functionality, debugging UI behavior, capturing browser screenshots, and viewing browser logs.
tanstack-start
Full-stack React framework powered by TanStack Router with SSR, streaming, server functions, and deployment to any hosting provider.
test
Advanced test implementation command with unit/E2E support, auto-execution, and smart fixing capabilities
serena
Token-efficient Serena MCP command for structured app development and problem-solving
project-guidelines-example
Example project-specific skill template based on a real production application.
notion-bug-pr
Skill that pulls bug tickets (titles containing 「不具合」) from a Notion database, investigates root cause in a GitHub repo, applies fixes, and opens draft PRs. Supports three modes — daily recurring schedule, one-shot at a specific time, or immediate on-demand run. Takes three or four args: Notion database URL, repo path or name, and either HH:MM (daily), "once HH:MM" (one-shot), or "now" (immediate).
graphify
any input (code, docs, papers, images, videos) to knowledge graph. Use when user asks any question about a codebase, documents, or project content - especially if graphify-out/ exists, treat the question as a /graphify query.
chrome
Comprehensive Chrome DevTools development system with native Chrome capabilities for debugging, E2E testing, performance analysis, and browser automation
web-design-guidelines
Review UI code for Web Interface Guidelines compliance. Use when asked to "review my UI", "check accessibility", "audit design", "review UX", or "check my site against best practices".
typescript-advanced-types
Master TypeScript's advanced type system including generics, conditional types, mapped types, template literals, and utility types for building type-safe applications. Use when implementing complex type logic, creating reusable type utilities, or ensuring compile-time type safety in TypeScript projects.
tailwind-css-patterns
Provides comprehensive Tailwind CSS utility-first styling patterns including responsive design, layout utilities, flexbox, grid, spacing, typography, colors, and modern CSS best practices. Use when styling React/Vue/Svelte components, building responsive layouts, implementing design systems, or optimizing CSS workflow.
skill-creator
Create new skills, modify and improve existing skills, and measure skill performance. Use when users want to create a skill from scratch, edit, or optimize an existing skill, run evals to test a skill, benchmark skill performance with variance analysis, or optimize a skill's description for better triggering accuracy.