.agents/skills/tanstack-start/SKILL.md
Full-stack SolidJS framework powered by TanStack Router with SSR, streaming, server functions, and deployment to any hosting provider.
npx skillsauth add em-jones/staccato-toolkit tanstack-startInstall 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.
TanStack Start is a full-stack SolidJS framework built on TanStack Router, powered by Vite and Nitro (via Vinxi). It provides server-side rendering, streaming, server functions (RPC), middleware, API routes, and deploys to any platform via Nitro presets.
Package: @tanstack/solid-start
Router Plugin: @tanstack/router-plugin
Build Tool: Vinxi (Vite + Nitro)
Status: RC (Release Candidate)
RSC Support: Solid Server Components support is in active development and will land as a non-breaking v1.x addition
npx @tanstack/cli create my-app
# Or manually:
npm install @tanstack/solid-start @tanstack/solid-router solid-js
npm install -D @tanstack/router-plugin typescript vite vite-tsconfig-paths
my-app/
app/
routes/
__root.tsx # Root layout
index.tsx # / route
posts.$postId.tsx # /posts/:postId
api/
users.ts # /api/users API route
client.tsx # Client entry
router.tsx # Router creation
ssr.tsx # SSR entry
routeTree.gen.ts # Auto-generated route tree
app.config.ts # TanStack Start config
db/
schema.ts # Drizzle schema
index.ts # DB client
drizzle.config.ts # Drizzle config
tsconfig.json
package.json
app.config.ts)import { defineConfig } from '@tanstack/solid-start/config'
import viteTsConfigPaths from 'vite-tsconfig-paths'
export default defineConfig({
vite: {
plugins: [
viteTsConfigPaths({ projects: ['./tsconfig.json'] }),
],
},
server: {
preset: 'node-server', // 'vercel' | 'netlify' | 'cloudflare-pages' | etc.
},
tsr: {
appDirectory: './app',
routesDirectory: './app/routes',
generatedRouteTree: './app/routeTree.gen.ts',
},
})
npm install drizzle-kit drizzle-orm
npm install better-sqlite3 # or postgres, mysql2, etc.
// db/schema.ts
import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core'
import { sql } from 'drizzle-orm'
export const users = sqliteTable('users', {
id: integer('id').primaryKey({ autoIncrement: true }),
name: text('name').notNull(),
email: text('email').notNull().unique(),
createdAt: integer('created_at', { mode: 'timestamp' })
.default(sql`(strftime('%s', 'now'))`),
})
// db/index.ts
import { drizzle } from 'drizzle-orm/better-sqlite3'
import Database from 'better-sqlite3'
import * as schema from './schema'
const sqlite = new Database('sqlite.db')
export const db = drizzle(sqlite, { schema })
createServerFn)Server functions provide type-safe RPC calls between client and server.
import { createServerFn } from '@tanstack/solid-start'
import { db } from '../db'
import { users } from '../db/schema'
// GET (data fetching, cacheable)
const getUsers = createServerFn()
.handler(async () => {
const allUsers = await db.select().from(users).all()
return allUsers
})
// POST (mutations, side effects)
const createUser = createServerFn({ method: 'POST' })
.validator((data: { name: string; email: string }) => data)
.handler(async ({ data }) => {
const [user] = await db.insert(users).values(data).returning()
return user
})
import { z } from 'zod'
const updateUser = createServerFn({ method: 'POST' })
.validator(
z.object({
id: z.number(),
name: z.string().min(1),
email: z.string().email(),
})
)
.handler(async ({ data }) => {
// data is fully typed: { id: number; name: string; email: string }
const [updated] = await db.update(users)
.set(data)
.where(sql`${users.id} = ${data.id}`)
.returning()
return updated
})
import { createMiddleware } from '@tanstack/solid-start'
const loggingMiddleware = createMiddleware().handler(async ({ next }) => {
console.log('Request started')
const result = await next()
console.log('Request completed')
return result
})
import { getWebRequest } from 'solid-js/web'
const authMiddleware = createMiddleware().handler(async ({ next }) => {
const request = getWebRequest()
const session = await getSession(request)
if (!session?.user) {
throw redirect({ to: '/login' })
}
// Pass typed context to handler
return next({ context: { user: session.user } })
})
const adminMiddleware = createMiddleware()
.middleware([authMiddleware])
.handler(async ({ next, context }) => {
// context.user is typed from authMiddleware
if (context.user.role !== 'admin') {
throw redirect({ to: '/unauthorized' })
}
return next({ context: { isAdmin: true } })
})
// Usage
const adminAction = createServerFn({ method: 'POST' })
.middleware([adminMiddleware])
.handler(async ({ context }) => {
// context: { user: User; isAdmin: boolean }
return { success: true }
})
// app/routes/api/users.ts
import { createAPIFileRoute } from '@tanstack/solid-start/api'
import { db } from '../../db'
import { users } from '../../db/schema'
export const APIRoute = createAPIFileRoute('/api/users')({
GET: async () => {
const allUsers = await db.select().from(users).all()
return Response.json(allUsers)
},
POST: async ({ request }) => {
const body = await request.json()
const [user] = await db.insert(users).values(body).returning()
return new Response(JSON.stringify(user), { status: 201 })
},
})
import { defer } from '@tanstack/solid-start'
import { createAsync } from '@tanstack/solid-router'
export const Route = createFileRoute('/dashboard')({
loader: async () => ({
criticalData: await fetchCriticalData(),
deferredData: defer(fetchSlowData()),
}),
component: Dashboard,
})
function Dashboard() {
const loaderData = Route.useLoaderData()
const criticalData = () => loaderData().criticalData
return (
<div>
<CriticalSection data={criticalData()} />
<Suspense fallback={<Loading />}>
<Async resolve={loaderData().deferredData}>
{(data) => <SlowSection data={data} />}
</Async>
</Suspense>
</div>
)
}
npm install better-auth drizzle-adapter
// lib/auth.ts
import { betterAuth } from 'better-auth'
import { drizzleAdapter } from 'better-auth/adapters/drizzle'
import { db } from '../db'
import * as schema from '../db/schema'
export const auth = betterAuth({
database: drizzleAdapter(db, {
provider: 'sqlite', // or 'postgres', 'mysql'
schema: {
user: schema.users,
session: schema.sessions,
account: schema.accounts,
verification: schema.verifications,
},
}),
})
// Server function for current user
const getSession = createServerFn()
.handler(async () => {
const session = await auth.api.getSession({
headers: getWebRequest().headers,
})
return session
})
// app.config.ts
export default defineConfig({
server: {
preset: 'node-server', // Self-hosted Node.js
// preset: 'vercel', // Vercel
// preset: 'netlify', // Netlify
// preset: 'cloudflare-pages', // Cloudflare Pages
// preset: 'aws-lambda', // AWS Lambda
// preset: 'deno-server', // Deno Deploy
// preset: 'bun', // Bun
},
})
createServerFn GET for data fetching (cacheable, preloadable)createServerFn POST for mutations and side effectsbeforeLoad for route-level auth guardsdefer() for non-critical data to improve TTFBdefaultPreload: 'intent' on the router for instant navigationawait in loaders leads to streaming issuesdeclare module '@tanstack/solid-router' loses all type safety(End of file - total 268 lines)
tools
<!--VITE PLUS START--> # Using Vite+, the Unified Toolchain for the Web This project is using Vite+, a unified toolchain built on top of Vite, Rolldown, Vitest, tsdown, Oxlint, Oxfmt, and Vite Task. Vite+ wraps runtime management, package management, and frontend tooling in a single global CLI called `vp`. Vite+ is distinct from Vite, but it invokes Vite through `vp dev` and `vp build`. ## Vite+ Workflow `vp` is a global binary that handles the full development lifecycle. Run `vp help` to pr
development
Guide for building performant data tables. Uses tanstack-table for table logic (sorting, filtering, pagination) and tanstack-virtual for rendering large datasets efficiently.
development
Expert guidance for building observable, expressive, and fault-tolerant TypeScript applications using the effect-ts/effect ecosystem. Covers Effect<A, E, R> type, error management, dependency injection via Layers, observability (logging, metrics, tracing), concurrency with Fibers, retry/scheduling, Schema validation, Streams, and Sinks.
tools
Complete E2E (end-to-end) and integration testing skill for TypeScript/NestJS projects using Jest, real infrastructure via Docker, and GWT pattern. ALWAYS use this skill when user needs to: **SETUP** - Initialize or configure E2E testing infrastructure: - Set up E2E testing for a new project - Configure docker-compose for testing (Kafka, PostgreSQL, MongoDB, Redis) - Create jest-e2e.config.ts or E2E Jest configuration - Set up test helpers for database, Kafka, or Redis - Configure .env.e2e environment variables - Create test/e2e directory structure **WRITE** - Create or add E2E/integration tests: - Write, create, add, or generate e2e tests or integration tests - Test API endpoints, workflows, or complete features end-to-end - Test with real databases, message brokers, or external services - Test Kafka consumers/producers, event-driven workflows - Working on any file ending in .e2e-spec.ts or in test/e2e/ directory - Use GWT (Given-When-Then) pattern for tests **REVIEW** - Audit or evaluate E2E tests: - Review existing E2E tests for quality - Check test isolation and cleanup patterns - Audit GWT pattern compliance - Evaluate assertion quality and specificity - Check for anti-patterns (multiple WHEN actions, conditional assertions) **RUN** - Execute or analyze E2E test results: - Run E2E tests - Start/stop Docker infrastructure for testing - Analyze E2E test results - Verify Docker services are healthy - Interpret test output and failures **DEBUG** - Fix failing or flaky E2E tests: - Fix failing E2E tests - Debug flaky tests or test isolation issues - Troubleshoot connection errors (database, Kafka, Redis) - Fix timeout issues or async operation failures - Diagnose race conditions or state leakage - Debug Kafka message consumption issues **OPTIMIZE** - Improve E2E test performance: - Speed up slow E2E tests - Optimize Docker infrastructure startup - Replace fixed waits with smart polling - Reduce beforeEach cleanup time - Improve test parallelization where safe Keywords: e2e, end-to-end, integration test, e2e-spec.ts, test/e2e, Jest, supertest, NestJS, Kafka, Redpanda, PostgreSQL, MongoDB, Redis, docker-compose, GWT pattern, Given-When-Then, real infrastructure, test isolation, flaky test, MSW, nock, waitForMessages, fix e2e, debug e2e, run e2e, review e2e, optimize e2e, setup e2e