AgentSkillsCN

mfing-bible-of-tanstack

涵盖 Start、Router 与 Query 的权威 TanStack 指南。支持以 { data: params } 为模式的服务器函数,实现路由与 loaderDeps 的无缝集成;通过统一的 queryOptions 进行 TanStack Query 缓存管理;提供 ServerOnly 身份验证机制、公开 API 路由与 Webhook 支持。无论是在创建路由、服务器函数、查询钩子、突变操作,还是在实现身份验证、构建 Webhook、调试数据获取流程、修复加载动画,或是开展任何 TanStack Start/Router/Query 相关的工作时,均可使用此指南。

SKILL.md
--- frontmatter
name: mfing-bible-of-tanstack
description: The definitive TanStack guide covering Start, Router, and Query. Server functions with { data: params } pattern, route integration with loaderDeps, TanStack Query cache management with unified queryOptions, ServerOnly authentication, public API routes, webhooks. Use when creating routes, server functions, query hooks, mutations, implementing authentication, building webhooks, debugging data fetching, fixing loading spinners, or any TanStack Start/Router/Query work.

The MF'ing Bible of TanStack

The definitive guide to TanStack Start, Router, and Query patterns. This skill provides indexed access to comprehensive documentation without consuming context when not needed.

Version Reference

PackageVersionNotes
@tanstack/react-start1.145.xfetch handler output (1.132+)
@tanstack/react-router1.144.x
@tanstack/react-query5.80.x
@tanstack/router-plugin1.145.x
hono4.xRecommended server runtime
@hono/node-server1.xNode.js adapter

Quick Reference (Essential Patterns)

The Three Critical Patterns

  1. Server Function Parameters: { data: params } destructuring
  2. Route Parameter Flow: loaderDepsloader → server function
  3. Unified QueryOptions: Single source of truth for cache keys

Server Function Template

typescript
import { createServerFn } from '@tanstack/react-start'
import { getServerAuth } from '@/lib/auth/serverAuth'
import { db, yourTable } from '@mysite/shared/db'

export const functionName = createServerFn({
  method: 'GET', // or 'POST'
})
  .inputValidator((params: YourParamsType) => params)
  .handler(async ({ data: params }) => {
    // 1. Auth context (if needed)
    const authContext = await getServerAuth(['admin', 'manager'])

    // 2. CRITICAL: Safe parameter handling
    const safeParams = params || {}

    // 3. Database operations
    const results = await db.select().from(yourTable)
      .where(/* conditions */)
      .limit(safeParams.limit || 50)
      .offset(safeParams.offset || 0)

    return results
  })

Route Template

typescript
import { createFileRoute } from '@tanstack/react-router'
import { serverFunction } from '@/lib/serverFunctions/yourFn'

export const Route = createFileRoute('/your/route/')(({
  validateSearch: (search) => ({
    param1: search?.param1 || undefined,
    param2: search?.param2 || undefined,
  }),

  // loaderDeps: ONLY for search parameters (query strings)
  loaderDeps: ({ search }) => ({
    param1: search?.param1 || undefined,
    param2: search?.param2 || undefined,
  }),

  loader: async ({ deps }) => {
    const data = await serverFunction({ data: deps })
    return { data }
  },

  component: YourComponent,
}))

Parameter Flow

code
URL: /route?search=test&status=active
    ↓
1. validateSearch → { search: 'test', status: 'active' }
    ↓
2. loaderDeps → { search: 'test', status: 'active' }
    ↓
3. loader → serverFunction({ data: deps })
    ↓
4. handler → ({ data: params }) where params = { search: 'test', status: 'active' }

Path Parameters vs Search Parameters

typescript
// PATH PARAMETERS: /users/$userId → automatically available
loader: async ({ params }) => {
  // params.userId is automatic - NO loaderDeps needed
  return getUserById({ data: { userId: params.userId } })
}

// SEARCH PARAMETERS: /users?search=test → requires loaderDeps
loaderDeps: ({ search }) => ({ search: search?.search }),
loader: async ({ deps }) => {
  return searchUsers({ data: deps })
}

When to Consult Reference Files

TaskReference File
Creating server functionsreferences/server-functions.md
Server function composition/cross-domainreferences/server-functions.md
Creating routes with loaderDepsreferences/routes-and-loaders.md
Route file naming conventionsreferences/routes-and-loaders.md
Adding authentication/RBACreferences/authentication.md
ServerOnly patternreferences/authentication.md
Building webhooks/public API routesreferences/api-routes-webhooks.md
Global middlewarereferences/api-routes-webhooks.md
TanStack Query integrationreferences/tanstack-query.md
Unified queryOptions patternreferences/tanstack-query.md
Cache invalidation/mutationsreferences/tanstack-query.md
Optimistic updatesreferences/tanstack-query.md
Fixing loading spinnersreferences/loading-states.md
SWR behavior configurationreferences/loading-states.md
Debugging hydration issuesreferences/debugging.md
CJS/ESM problemsreferences/debugging.md
Testing server functionsreferences/debugging.md
Production deploymentreferences/production-deployment.md
Docker deploymentreferences/production-deployment.md
Static file servingreferences/production-deployment.md
Hono server setupreferences/production-deployment.md
Code review for mistakesreferences/anti-patterns.md
Common errors to avoidreferences/anti-patterns.md

Critical Rules Summary

