Skip to content
Skill · Windsurf.windsurfrulesCoding

Windsurf Rules for Full-Stack TypeScript Apps

Keeps a TypeScript monorepo honest: one source of truth for types, API contracts that cannot drift, and forms validated by the same schema the server uses.

Use when: Use in a full-stack TypeScript repo (Next.js, TanStack Start, or Express + React) where the client and server share a package and you want Windsurf to stop duplicating types and hand-rolling fetch calls.

@libraryNewcomer
0 upvotes0 forks0 comments

The skill file

Windsurf · .windsurfrules
.windsurfrules2731 chars
# Full-Stack TypeScript Rules ## Type sharing - All types shared between client and server live in `packages/shared/src/types/`. Never redeclare a shape that already exists there — import it. - Before defining a new interface, search the shared package for an existing one. If a near-match exists, extend it with `Pick`/`Omit` instead of copying fields. - Database row types come from the ORM (`InferSelectModel` / Prisma generated types). Never hand-write a type that mirrors a table. - `any` is banned. If a type is genuinely unknown, use `unknown` and narrow it with a type guard in the same file. ## API contract sync - Every API endpoint has exactly one Zod schema for its input and one for its output, defined next to the handler and exported. - The client never calls `fetch` directly. All requests go through the typed client (`api.*`). If an endpoint is missing from the client, add it there first. - When you change an endpoint's input or output, update in the same edit: the Zod schema, the handler, every client call site, and the endpoint's test. Do not leave any of these for "later". - Breaking changes to a deployed endpoint require a new versioned route (`/v2/...`); never repurpose an existing field's meaning. ## Form validation - Every form uses `react-hook-form` with `zodResolver`, and the resolver schema must be the same exported schema the server validates with. Never write a separate client-only copy. - Client-side refinements that the server cannot check (e.g. "passwords match") go in a `.extend()`/`.refine()` wrapper around the shared schema, not a fork of it. - Display server validation errors via `setError` mapped from the error response — never a generic "Something went wrong" toast when field-level info is available. - Disable the submit button while `isSubmitting` is true; every mutation needs a pending state. ## Project hygiene - Run `npm run typecheck` after any change that touches more than one file. Type errors are never acceptable in a suggested diff. - Never edit generated files: `*.gen.ts`, `prisma/client`, anything under `dist/`. Fix the generator input instead. - Server-only modules (`db`, `env`, anything importing `node:` builtins) must never be imported from a file that renders on the client. If unsure, check the import chain before adding the import. - Environment variables are read only through the validated `env` object, never `process.env` directly. ## Testing - New shared schemas get a test asserting one valid and two invalid payloads. - API handler changes require updating the handler's integration test in the same commit. - Do not mock the typed API client in component tests — mock at the HTTP boundary (MSW) so contract drift surfaces in tests.

Install

drop into your repo

Save this to your project or home directory so Windsurf can load it.

path./.windsurfrules
Discussion

What people are saying

Forks

0 forks of this skill
Loading forks…