plugins/tanstack-start-expert/skills/start-middleware/SKILL.md
Use when composing cross-cutting server logic in TanStack Start with createMiddleware — request vs server function middleware, chaining via .middleware([...]), shared context with next({ context }), client↔server sendContext, client middleware (.client), global middleware via createStart in src/start.ts, middleware factories for authorization, and staticFunctionMiddleware. Do NOT use for: defining the RPC itself (use start-server-functions), raw HTTP endpoints (use start-server-routes), or route-level UX guards (use router beforeLoad).
npx skillsauth add fusengine/agents start-middlewareInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Middleware customizes the behavior of server functions and server routes. It is
composable — one middleware can depend on others to form an ordered chain.
Import createMiddleware from @tanstack/react-start. This skill targets
@tanstack/react-start v1.166.2.
Before implementing, verify current APIs against Context7
(/websites/tanstack_start_framework_react) + Exa, then explore the target
codebase. After changes, run fuse-ai-pilot:sniper.
.middleware() → .validator() →
.client() → .server(). Any other order is a type error.sendContext
is a well-formed identifier, not an authorized one. Always re-check access
against the session principal before using a client-sent value as a query
key, filter, or path param.next({ sendContext }). Anything the client sends, the client can lie about
— derive the session from a server-trusted source (cookie + DB), never from
sendContext..client() runs on the server during SSR. Guard browser-only APIs
(localStorage, window) with typeof window !== 'undefined'.staticFunctionMiddleware must be LAST in the chain (from
@tanstack/start-static-server-functions, experimental).| Feature | Request Middleware | Server Function Middleware |
|---------|--------------------|-----------------------------|
| Created with | createMiddleware() | createMiddleware({ type: 'function' }) |
| Scope | All server requests (SSR, routes, functions) | Server functions only |
| Methods | .server() | .client(), .server() |
| Input validation | No | Yes (.validator()) |
| Client-side logic | No | Yes |
Request middleware cannot depend on server function middleware; server function middleware can depend on both.
src/
├── start.ts # createStart → global request/function middleware
├── middleware/
│ ├── auth.ts # authMiddleware (request) + authorization factory
│ └── logging.ts # request logging
→ See auth-authorization.md
| Topic | Reference | Load when | |-------|-----------|-----------| | Types & chaining | types.md | Choosing request vs function middleware, attaching to fns/routes | | Context passing | context.md | Sharing data via next(), sendContext client↔server, headers/fetch | | Global & static | global.md | Global middleware, execution order, staticFunctionMiddleware |
| Template | When to Use | |----------|-------------| | auth-authorization.md | Auth + permission-based authorization factory | | client-middleware.md | Client-side headers, custom fetch, telemetry |
import { createMiddleware } from '@tanstack/react-start'
const logging = createMiddleware().server(async ({ next, request }) => {
console.log(request.url)
return next()
})
const auth = createMiddleware().server(async ({ next, request }) => {
const session = await getSession(request.headers)
if (!session) throw new Error('Unauthorized')
return next({ context: { session } }) // typed downstream
})
const fn = createServerFn().middleware([auth]).handler(async ({ context }) =>
db.orders.findMany({ where: { userId: context.session.userId } }),
)
→ See context.md for sendContext and header merging
sendContext shape as authorizationlocalStorage/window in .client() without an SSR guardstaticFunctionMiddleware anywhere but lasttesting
Copy self-audit and ban-lists — filler verbs/hype adjectives, slop placeholder names, fake-precise numbers, Title Case headlines, humor in error copy ('Oops!'), em-dash crutch, one copy register per page.
development
Logged-in web apps — dashboards, auth flows, settings, onboarding, data tables, command palettes, modals, toasts. Register `product`: density and glance-speed over marketing polish, no hero/CTA-tricks, every data surface covers empty/loading/error explicitly, tables and dataviz follow preattentive-processing rules.
development
Marketing sites, landing pages, campaign pages — register `brand` (design IS the product). Structure comes from the register's POV + a macrostructure pick, never from copying an inspiration site's section flow. Hero discipline, deviated section order, asymmetric grids, and a silhouette lookalike-test gate before ship.
development
Token-strategy core — OKLCH color rules, neutral tinting, accent-commitment levels, type scale, 8pt spacing grid, touch targets, and the canonical output format of design-system.md (the file the harness gates on). This is routing step 1 of design-method/SKILL.md — read it before design-web/design-webapp/design-ios/design-android, before picking or auditing a single color/type/spacing value.