Server Functions

  • Always use { data: params } destructuring in handler
  • Always handle undefined: const safeParams = params || {}
  • Use shared database connection from @/lib/db
  • Use Fn suffix for server function files: prospectsFn.ts
  • See references/server-functions.md for composition patterns

Routes

  • loaderDeps is ONLY for search parameters (query strings)
  • Path parameters ($prospectId) are automatically available in params
  • Never use loaderDeps for path parameters
  • Use descriptive names not index.tsx: prospects-pipeline.tsx
  • See references/routes-and-loaders.md for complete patterns

Authentication

  • Use createServerOnlyFn - guarantees server-only execution
  • Never put auth logic directly in handlers
  • Never use middleware pattern for auth (tree-shaking issues)
  • See references/authentication.md for ServerOnly pattern

TanStack Query

  • Use singular cache keys matching DB names: ['prospect', filters]
  • Unified queryOptions shared between routes and hooks
  • Never duplicate query definitions
  • See references/tanstack-query.md for cache management

Public API Routes

  • Use createFileRoute with server.handlers for webhooks
  • Read raw body with request.text() for signature verification
  • See references/api-routes-webhooks.md for webhook patterns

File Structure Convention

code
apps/web/
├── src/
│   ├── lib/
│   │   ├── db.ts                    # Shared database connection
│   │   └── serverFunctions/
│   │       ├── prospectsFn.ts       # Server functions with Fn suffix
│   │       ├── authFn.ts
│   │       └── campaignFn.ts
│   ├── routes/
│   │   └── admin/prospects/
│   │       ├── prospects-pipeline.tsx    # Descriptive name (not index.tsx)
│   │       └── $prospectId/
│   │           └── prospect-details.tsx
│   └── hooks/
│       └── useProspects.ts
├── server.mjs                       # Hono production server wrapper
└── dist/                            # Build output (after vite build)
    ├── server/server.js             # TanStack fetch handler (NOT standalone!)
    └── client/                      # Static assets (JS, CSS, images)

Production Server (Quick Reference)

CRITICAL: TanStack Start outputs a fetch handler, NOT a runnable server. Wrap with Hono:

javascript
// server.mjs - Hono wrapper for TanStack Start
import { Hono } from 'hono'
import { serve } from '@hono/node-server'
import { serveStatic } from '@hono/node-server/serve-static'
import handler from './dist/server/server.js'

const app = new Hono()

// Static files FIRST (TanStack doesn't serve these!)
app.use('/assets/*', serveStatic({ root: './dist/client' }))
app.use('/images/*', serveStatic({ root: './dist/client' }))

// TanStack Start handles everything else
app.all('*', (c) => handler.fetch(c.req.raw))

serve({ fetch: app.fetch, port: 3001 })

See references/production-deployment.md for complete patterns.


Common Patterns

Cross-Domain Server Function Calls

typescript
// ✅ CORRECT: Use server functions for cross-domain queries
const contact = await getContactFullProfile({ data: { contactid: input.contactid } })
const product = await getProductDetails({ data: { productid: input.productid } })

// ❌ WRONG: Direct database queries across domains
const contact = await db.query.contact.findFirst(...)  // Don't do this from invoiceFn

Same-Domain Direct Queries

typescript
// ✅ CORRECT: Direct queries within same domain
export const updateSubscription = createServerFn({ method: 'PUT' })
  .handler(async ({ data: input }) => {
    // Same-domain lookup - OK to use direct query
    const existing = await db.query.subscription.findFirst({
      where: eq(subscription.subscriptionid, input.subscriptionid)
    })
    // ...
  })

Auth Patterns by Role

typescript
// No authentication required
const authContext = undefined  // Skip getServerAuth

// Any authenticated user
const authContext = await getServerAuthAnyRole()

// Specific roles
const authContext = await getServerAuth(['admin'])
const authContext = await getServerAuth(['admin', 'manager'])

Reference Files

Detailed Documentation

  • references/server-functions.md - Server function patterns, composition, cross-domain queries, database connection, input validation
  • references/routes-and-loaders.md - Route integration, loaderDeps, file naming, path vs search parameters
  • references/authentication.md - ServerOnly auth, RBAC middleware, createServerOnlyFn, auth patterns by role
  • references/api-routes-webhooks.md - Public API routes, webhooks, raw body handling, global middleware
  • references/tanstack-query.md - QueryOptions, cache keys, mutations, optimistic updates, prefetching
  • references/loading-states.md - SWR behavior, pending components, spinner fixes, shouldReload
  • references/debugging.md - Hydration issues, CJS/ESM problems, testing setup, Vite config
  • references/anti-patterns.md - Common mistakes, what NOT to do, parameter safety

Finding Specific Patterns

Use grep to search reference files:

bash
grep -i "optimistic" references/tanstack-query.md
grep -i "webhook" references/api-routes-webhooks.md
grep -i "hydration" references/debugging.md
grep -i "loaderDeps" references/routes-and-loaders.md

Key Insight: Architecture

Our application is a modern full-stack React solution using:

  • TanStack Start: Unified server/client routing with file-based routing
  • TanStack Query: All client-side data fetching with SWR caching
  • Server Functions: Type-safe RPC endpoints with middleware

This means:

  • Single application (no separate backend)
  • Automatic refetching on focus/reconnect
  • Request deduplication
  • SSR compatibility
  • Type safety end-to-end

Never learn these patterns again. This is the way.